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

register TraitEventHandlers with tags #230

Closed
wants to merge 9 commits into from

Conversation

rmorshea
Copy link
Contributor

@rmorshea rmorshea commented May 23, 2016

Allows the use of metadata as a way to register trait events

class A(HasTraits):
    foo = Int().tag(bar=True)
    @observe(tags={'bar':True})
    def _bar_observer(self, change):
        print 'change from bar tagged trait'

a = A()

a.foo = 1

The same pattern also works for @validate and @default.

General:

Under The Hood:

  1. default, validate, observe (method and decorator), unobserve, and unobserve_all have a new 'tags' keyword that provides metadata for gathering groups of traits for observation or for removing observers (depending on the method).
    • The metadata provided via '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.
  2. New functionality for unobserve_all
    • Previously unobserve_all either deleted all notifiers, or all notifiers of a given name.
    • Now, it's taken a feature previously belonging to HasTraits.unobserve when its 'handler' argument was None. Thus unobserve_all can remove the handlers of a given name and type.
    • Additionally, unobserve_all can now remove all handlers of a specified type across all traits.
    • Calls to unobserve where 'handler' is None are redirected to unobserve_all with a warning.

@rmorshea rmorshea force-pushed the tagged_events branch 4 times, most recently from 700eb03 to 48bff94 Compare May 23, 2016 05:57
@@ -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'):
Copy link
Member

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.

Copy link
Contributor Author

@rmorshea rmorshea May 31, 2016

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.

Copy link
Member

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.

@rmorshea
Copy link
Contributor Author

@minrk waiting on #240

self.trait_names = names
caches_instance_resources = True

def __init__(self, names, tags, type):
Copy link
Member

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.

Copy link
Contributor Author

@rmorshea rmorshea Jun 22, 2016

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.

@rmorshea rmorshea force-pushed the tagged_events branch 2 times, most recently from 7907497 to ee9421e Compare June 22, 2016 21:02
@Carreau
Copy link
Member

Carreau commented Jun 22, 2016

@rmorshea Sorry if I can seem annoying, I understand that I might be looking like I'm bikeshedding.
Though, I have been confronted to this kind of things before. These are things that I learned the hard way, and these are difficult things to get use to.

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.

@rmorshea
Copy link
Contributor Author

@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.

@rmorshea
Copy link
Contributor Author

... adding tests for @validate and @default via tags

@Carreau
Copy link
Member

Carreau commented Jun 22, 2016

Don't worry that's something you'll learn through time !

@@ -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', {}))
Copy link
Member

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

@minrk
Copy link
Member

minrk commented Jul 6, 2016

should HasTraits.trait_events return these static handlers in addition to the methods it already does?

I don't think there is a reason to distinguish between @observe and observe. What prompted you to maintain this distinction? When would it be relevant?

@minrk minrk modified the milestone: 5.0 Jul 29, 2016
@rmorshea rmorshea force-pushed the tagged_events branch 2 times, most recently from 1431e09 to 751b750 Compare February 1, 2017 06:48
@rmorshea
Copy link
Contributor Author

rmorshea commented Feb 1, 2017

@minrk rebased. Not sure how trivial the hit to patch coverage is yet...

@rmorshea
Copy link
Contributor Author

@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.

@ankostis
Copy link
Contributor

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):
Copy link
Member

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?

Copy link
Contributor Author

@rmorshea rmorshea Mar 20, 2017

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.

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",
Copy link
Member

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.

Copy link
Contributor Author

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.

@@ -995,6 +1066,7 @@ def setup_instance(*args, **kwargs):
self._trait_values = {}
self._trait_notifiers = {}
self._trait_validators = {}
self._static_trait_notifiers = []
Copy link
Member

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?

Copy link
Contributor Author

@rmorshea rmorshea Mar 20, 2017

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.

events.extend(self.trait_events().values())

for e in events:
if e.caches_instance_resources:
Copy link
Member

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?

Copy link
Contributor Author

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.

@rmorshea
Copy link
Contributor Author

@minrk, I've drastically simplified things. Still need to review the accuracy of the docstrings, and edit this PR's description to reflect changes.

@rmorshea
Copy link
Contributor Author

After much simplifying, this should be ready to be merged.

cc: @minrk

@rmorshea
Copy link
Contributor Author

I think we can push this back to 5.1 since it's not a breaking change.

@rmorshea rmorshea modified the milestones: 5.1, 5.0 Aug 14, 2017
@Carreau Carreau added the Closed PR Stalled PR, feel free to resurrect. label May 31, 2020
@Carreau
Copy link
Member

Carreau commented May 31, 2020

Closing as state for 3 years. Labeled accordingly, feel free to reopen.

@Carreau Carreau closed this May 31, 2020
@Carreau Carreau modified the milestones: 5.1, no action Aug 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closed PR Stalled PR, feel free to resurrect.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

don't use assert.
4 participants