Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix behavior of PublicOnlyProxy in setattr, wrapped methods, and calling #5997

Merged
merged 6 commits into from
Jun 29, 2023

Conversation

Czaki
Copy link
Collaborator

@Czaki Czaki commented Jun 23, 2023

Fixes/Closes

Closes #5767

Description

This PR makes a few tweaks to the behavior of PublicOnlyProxy:

  • in setattr, if we want to set an attribute of a wrapped object, if the attribute value is itself an object wrapped with PublicOnlyProxy, we unwrap it. This has two benefits: (1) checking the attribute later incurs a significant cost in _is_called_from_napari, which involves frame inspection. This way we don't incur that cost. And (2) Certain equality checks fail when PublicOnlyProxy's interact with Qt. This is Setting active layer from a plugin on PublicOnlyProxy works partially, but not in GUI #5767 and this PR fixes that.
  • in PublicOnlyProxy.create, we now check whether the object we want to wrap is a method, because then the module we need to check is not that of the method (this is always builtins), but rather the module of the object that that method is bound to. This is now fixed.
  • When a PublicOnlyProxy'd object is callable, we unwrap the arguments, call the unwrapped function with the unwrapped arguments, then wrap the result. This avoids the performance issues detailed above.

@github-actions github-actions bot added the tests Something related to our tests label Jun 23, 2023
@Czaki Czaki added the bugfix PR with bugfix label Jun 23, 2023
@Czaki Czaki added this to the 0.4.18 milestone Jun 23, 2023
@codecov
Copy link

codecov bot commented Jun 23, 2023

Codecov Report

Merging #5997 (f7f47ef) into main (49e235a) will increase coverage by 0.01%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #5997      +/-   ##
==========================================
+ Coverage   91.56%   91.57%   +0.01%     
==========================================
  Files         573      573              
  Lines       49809    49865      +56     
==========================================
+ Hits        45606    45663      +57     
+ Misses       4203     4202       -1     
Impacted Files Coverage Δ
napari/utils/_proxies.py 93.40% <100.00%> (-0.72%) ⬇️
napari/utils/_tests/test_proxies.py 100.00% <100.00%> (ø)

... and 9 files with indirect coverage changes

@Czaki Czaki mentioned this pull request Jun 23, 2023
@jni
Copy link
Member

jni commented Jun 26, 2023

@Czaki I'm sorry, I don't really understand this PR... I trust that you know what you're doing 😅 but as a reviewer I'd like to understand it. Do you think you could explain it in a bit more detail? Something like, "When passing the viewer to plugins, we wrap it in a PublicOnlyProxy, to avoid plugins accessing private attributes and methods. This has worked fine in most cases but fails when X, because Y. To fix it, we remove the proxy before doing Z."

It's also unclear to me whether this fixes the "whole class of issues" exemplified by #5767 as you commented there, or just two (__call__ and __setattr__) — but are there more?

@Czaki
Copy link
Collaborator Author

Czaki commented Jun 26, 2023

It's also unclear to me whether this fixes the "whole class of issues" exemplified by #5767 as you commented there, or just two (__call__ and __setattr__) — but are there more?

The pointed issue shows the problem with one function. I mean that same problem may happen in any place where we pass napari objects to Qt, as it uses something like id(), not is to compare objects. So it does not treat wrapped and unwrapped objects as identical.

@Czaki I'm sorry, I don't really understand this PR... I trust that you know what you're doing 😅 but as a reviewer I'd like to understand it

Ok. I will add more explanation.

@Czaki Czaki changed the title unwrap aurguments of called napari method unwrap arguments of called napari method Jun 27, 2023
@Czaki
Copy link
Collaborator Author

Czaki commented Jun 27, 2023

@jni I have added docstrings. Could you check now?

@jni jni changed the title unwrap arguments of called napari method Fix behavior of PublicOnlyProxy in setattr, wrapped methods, and calling Jun 28, 2023
@jni jni added the ready to merge Last chance for comments! Will be merged in ~24h label Jun 28, 2023
@jni
Copy link
Member

jni commented Jun 28, 2023

@Czaki I've updated the comments, updated the PR description, and made the variables in the tests more descriptive because I was having trouble following the logic. Could you please double-check them and thumbs-up here if all good? Otherwise I think this is ready to merge! 🚀 Thanks for the pair session which made things clear. 😊

@@ -134,7 +152,15 @@ def __dir__(self):
@classmethod
def create(cls, obj: Any) -> Union['PublicOnlyProxy', Any]:
# restrict the scope of this proxy to napari objects
mod = getattr(type(obj), '__module__', None) or ''
if type(obj).__name__ == 'method':
# If the given object is a method, we check the module *of the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow, bizarre!

@jni jni merged commit 2b02b0e into napari:main Jun 29, 2023
36 checks passed
@jni jni deleted the bugfix/unwrap_roxy_on_call branch June 29, 2023 05:31
Czaki added a commit that referenced this pull request Jun 30, 2023
…ing (#5997)

# Fixes/Closes

Closes #5767

# Description

This PR makes a few tweaks to the behavior of PublicOnlyProxy:

- in setattr, if we want to set an attribute of a wrapped object, if the
attribute value is itself an object wrapped with PublicOnlyProxy, we
unwrap it. This has two benefits: (1) checking the attribute later
incurs a significant cost in `_is_called_from_napari`, which involves
frame inspection. This way we don't incur that cost. And (2) Certain
equality checks fail when PublicOnlyProxy's interact with Qt. This is
#5767 and this PR fixes that.
- in `PublicOnlyProxy.create`, we now check whether the object we want
to wrap is a method, because then the module we need to check is not
that of the method (this is always `builtins`), but rather the module of
the object *that that method is bound to.* This is now fixed.
- When a PublicOnlyProxy'd object is callable, we unwrap the arguments,
call the unwrapped function with the unwrapped arguments, then wrap the
result. This avoids the performance issues detailed above.

---------

Co-authored-by: Juan Nunez-Iglesias <jni@fastmail.com>
melissawm pushed a commit to melissawm/napari that referenced this pull request Jul 3, 2023
…ing (napari#5997)

# Fixes/Closes

Closes napari#5767

# Description

This PR makes a few tweaks to the behavior of PublicOnlyProxy:

- in setattr, if we want to set an attribute of a wrapped object, if the
attribute value is itself an object wrapped with PublicOnlyProxy, we
unwrap it. This has two benefits: (1) checking the attribute later
incurs a significant cost in `_is_called_from_napari`, which involves
frame inspection. This way we don't incur that cost. And (2) Certain
equality checks fail when PublicOnlyProxy's interact with Qt. This is
napari#5767 and this PR fixes that.
- in `PublicOnlyProxy.create`, we now check whether the object we want
to wrap is a method, because then the module we need to check is not
that of the method (this is always `builtins`), but rather the module of
the object *that that method is bound to.* This is now fixed.
- When a PublicOnlyProxy'd object is callable, we unwrap the arguments,
call the unwrapped function with the unwrapped arguments, then wrap the
result. This avoids the performance issues detailed above.

---------

Co-authored-by: Juan Nunez-Iglesias <jni@fastmail.com>
@brisvag brisvag removed the ready to merge Last chance for comments! Will be merged in ~24h label Jul 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix PR with bugfix tests Something related to our tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Setting active layer from a plugin on PublicOnlyProxy works partially, but not in GUI
3 participants