Skip to content

Commit

Permalink
rework code and tests for bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
rmorshea committed Apr 1, 2016
1 parent bdeae73 commit 1559adb
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 130 deletions.
35 changes: 25 additions & 10 deletions traitlets/tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,20 +702,35 @@ class A(HasTraits):

a = A()

def _test_observer(change):
a.foo = change['new']
def _test_observer1(change):
a.foo += 1
def _test_observer2(change):
a.foo += 1
def _test_observer3(change):
a.foo += 1

# test that multiple evals will register together
a.observe(_test_observer1, tags={'type': lambda v: v in 'ac'})
a.observe(_test_observer2, tags={'type': lambda v: v in 'ab'})
# test that evals and static tags register together
a.observe(_test_observer3, tags={'type': 'a'})

a.observe(_test_observer, tags={'type': lambda v: v in 'abc'})
a.bar = 1
self.assertEqual(a.foo, a.bar)
self.assertEqual(a.foo, 3)
a.foo = 0

a.add_traits(baz=Int().tag(type='b'))
a.baz = 2
self.assertEqual(a.foo, a.baz)
a.unobserve(_test_observer1, 'bar')
a.unobserve(_test_observer3, 'bar')
a.bar = 2
self.assertEqual(a.foo, 1)
a.foo = 0

a.unobserve(_test_observer, 'bar')
a.bar = 3
self.assertNotEqual(a.foo, a.bar)
# test tagged notifiers know about
# dynamically added traits
a.add_traits(baz=Int().tag(type='b'))
a.baz = 1
self.assertEqual(a.foo, 1)
a.foo = 0


class TestHasTraits(TestCase):
Expand Down
60 changes: 30 additions & 30 deletions traitlets/traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from .utils.getargspec import getargspec
from .utils.importstring import import_item
from .utils.sentinel import Sentinel
from .utils.mapping import isdict
from .utils.dict_types import mapping

SequenceTypes = (list, tuple, set, frozenset)

Expand Down Expand Up @@ -214,7 +214,11 @@ def __eq__(self, other):
if isinstance(other, types.FunctionType):
return self.func == other
else:
return self.func(other)
try:
return self.func(other)
except:
return False
__hash__ = None


def getmembers(object, predicate=None):
Expand Down Expand Up @@ -1142,15 +1146,11 @@ def trait_notifiers(self, name, type):
if k in d['tags']:
for t in (All, type):
if v in d['tags'][k]:
if id(v) not in d['tags'][k].ids():
# accounts for _SimpleEval
isdictkeys = list(d['tags'][k].keys())
v = isdictkeys[isdictkeys.index(v)]
if t in d['tags'][k][v]:
for c in d['tags'][k][v][t]:
if c not in notifiers:
notifiers.append(c)

for m in d['tags'][k][v]:
if t in m:
for c in m[t]:
if c not in notifiers:
notifiers.append(c)
return notifiers


Expand Down Expand Up @@ -1201,18 +1201,21 @@ def _add_notifiers(self, handler, name, tags, type):
tagged = self._trait_notifiers['tags']
for k, v in tags.items():
if k in tagged:
values = tagged[k]
notifier_mapping = tagged[k]
else:
values = isdict()
tagged[k] = values
# mapping handles all types
# and custom equivalence
notifier_mapping = mapping()
tagged[k] = notifier_mapping

if isinstance(v, types.FunctionType):
v = _SimpleEval(v)
# get the internal dict to which `v` should be assigned
values = notifier_mapping.get_internal_dict(v)

if v in values:
d = values[v]
else:
# isdict handles unhashable types
d = {}
values[v] = d

Expand All @@ -1238,21 +1241,18 @@ def _remove_notifiers(self, handler, name, tags, type):
trait = getattr(self.__class__, name, None)
if isinstance(trait, TraitType):
d = self._trait_notifiers
for tags in (tags, trait.metadata):
for k, v in tags.items():
if k in d['tags']:
mapping = d['tags'][k]
if v in mapping and id(v) not in mapping.ids():
# accounts for _SimpleEval
isdictkeys = list(mapping.keys())
v = isdictkeys[isdictkeys.index(v)]
try:
if handler is None:
del mapping[v][type]
else:
mapping[v][type].remove(handler)
except KeyError:
pass
for k, v in trait.metadata.items():
if k in d['tags']:
for t in (All, type):
if v in d['tags'][k]:
for m in d['tags'][k][v]:
try:
if handler is None:
del m[t]
else:
m[t].remove(handler)
except:
pass

def on_trait_change(self, handler=None, name=None, remove=False):
"""DEPRECATED: Setup a handler to be called when a trait changes.
Expand Down

0 comments on commit 1559adb

Please sign in to comment.