Skip to content
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

register TraitEventHandlers with tags #230

Closed
wants to merge 9 commits into from
159 changes: 158 additions & 1 deletion traitlets/tests/test_traitlets.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
TraitError, Union, All, Undefined, Type, This, Instance, TCPAddress,
List, Tuple, ObjectName, DottedObjectName, CRegExp, link, directional_link,
ForwardDeclaredType, ForwardDeclaredInstance, validate, observe, default,
observe_compat, BaseDescriptor, HasDescriptors,
observe_compat, BaseDescriptor, HasDescriptors, parse_notifier_name,
parse_notifier_names, EventHandler
)

import six
Expand All @@ -31,6 +32,7 @@ def change_dict(*ordered_values):
change_names = ('name', 'old', 'new', 'owner', 'type')
return dict(zip(change_names, ordered_values))


#-----------------------------------------------------------------------------
# Helper classes for testing
#-----------------------------------------------------------------------------
Expand Down Expand Up @@ -567,6 +569,10 @@ def notify1(self, change):
def notify2(self, change):
self._notify2.append(change)

def test_raise_on_no_names(self):
with pytest.raises(TypeError):
observe()

def test_notify_all(self):

class A(HasTraits):
Expand Down Expand Up @@ -608,6 +614,142 @@ class A(HasTraits):
self.assertTrue(change in self._notify1)
self.assertRaises(TraitError,setattr,a,'a','bad string')

def test_observe_decorator_via_tags(self):

class A(HasTraits):
foo = Int()
bar = Int().tag(test=True)

@observe(tags={'test':True})
def _test_observer(self, change):
self.foo = change['new']

a = A()
a.bar = 1
self.assertEqual(a.foo, a.bar)

def test_observe_via_tags(self):

class unhashable(object):
__hash__ = None
u = unhashable()

class A(HasTraits):
foo = Int()
bar = Int().tag(type='a', obj=u)
baz = Int().tag(type='z')

a = A()

def _test_observer1(change):
a.foo += 1
def _test_observer2(change):
a.foo += 1

# test that multiple evals will register together

a.observe(_test_observer1, tags={'type': lambda v: v in ('a','c')})
a.observe(_test_observer2, tags={'type': lambda v: v in ('a','b')})

a.bar = 1
self.assertEqual(a.foo, 2)
a.foo = 0

a.unobserve_all()

# test that hashable and unhashable tags register
a.observe(_test_observer1, tags={'type': 'a'})
a.observe(_test_observer2, tags={'obj': u})

a.bar = 2
self.assertEqual(a.foo, 2)
a.foo = 0

a.unobserve_all()

a.add_traits(baz=Int().tag(type='b'))
a.observe(_test_observer1, tags={'type': 'b'})
a.baz = 1
self.assertEqual(a.foo, 1)
a.foo = 0

a.unobserve_all()

def test_unobserve_via_tags(self):

class A(HasTraits):
foo = Int()
bar = Int().tag(type='a')

a = A()

def _test_observer(change):
a.foo += 1

a.observe(_test_observer, tags={'type': 'a'})
a.unobserve(_test_observer, names='bar')

a.bar = 1
self.assertEqual(a.foo, 0)

a.observe(_test_observer, tags={'type': 'a'})
a.unobserve(_test_observer, tags={'type': 'a'})

a.bar = 2
self.assertEqual(a.foo, 0)

def test_validate_via_tags(self):

def _domain_like(x):
return isinstance(x, (tuple, list)) and len(x) == 2

class A(HasTraits):
foo = Int().tag(domain=(1, 11))
bar = Int().tag(domain=(4, 8))
baz = Int().tag(domain=None)

@validate(tags={'domain': lambda x: _domain_like(x)})
def _domain_type_coecer(self, prop):
d = prop.trait.metadata['domain']
if prop.value <= d[0]:
return d[0]
elif prop.value >= d[1]:
return d[1]-1
else:
return prop.value

a = A()

a.foo = 0
self.assertEqual(a.foo, 1)
a.foo = 11
self.assertEqual(a.foo, 10)
a.foo = 6
self.assertEqual(a.foo, 6)

a.bar = 0
self.assertEqual(a.bar, 4)
a.bar = 11
self.assertEqual(a.bar, 7)
a.bar = 6
self.assertEqual(a.bar, 6)

def test_default_via_tags(self):

class A(HasTraits):
foo = Int().tag(type='a')
bar = Int().tag(type='a')
baz = Int().tag(type='b')

@default(tags={'type': 'a'})
def _a_type_default(self):
return 1

a = A()
self.assertEqual(a.foo, 1)
self.assertEqual(a.bar, 1)
self.assertEqual(a.baz, 0)

def test_subclass(self):

class A(HasTraits):
Expand Down Expand Up @@ -2494,3 +2636,18 @@ def __init__(__self, cls, self):
pass

x = X(cls=None, self=None)


#-----------------------------------------------------------------------------
# Misc testing for improved coverage
#-----------------------------------------------------------------------------


def test_notifier_parsing():
with pytest.raises(TypeError):
parse_notifier_name([0])

with pytest.raises(TypeError):
parse_notifier_names(0, {"type": None})


Loading