Skip to content

Commit

Permalink
Deprecate on_trait_change and magic change handlers, and add HasTrait…
Browse files Browse the repository at this point in the history
…s.observe
  • Loading branch information
SylvainCorlay committed Aug 4, 2015
1 parent c661f4b commit 2a2c45e
Showing 1 changed file with 68 additions and 24 deletions.
92 changes: 68 additions & 24 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,46 +688,37 @@ def _notify_trait(self, name, old_value, new_value):

# First dynamic ones
callables = []
callables.extend(self._trait_notifiers.get(name,[]))
callables.extend(self._trait_notifiers.get('anytrait',[]))
callables.extend(self._trait_notifiers.get(name, []))
callables.extend(self._trait_notifiers.get('anytrait', []))

# Now static ones
try:
cb = getattr(self, '_%s_changed' % name)
except:
pass
else:
callables.append(cb)
warn("_[traitname]_changed change handlers are deprecated: use observe instead",
DeprecationWarning, stacklevel=2)
callables.append(self._wrap_old_handler(cb))

# Call them all now
for c in callables:
# Traits catches and logs errors here. I allow them to raise
if callable(c):
argspec = getargspec(c)

nargs = len(argspec[0])
# Bound methods have an additional 'self' argument
# I don't know how to treat unbound methods, but they
# can't really be used for callbacks.
if isinstance(c, types.MethodType):
offset = -1
else:
offset = 0
nargs = len(getargspec(c)[0])

# Bound methods have an additional 'self' argument.
offset = -1 if isinstance(c, types.MethodType) else 0

if nargs + offset == 0:
c()
elif nargs + offset == 1:
c(name)
elif nargs + offset == 2:
c(name, new_value)
elif nargs + offset == 3:
c(name, old_value, new_value)
elif nargs + offset == 4:
c(name, old_value, new_value, self)
c({'name': name, 'old': old_value, 'new': new_value, 'object': self})
else:
raise TraitError('a trait changed callback '
'must have 0-4 arguments.')
raise TraitError('an observe change callback '
'must have 0-1 arguments.')
else:
raise TraitError('a trait changed callback '
raise TraitError('an observe change callback '
'must be callable.')

def _add_notifiers(self, handler, name):
Expand All @@ -746,8 +737,33 @@ def _remove_notifiers(self, handler, name):
except ValueError:
pass

def _wrap_old_handler(self, c):
if callable(c):
nargs = len(getargspec(c)[0])

# Bound methods have an additional 'self' argument.
offset = -1 if isinstance(c, types.MethodType) else 0

def handler(change):
if nargs + offset == 0:
c()
elif nargs + offset == 1:
c(change['name'])
elif nargs + offset == 2:
c(change['name'], change['new'])
elif nargs + offset == 3:
c(change['name'], change['old'], change['new'])
elif nargs + offset == 4:
c(change['name'], change['old'], change['new'], change['object'])
else:
raise TraitError('a trait changed callback '
'must have 0-4 arguments.')
return handler
else:
raise TraitError('a trait changed callback must be callable.')

def on_trait_change(self, handler, name=None, remove=False):
"""Setup a handler to be called when a trait changes.
"""DEPRECATED: Setup a handler to be called when a trait changes.
This is used to setup dynamic notifications of trait changes.
Expand All @@ -771,6 +787,33 @@ def on_trait_change(self, handler, name=None, remove=False):
If False (the default), then install the handler. If True
then unintall it.
"""
warn("on_trait_change is deprecated: use observe instead", DeprecationWarning,
stacklevel=2)
return self.observe( self._wrap_old_handler(handler), name=name, remove=remove)

def observe(self, handler, name=None, remove=False):
"""Setup a handler to be called when a trait changes.
This is used to setup dynamic notifications of trait changes.
Parameters
----------
handler : callable
A callable that is called when a trait changes. Its
signature can be handler() or handler(change), where change is a
dictionary with the following optional keys:
- object : the HasTraits instance
- old : the old value of the modified trait attribute
- new : the new value of the modified trait attribute
- name : the name ofthe modified trait attribute.
name : list, str, None
If None, the handler will apply to all traits. If a list
of str, handler will apply to all names in the list. If a
str, the handler will apply just to that name.
remove : bool
If False (the default), then install the handler. If True
then unintall it.
"""
if remove:
names = parse_notifier_name(name)
for n in names:
Expand All @@ -779,6 +822,7 @@ def on_trait_change(self, handler, name=None, remove=False):
names = parse_notifier_name(name)
for n in names:
self._add_notifiers(handler, n)
return handler

@classmethod
def class_trait_names(cls, **metadata):
Expand Down

0 comments on commit 2a2c45e

Please sign in to comment.