-
-
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
Cross Validation Using New Decorator API #73
Conversation
b580caa
to
4a2fcc7
Compare
4a2fcc7
to
25753a9
Compare
Like mentioned elsewhere, we will have problems if we have two different validators because validators also can coerce the argument to make it valid. Having the validator be a magic name enforces only one validator/coercer. Another option is to have the decorator fail the second time it is used to register a validator (I'm not sure if I like that any better, though...). |
A small detail: having an |
@SylvainCorlay let me know if that last commit addresses your concern. @jasongrout I think I'm going to open up a new branch based on this one, that will try to address the organization that these decorators should take on (and hopefully in the process, solve the problem of chaining validators). |
|
||
@staticmethod | ||
def register_event(inst, method, name): | ||
getattr(inst, 'observe')(method, name) |
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.
inst.observe(method, name)
?
@SylvainCorlay or @jasongrout, are you guys available to chat briefly? |
for name in self.names: | ||
inst.observe(types.MethodType(self.func, inst), name=name) | ||
self.register_event(ints, meth, name) |
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.
typo ints
6f07c4b
to
7ebe0a0
Compare
7ebe0a0
to
7c94d91
Compare
@@ -649,14 +697,23 @@ def observe(*names): | |||
""" | |||
return ObserveHandler(names) | |||
|
|||
def validate(name): | |||
""" A decorator which can be used to cross validate a member on a class. |
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.
Let's not use the word "cross validate". How about "A decorator for validating the state of an HasTraits object when a Trait is set." or something like that.
"""Setup a handler to be called when a trait should be cross valdiated. | ||
|
||
This is used to setup dynamic notifications for cross validation. | ||
Cross validation handlers chain, meaning that the output of a prior |
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.
We don't have chaining anymore, right?
Do we have tests to go along with this? |
@jasongrout I was just using the same language as in 'observe', so I'll make that edit on both. And no tests yet, but I'll write those after lunch. |
if isinstance(cb, types.MethodType): | ||
nargs -= 1 | ||
if nargs == 0: | ||
value = cb() |
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 don't think it makes sense to have a validator that takes no arguments. How will it know what value to return, if it has no idea what was set? I think validators should always take one argument, this change dictionary you created above.
@rmorshea - thanks. The observe should probably also use Trait instead of member. @SylvainCorlay, what do you think? |
To the first, good point, on the other, I figured it would be best to prevent people from trying to pass in multiple attribute names, but since observe is *names they'd probably get an error for too many arguments instead. I'll take that out. |
This looks good to me. |
I'm making these api updates to my matplotlib branch that uses traitlets and the callback signature of Because there isn't an easy way to enumerate the change dict (since it isn't ordered) I'm tempted to gravitate towards a naming scheme like It might be nicer to make the signature |
I recall that we discussed this with Chris and Jason and the choice for not expanding the dictionary came from the fact that it is future-proof, in that we will be able to add extra arguments to |
So then just go with |
@SylvainCorlay sadly (or maybe not...) that doesn't work in functions:
It does work, however, in class bodies:
|
Sorry I deleted my previous message by mistake that was saying |
So then we're back to |
I guess so. In any case, this PR is about the validation decorator. To change back to **change, we will need to get everyone around the table again on that decision... |
method = types.MethodType(self.func, inst) | ||
return method(*args, **kwargs) | ||
|
||
def __call__(self, *args, **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.
old __call__
method forced the use of super
to access overwritten methods. 9ea2afa enables this syntax:
from traitlets import HasTraits, Int, observe
class A(HasTraits):
i, j = Int(), Int()
@observe('i')
def _i_changed(self, change):
self.j = change['new']
class B(A):
k = Int()
@observe('i')
def _i_changed(self, change):
A._i_changed(self, change)
self.k = change['new']
I think it is a good idea for this to work with and without super, but let's add tests for that. |
@rmorshea I don't think that you need to create a class EventHandler(BaseDescriptor):
def _init_call(self, func):
self.func = func
return self
def __call__(self, *args, **kwargs):
if hasattr(self, 'func'):
return self.func(*args, **kwargs)
else:
return self._init_call(*args, **kwargs)
def __get__(self, inst, cls=None):
if inst is None:
return self
return types.MethodType(self.func, inst) Besides, this would simplify the support of bare functions in the future. |
dd74c93
to
9ea2afa
Compare
@minrk @jasongrout @ellisonbg do you guys want to have another look at this? |
@SylvainCorlay thanks, I'll try to have a look tomorrow. |
7e1c41b
to
8ce0f31
Compare
|
@rmorshea let's hold off until this is merged and iterate in other PRs. |
8ce0f31
to
1a527b4
Compare
@SylvainCorlay I'll make a separate pr. |
@SylvainCorlay @rmorshea 👍 from me. I'm happy to merge if this gets OK from @ellisonbg. |
Looks good, merging. |
Cross Validation Using New Decorator API
brief example
'result : 2'