Skip to content

Commit

Permalink
fix trait_notifiers method and make trigger_notifiers public
Browse files Browse the repository at this point in the history
  • Loading branch information
rmorshea committed Oct 24, 2015
1 parent b7032a1 commit 8156a35
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 45 deletions.
2 changes: 1 addition & 1 deletion traitlets/tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def change_dict(*ordered_values):

class HasTraitsStub(HasTraits):

def _trigger_notifiers(self, change):
def trigger_notifiers(self, change):
self._notify_name = change['name']
self._notify_old = change['old']
self._notify_new = change['new']
Expand Down
69 changes: 25 additions & 44 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ def hold_trait_notifications(self):
return
else:
cache = {}
_trigger_notifiers = self._trigger_notifiers
trigger_notifiers = self.trigger_notifiers

def hold(change):
name = change['name']
Expand All @@ -879,7 +879,7 @@ def hold(change):
try:
# Replace _notify_change with `hold`, caching and compressing
# notifications, disable cross validation and yield.
self._trigger_notifiers = hold
self.trigger_notifiers = hold
self._cross_validation_lock = True
yield
# Cross validate final values when context is released.
Expand All @@ -889,7 +889,7 @@ def hold(change):
setattr(self, name, value)
except TraitError as e:
# Roll back in case of TraitError during final cross validation.
self._trigger_notifiers = lambda x: None
self.trigger_notifiers = lambda x: None
for name in cache:
for type in cache[name]:
changes = cache[name][type]
Expand All @@ -905,14 +905,14 @@ def hold(change):
finally:
# Reset the _notify_change to original value, enable cross-validation
# and fire resulting change notifications.
self._trigger_notifiers = _trigger_notifiers
self.trigger_notifiers = trigger_notifiers
self._cross_validation_lock = False

if isinstance(_trigger_notifiers, types.MethodType):
if isinstance(trigger_notifiers, types.MethodType):
# Presence of the method _notify_trait
# on __dict__ can cause memory leaks
# and prevents pickleability
self.__dict__.pop('_trigger_notifiers')
self.__dict__.pop('trigger_notifiers')

# trigger delayed notifications
for name in cache:
Expand All @@ -924,35 +924,28 @@ def hold(change):
change = changes[0]
if len(changes)>1:
change['new'] = changes[-1]['new']
self._trigger_notifiers(change)
self.trigger_notifiers(change)
else:
for change in changes:
self._trigger_notifiers(change)
self.trigger_notifiers(change)

def trait_notifiers(self, *names, **tags):
if All in names:
def trait_notifiers(self, name=None, *types):
all_types = len(types)==0 or All in types

if name in (All, None) and all_types:
notifiers = list(self._trait_notifiers)
else:
notifiers = list()
FoundNotifier = Exception()
for n in self._trait_notifiers:
try:
for name in names:
if name in n.trait_names:
if name in self.trait_names(**n.tags):
notifiers.append(n)
raise FoundNotifier
for key in tags:
if key in n.tags:
if n.tags[key] == tags[key]:
notifiers.append(n)
raise FoundNotifier
except Exception as e:
if e is not FoundNotifier:
raise e
if n.type in types or all_types:
if name is not None:
if name in n.trait_names or name is All:
notifiers.append(n)
elif name in self.trait_names(**n.tags):
notifiers.append(n)

# DEPRECATED: magic notifiers
for name in names:
if all_types or 'change' in types:
magic_name = '_%s_changed' % name
if hasattr(self, magic_name):
class_value = getattr(self.__class__, magic_name)
Expand All @@ -965,31 +958,22 @@ def trait_notifiers(self, *names, **tags):
wrap = _CallbackWrapper(name)
notifiers.append(wrap(cb))

d = dict()
for n in notifiers:
if n.type not in d:
nlist = []
d[n.type] = nlist
else:
nlist = d[n.type]
nlist.append(n)

return d
return notifiers

def _notify_trait(self, name, old, new, subchanges=None):
if subchanges is not None:
for change in subchanges:
self._trigger_notifiers(change)
self.trigger_notifiers(change)

# a standard "change" is implicit when notifying
standard = {'name': name, 'old': old, 'new': new,
'owner': self, 'type': 'change'}
self._trigger_notifiers(standard)
self.trigger_notifiers(standard)


def _trigger_notifiers(self, change):
def trigger_notifiers(self, change):
name, type = change['name'], change['type']
notifiers = self.trait_notifiers(name).get(type)
notifiers = self.trait_notifiers(name, type)

if notifiers is not None:
# Traits catches and logs errors here. I allow them to raise
Expand Down Expand Up @@ -1251,11 +1235,8 @@ def add_traits(self, **traits):
self.__class__ = type(self.__class__.__name__, (self.__class__,),
traits)

for name, trait in traits.items():
for trait in traits.values():
trait.instance_init(self)
for o in self._trait_notifiers:
if trait.name in self.traits(**o.tags):
o.trait_names |= set([trait.name])

def set_trait(self, name, value):
"""Forcibly sets trait attribute, including read-only attributes."""
Expand Down

0 comments on commit 8156a35

Please sign in to comment.