traitlets icon indicating copy to clipboard operation
traitlets copied to clipboard

Support observe as a decorator outside of classes

Open aplavin opened this issue 9 years ago • 7 comments

Please note that I have only used traitlets as a part of ipywidgets, and not sure how feasible are the suggestions below for its other uses - but for widgets they would be quite useful.

So, when one needs to observe a widget trait, it can't be done without separately defining the handler function and then subscribing:

def handler(*_):
    # ... code ...

w.observe(handler, 'value')

Wouldn't it be better if you could just write like this:

@w.observe('value')
def _(*_):
    # ... code ...

This way there is also no need to make a name for the handle function.

However, it's often useful to call the handler once after setting everything up, e.g. if you make some plots in the handler it makes sense to display them without forcing user to move a slider or something. So, I also suggest adding an argument to observe, like run=True (looking for a better name :) ) or a separate method observe_and_run which would also execute the handler (in addition to subscribing). This way we still wouldn't have to think of a name for the handler function.

So, instead of:

def handler(*_):
    # ... code ...

w.observe(handler, 'value')
handler()

we would be able to write:

@w.observe('value', run=True)
def _(*_):
    # ... code ...

aplavin avatar Aug 16 '16 10:08 aplavin

So we have considered something like this

@observe('foo.bar')
def _(change):
     ....

We have to take into account that @observe takes multiple arguments to register the same callback to multiple attributes. The same holds for @validate.

SylvainCorlay avatar Aug 16 '16 11:08 SylvainCorlay

@SylvainCorlay and why observe('foo.bar') instead of foo.observe('bar') which would remain consistent with other parts?

aplavin avatar Aug 16 '16 11:08 aplavin

  1. Because foo.observe and the free observe functions are two different things.
    • The former is a method foo.observe(func, names=['bar', 'baz'])
    • The latter is to be used as a decorator for methods @observe('bar', 'baz')
  2. Because you may want to observe things from multiple HasTrait instances @observe('foo.bar', 'fooo.baz')

SylvainCorlay avatar Aug 16 '16 11:08 SylvainCorlay

@SylvainCorlay as for your point 2. - why implement the feature to observe several traits when Python natively allows for this? I mean why not just write (assuming the decorator returns the function passed):

@foo.observe('bar')
@fooo.observe('baz')
def _(*_):
    # ... code ...

It also has the benefit of less ambiguity: for example @observe('bar', 'change') looks similar to @observe('bar', type='change') - in the first call 'change' is a name of attribute, and in the second it's the type of event.

aplavin avatar Aug 16 '16 18:08 aplavin

@aplavin I don't see @foo.observe being feasible since it would require too many unpleasant heuristics to handle all the ways foo.observe can be called. However, I think this could be done pretty easily:

@observe(*args, owners=(iterable or instance))
def _(change):
    # your callback code

rmorshea avatar Aug 16 '16 19:08 rmorshea

By the way, why a HasTrait instance e.g. w supports the w.observe() method to install observers dynamically, but has no respective "public" method for installing dynamic validators?

I said public because I noticed that w._register_validator(...) works quit the same with w.observe().

Am I abusing stuff?

ankostis avatar May 12 '17 16:05 ankostis

I see two reasons for keeping it private:

  1. There hasn't been a compelling use case that couldn't be accomplished with the decorator yet. How are you using it?
  2. We have talked about making coercion different from validation (they are one and the same right now). In which case we might make validators public, but also block them from coercing values. On Fri, May 12, 2017 at 9:51 AM Kostis Anagnostopoulos < [email protected]> wrote:

By the way, why a HasTrait instance e.g. w supports the w.observe() method to install observers dynamically, but has no respective "public" method for installing dynamic validators?

I said public because I noticed that w._register_validator(...) works quit the same with w.observe().

Am I abusing stuff?

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub https://github.com/ipython/traitlets/issues/273#issuecomment-301128809, or mute the thread https://github.com/notifications/unsubscribe-auth/AD2tBqUbZVNYNBYbxXNzYg-L9pZuXhCaks5r5I38gaJpZM4JlQ_z .

rmorshea avatar May 14 '17 01:05 rmorshea