-
-
Notifications
You must be signed in to change notification settings - Fork 200
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
register TraitEventHandlers with tags #230
Conversation
700eb03
to
48bff94
Compare
traitlets/traitlets.py
Outdated
@@ -1199,7 +1327,7 @@ def on_trait_change(self, handler=None, name=None, remove=False): | |||
else: | |||
self.observe(_callback_wrapper(handler), names=name) | |||
|
|||
def observe(self, handler, names=All, type='change'): | |||
def observe(self, handler, names=None, tags=None, type='change'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The insertion of an argument makes this a backward-incompatible change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@minrk I've inserted a warning, so it could be phased in. Should it be moved after type
though? The order wouldn't really make sense, but it would make it compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'd get in the habit of putting new args at the end. Most of these should be treated (in documentation, etc.) as keyword-only, even if they can be positional.
traitlets/traitlets.py
Outdated
self.trait_names = names | ||
caches_instance_resources = True | ||
|
||
def __init__(self, names, tags, type): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change of API also, types moves from 3rd to 4th.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been changed now.
7907497
to
ee9421e
Compare
@rmorshea Sorry if I can seem annoying, I understand that I might be looking like I'm bikeshedding. Also @rmorshea You are doing a fantastic job, and understand that if we reach this kind of level of review in a pull request is because you are doing really great, so we are pushing you into even harder things ! I hope you are not taking anything personally, and if you find there is too much pressure, or that we are too hard, feel free to ask for help. I know it can be hard to have a PR languishing for a long time. |
@Carreau I totally understand. The fact that I'm not working heavily with traitlets myself means I might not have the greatest foresight for the problems that will crop up in the future. So I value whatever input you or anyone else has. |
... adding tests for |
Don't worry that's something you'll learn through time ! |
fc33fd8
to
b4c9131
Compare
traitlets/traitlets.py
Outdated
@@ -820,10 +846,10 @@ def validate(*names): | |||
exiting the ``hold_trait_notifications` context, and such changes may not | |||
commute. | |||
""" | |||
return ValidateHandler(names) | |||
return ValidateHandler(names, tags=kwargs.get('tags', {})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably raise on unrecognized kwargs
I don't think there is a reason to distinguish between |
1431e09
to
751b750
Compare
@minrk rebased. Not sure how trivial the hit to patch coverage is yet... |
@minrk, any chance we can fast-track a review of this PR? I'm hoping to integrate this functionality in my work for matplotlib soon. |
In general it would be nice to start having some 5.x.x pre-releases, of possible. |
@@ -820,7 +840,7 @@ def compatible_observer(self, change_or_name, old=Undefined, new=Undefined): | |||
return compatible_observer | |||
|
|||
|
|||
def validate(*names): | |||
def validate(*names, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why **kwargs
instead of explict tags=None
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature is the same as observe
because both validators and default generators can be registered to multiple traits via *names
or tags=<metadata>
. So to mirror the usage of observe
all keywords need to be captured and handled in **kwargs
.
traitlets/traitlets.py
Outdated
for n in names: | ||
self._remove_notifiers(handler, n, type) | ||
if isinstance(tags, six.string_types): | ||
warn("new argument 'tags' introduced: use 'type' as keyword", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since tags
didn't get inserted before type
, I'm not sure why this warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this should be removed.
traitlets/traitlets.py
Outdated
@@ -995,6 +1066,7 @@ def setup_instance(*args, **kwargs): | |||
self._trait_values = {} | |||
self._trait_notifiers = {} | |||
self._trait_validators = {} | |||
self._static_trait_notifiers = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the new _static_trait_notifiers
for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm working on a rewrite to remove this. It has to do with complications associated with re-registering tagged notifiers created with HasTraits.observe
to new traits created via add_traits
.
traitlets/traitlets.py
Outdated
events.extend(self.trait_events().values()) | ||
|
||
for e in events: | ||
if e.caches_instance_resources: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does e.caches_instance_resources
mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to _static_trait_notifiers
I need to rethink this and make the implementation cleaner.
@minrk, I've drastically simplified things. Still need to review the accuracy of the docstrings, and edit this PR's description to reflect changes. |
After much simplifying, this should be ready to be merged. cc: @minrk |
I think we can push this back to 5.1 since it's not a breaking change. |
Closing as state for 3 years. Labeled accordingly, feel free to reopen. |
Allows the use of metadata as a way to register trait events
The same pattern also works for
@validate
and@default
.General:
assert
. #219,observe
should allow any iterable containers. #215, observe should raise with better error if wrong argument passed. #221Under The Hood:
default
,validate
,observe
(method and decorator),unobserve
, andunobserve_all
have a new'tags'
keyword that provides metadata for gathering groups of traits for observation or for removing observers (depending on the method).'tags'
does not create a dynamic reference to traits - e.g. if the metadata of a trait is modified, the tagged registration of observers to that trait will not be recomputed. Thus, in edge cases where metadata might be modified, or new traits are added, the timing of a call to one of the above methods should be closely considered.unobserve_all
unobserve_all
either deleted all notifiers, or all notifiers of a given name.HasTraits.unobserve
when its'handler'
argument wasNone
. Thusunobserve_all
can remove the handlers of a given name and type.unobserve_all
can now remove all handlers of a specified type across all traits.unobserve
where'handler'
isNone
are redirected tounobserve_all
with a warning.