-
Notifications
You must be signed in to change notification settings - Fork 85
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
Add observe decorator and instance method #1086
Conversation
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.
Done with first-pass review. Could you explain a bit more how the observers
dict is working in update_traits_class_dict
? It's not clear to me why it makes sense to put both trait names and method names in there. (I'm not saying it doesn't make sense; just that I'm not yet seeing how it works.)
traits/ctraits.c
Outdated
@@ -735,6 +735,14 @@ has_traits_init(PyObject *obj, PyObject *args, PyObject *kwds) | |||
Py_DECREF(value); | |||
} | |||
|
|||
/* Make sure all of the object's observers have been set up: */ | |||
value = PyObject_CallMethod(obj, "_init_trait_observers", "()"); |
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.
It would be better to use NULL
instead of "()"
for the third argument: that's the documented way to spell "no arguments", and it avoids doing runtime parsing of the format string.
We should change the pre-existing cases of a "()"
format too, but not in this PR. If you prefer to defer all the changes to a separate PR, that's fine, too.
traits/ctraits.c
Outdated
if (value == NULL) { | ||
return -1; | ||
} | ||
|
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.
Spacing nitpick: suggest removing this blank line. (I know this spacing matches what's elsewhere in this function, but to me the Py_DECREF
is part of the same conceptual code block as the call and error check.)
But this is totally a nitpick: feel free to ignore.
traits/has_traits.py
Outdated
@@ -385,6 +394,11 @@ def update_traits_class_dict(class_name, bases, class_dict): | |||
prefix_list = [] | |||
view_elements = {} | |||
|
|||
# Mapping from method name or trait name to list(dict) |
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.
Can you elaborate on how method names are being used here? The description here is surprising at first glance (that the keys of the dictionary could be either a method name or a trait name). How does this work, and why don't we need two separate dictionaries?
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 am sorry for the confusion - the keys here are only method names.
I went ahead with the Property
support in the other branches where the name
(defined in the class dict) could refer to the name of a trait that is a property. Here we are not dealing with property so that half of the comment is misleading. I will remove that.
traits/has_traits.py
Outdated
value dispatch | ||
=========== ======================================================= | ||
``same`` Run notifications on the same thread where the change | ||
occurs. |
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.
For ease of comparison with the "ui" case, it may also be worth documenting here that the listeners/observers are executed immediately in this case.
traits/has_traits.py
Outdated
HasTraits.observe | ||
""" | ||
|
||
def decorator(function): |
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.
decorator
is a generic name here; can we use something more informative? These names could potentially show up in tracebacks or during debugging. (Same for function
.)
traits/has_traits.py
Outdated
Parameters | ||
---------- | ||
function : callable | ||
Unbound method of a subclass of HasTraits |
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.
"Unbound method" isn't quite right here: Python 3 doesn't have unbound methods any more. What's being passed here is simply a function. Not sure what to suggest instead, though: maybe just drop the word "Unbound" and say "Method of a HasTraits subclass"?
traits/has_traits.py
Outdated
""" Create input arguments for HasTraits.observe and attach the input | ||
to the callable. | ||
|
||
Metaclass will then collect this information for calling |
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.
Metaclass will then collect this information for calling | |
The metaclass will then collect this information for calling |
traits/has_traits.py
Outdated
@@ -2066,6 +2260,8 @@ def on_trait_change( | |||
"""Causes the object to invoke a handler whenever a trait attribute | |||
matching a specified pattern is modified, or removes the association. | |||
|
|||
See also ``HasTraits.observe`` for a newer API. |
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.
Could use the Numpy doc "See Also" header here to make this more prominent.
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.
LGTM. One docstring suggestion.
Thank you.
I think I lost it... maybe GH buried it, could you point me to it please? |
I've lost it, too. I frequently add a comment, forget to hit the "submit" button on that comment, and then navigate away; this might have been one of those cases. Sorry. LGTM |
No problem. Thanks. |
This PR implements item 14 of #977
Details:
observe
function in theobservers
subpackage (to be renamed toobserve
too)- This prevents
HasTraits
from knowing any more internal details than necessary.- Maybe for discussion: This also allows advanced users to supply their own
dispatcher
function, e.g.gevent.spawn
without requiring another release of Traits, or requiring registration mechanismHasTraits.observe
method in the traits API. It is similar toHasTraits.on_trait_change
.name
is renamed toexpression
.on_trait_change
don't have to combine a list of string (preferred by me) into a comma-separated string. This will be more compatible with the future counterpart of Propertydepends_on
.Expression
object.name
is optional and can be None to mean anytrait. Nowexpression
is a required positional argument and cannot be None. The use case of anytrait can be supported by thefilter_
expression (to be introduced in a future PR, related to Add an observer for observing traits using an arbitrary filter #1078).(handler, expression)
is retained to be consistent withon_trait_change
. This will lower the barrier for migrating toobserve
.@observe
decorator in the traits API. It is very similar to@on_trait_change
in that it wraps theHasTraits.observe
method and it supportspost_init
flag.traits.observers.api
and expandedtraits.api
Checklist
docs/source/traits_api_reference
)Update User manual (: Will be in a separate PRdocs/source/traits_user_manual
)traits-stubs