diff options
author | dvshkurko <dvshkurko@yandex-team.ru> | 2022-02-10 16:45:52 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:52 +0300 |
commit | c768a99151e47c3a4bb7b92c514d256abd301c4d (patch) | |
tree | 1a2c5ffcf89eb53ecd79dbc9bc0a195c27404d0c /contrib/python/traitlets | |
parent | 321ee9bce31ec6e238be26dbcbe539cffa2c3309 (diff) | |
download | ydb-c768a99151e47c3a4bb7b92c514d256abd301c4d.tar.gz |
Restoring authorship annotation for <dvshkurko@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/traitlets')
-rw-r--r-- | contrib/python/traitlets/py2/tests/ya.make | 24 | ||||
-rw-r--r-- | contrib/python/traitlets/py2/traitlets/tests/_warnings.py | 214 | ||||
-rw-r--r-- | contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py | 4836 | ||||
-rw-r--r-- | contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py | 362 | ||||
-rw-r--r-- | contrib/python/traitlets/py2/traitlets/tests/utils.py | 76 | ||||
-rw-r--r-- | contrib/python/traitlets/py2/ya.make | 6 | ||||
-rw-r--r-- | contrib/python/traitlets/py3/traitlets/tests/_warnings.py | 194 | ||||
-rw-r--r-- | contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py | 4608 | ||||
-rw-r--r-- | contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py | 354 | ||||
-rw-r--r-- | contrib/python/traitlets/py3/traitlets/tests/utils.py | 78 | ||||
-rw-r--r-- | contrib/python/traitlets/py3/ya.make | 6 | ||||
-rw-r--r-- | contrib/python/traitlets/ya.make | 4 |
12 files changed, 5381 insertions, 5381 deletions
diff --git a/contrib/python/traitlets/py2/tests/ya.make b/contrib/python/traitlets/py2/tests/ya.make index 050f5d1cc0..d2d3e3b9bf 100644 --- a/contrib/python/traitlets/py2/tests/ya.make +++ b/contrib/python/traitlets/py2/tests/ya.make @@ -1,15 +1,15 @@ PY2TEST() - -OWNER(g:python-contrib borman nslus) - -PEERDIR( + +OWNER(g:python-contrib borman nslus) + +PEERDIR( contrib/python/traitlets -) - +) + ENV( YA_PYTEST_DISABLE_DOCTEST=yes ) - + SRCDIR(contrib/python/traitlets/py2/traitlets) TEST_SRCS( @@ -18,8 +18,8 @@ TEST_SRCS( tests/test_traitlets.py tests/test_traitlets_enum.py tests/utils.py -) - -NO_LINT() - -END() +) + +NO_LINT() + +END() diff --git a/contrib/python/traitlets/py2/traitlets/tests/_warnings.py b/contrib/python/traitlets/py2/traitlets/tests/_warnings.py index beb3bf6ad5..f135d1f67e 100644 --- a/contrib/python/traitlets/py2/traitlets/tests/_warnings.py +++ b/contrib/python/traitlets/py2/traitlets/tests/_warnings.py @@ -1,107 +1,107 @@ -# From scikit-image: https://github.com/scikit-image/scikit-image/blob/c2f8c4ab123ebe5f7b827bc495625a32bb225c10/skimage/_shared/_warnings.py -# Licensed under modified BSD license - -__all__ = ['all_warnings', 'expected_warnings'] - -from contextlib import contextmanager -import sys -import warnings -import inspect -import re - - -@contextmanager -def all_warnings(): - """ - Context for use in testing to ensure that all warnings are raised. - Examples - -------- - >>> import warnings - >>> def foo(): - ... warnings.warn(RuntimeWarning("bar")) - We raise the warning once, while the warning filter is set to "once". - Hereafter, the warning is invisible, even with custom filters: - >>> with warnings.catch_warnings(): - ... warnings.simplefilter('once') - ... foo() - We can now run ``foo()`` without a warning being raised: - >>> from numpy.testing import assert_warns - >>> foo() - To catch the warning, we call in the help of ``all_warnings``: - >>> with all_warnings(): - ... assert_warns(RuntimeWarning, foo) - """ - - # Whenever a warning is triggered, Python adds a __warningregistry__ - # member to the *calling* module. The exercize here is to find - # and eradicate all those breadcrumbs that were left lying around. - # - # We proceed by first searching all parent calling frames and explicitly - # clearing their warning registries (necessary for the doctests above to - # pass). Then, we search for all submodules of skimage and clear theirs - # as well (necessary for the skimage test suite to pass). - - frame = inspect.currentframe() - if frame: - for f in inspect.getouterframes(frame): - f[0].f_locals['__warningregistry__'] = {} - del frame - - for mod_name, mod in list(sys.modules.items()): - if 'six.moves' in mod_name: - continue - try: - mod.__warningregistry__.clear() - except AttributeError: - pass - - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter("always") - yield w - - -@contextmanager -def expected_warnings(matching): - """Context for use in testing to catch known warnings matching regexes - - Parameters - ---------- - matching : list of strings or compiled regexes - Regexes for the desired warning to catch - Examples - -------- - >>> from skimage import data, img_as_ubyte, img_as_float - >>> with expected_warnings(['precision loss']): - ... d = img_as_ubyte(img_as_float(data.coins())) - Notes - ----- - Uses `all_warnings` to ensure all warnings are raised. - Upon exiting, it checks the recorded warnings for the desired matching - pattern(s). - Raises a ValueError if any match was not found or an unexpected - warning was raised. - Allows for three types of behaviors: "and", "or", and "optional" matches. - This is done to accomodate different build enviroments or loop conditions - that may produce different warnings. The behaviors can be combined. - If you pass multiple patterns, you get an orderless "and", where all of the - warnings must be raised. - If you use the "|" operator in a pattern, you can catch one of several warnings. - Finally, you can use "|\A\Z" in a pattern to signify it as optional. - """ - with all_warnings() as w: - # enter context - yield w - # exited user context, check the recorded warnings - remaining = [m for m in matching if not '\A\Z' in m.split('|')] - for warn in w: - found = False - for match in matching: - if re.search(match, str(warn.message)) is not None: - found = True - if match in remaining: - remaining.remove(match) - if not found: - raise ValueError('Unexpected warning: %s' % str(warn.message)) - if len(remaining) > 0: - msg = 'No warning raised matching:\n%s' % '\n'.join(remaining) - raise ValueError(msg) +# From scikit-image: https://github.com/scikit-image/scikit-image/blob/c2f8c4ab123ebe5f7b827bc495625a32bb225c10/skimage/_shared/_warnings.py +# Licensed under modified BSD license + +__all__ = ['all_warnings', 'expected_warnings'] + +from contextlib import contextmanager +import sys +import warnings +import inspect +import re + + +@contextmanager +def all_warnings(): + """ + Context for use in testing to ensure that all warnings are raised. + Examples + -------- + >>> import warnings + >>> def foo(): + ... warnings.warn(RuntimeWarning("bar")) + We raise the warning once, while the warning filter is set to "once". + Hereafter, the warning is invisible, even with custom filters: + >>> with warnings.catch_warnings(): + ... warnings.simplefilter('once') + ... foo() + We can now run ``foo()`` without a warning being raised: + >>> from numpy.testing import assert_warns + >>> foo() + To catch the warning, we call in the help of ``all_warnings``: + >>> with all_warnings(): + ... assert_warns(RuntimeWarning, foo) + """ + + # Whenever a warning is triggered, Python adds a __warningregistry__ + # member to the *calling* module. The exercize here is to find + # and eradicate all those breadcrumbs that were left lying around. + # + # We proceed by first searching all parent calling frames and explicitly + # clearing their warning registries (necessary for the doctests above to + # pass). Then, we search for all submodules of skimage and clear theirs + # as well (necessary for the skimage test suite to pass). + + frame = inspect.currentframe() + if frame: + for f in inspect.getouterframes(frame): + f[0].f_locals['__warningregistry__'] = {} + del frame + + for mod_name, mod in list(sys.modules.items()): + if 'six.moves' in mod_name: + continue + try: + mod.__warningregistry__.clear() + except AttributeError: + pass + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + yield w + + +@contextmanager +def expected_warnings(matching): + """Context for use in testing to catch known warnings matching regexes + + Parameters + ---------- + matching : list of strings or compiled regexes + Regexes for the desired warning to catch + Examples + -------- + >>> from skimage import data, img_as_ubyte, img_as_float + >>> with expected_warnings(['precision loss']): + ... d = img_as_ubyte(img_as_float(data.coins())) + Notes + ----- + Uses `all_warnings` to ensure all warnings are raised. + Upon exiting, it checks the recorded warnings for the desired matching + pattern(s). + Raises a ValueError if any match was not found or an unexpected + warning was raised. + Allows for three types of behaviors: "and", "or", and "optional" matches. + This is done to accomodate different build enviroments or loop conditions + that may produce different warnings. The behaviors can be combined. + If you pass multiple patterns, you get an orderless "and", where all of the + warnings must be raised. + If you use the "|" operator in a pattern, you can catch one of several warnings. + Finally, you can use "|\A\Z" in a pattern to signify it as optional. + """ + with all_warnings() as w: + # enter context + yield w + # exited user context, check the recorded warnings + remaining = [m for m in matching if not '\A\Z' in m.split('|')] + for warn in w: + found = False + for match in matching: + if re.search(match, str(warn.message)) is not None: + found = True + if match in remaining: + remaining.remove(match) + if not found: + raise ValueError('Unexpected warning: %s' % str(warn.message)) + if len(remaining) > 0: + msg = 'No warning raised matching:\n%s' % '\n'.join(remaining) + raise ValueError(msg) diff --git a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py index 5c0440f9ba..11b334cb60 100644 --- a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py +++ b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets.py @@ -1,2419 +1,2419 @@ -# encoding: utf-8 -"""Tests for traitlets.traitlets.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. -# -# Adapted from enthought.traits, Copyright (c) Enthought, Inc., -# also under the terms of the Modified BSD License. - -import pickle -import re -import sys +# encoding: utf-8 +"""Tests for traitlets.traitlets.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. +# +# Adapted from enthought.traits, Copyright (c) Enthought, Inc., +# also under the terms of the Modified BSD License. + +import pickle +import re +import sys from ._warnings import expected_warnings - -from unittest import TestCase -import pytest -from pytest import mark - -from traitlets import ( - HasTraits, MetaHasTraits, TraitType, Any, Bool, CBytes, Dict, Enum, - Int, CInt, Long, CLong, Integer, Float, CFloat, Complex, Bytes, Unicode, - 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, -) - -import six - -def change_dict(*ordered_values): - change_names = ('name', 'old', 'new', 'owner', 'type') - return dict(zip(change_names, ordered_values)) - -#----------------------------------------------------------------------------- -# Helper classes for testing -#----------------------------------------------------------------------------- - - -class HasTraitsStub(HasTraits): - - def notify_change(self, change): - self._notify_name = change['name'] - self._notify_old = change['old'] - self._notify_new = change['new'] - self._notify_type = change['type'] - - -#----------------------------------------------------------------------------- -# Test classes -#----------------------------------------------------------------------------- - - -class TestTraitType(TestCase): - - def test_get_undefined(self): - class A(HasTraits): - a = TraitType - a = A() - with self.assertRaises(TraitError): - a.a - - def test_set(self): - class A(HasTraitsStub): - a = TraitType - - a = A() - a.a = 10 - self.assertEqual(a.a, 10) - self.assertEqual(a._notify_name, 'a') - self.assertEqual(a._notify_old, Undefined) - self.assertEqual(a._notify_new, 10) - - def test_validate(self): - class MyTT(TraitType): - def validate(self, inst, value): - return -1 - class A(HasTraitsStub): - tt = MyTT - - a = A() - a.tt = 10 - self.assertEqual(a.tt, -1) - - def test_default_validate(self): - class MyIntTT(TraitType): - def validate(self, obj, value): - if isinstance(value, int): - return value - self.error(obj, value) - class A(HasTraits): - tt = MyIntTT(10) - a = A() - self.assertEqual(a.tt, 10) - - # Defaults are validated when the HasTraits is instantiated - class B(HasTraits): - tt = MyIntTT('bad default') - self.assertRaises(TraitError, B) - - def test_info(self): - class A(HasTraits): - tt = TraitType - a = A() - self.assertEqual(A.tt.info(), 'any value') - - def test_error(self): - class A(HasTraits): - tt = TraitType - a = A() - self.assertRaises(TraitError, A.tt.error, a, 10) - - def test_deprecated_dynamic_initializer(self): - class A(HasTraits): - x = Int(10) - def _x_default(self): - return 11 - class B(A): - x = Int(20) - class C(A): - def _x_default(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - - def test_dynamic_initializer(self): - - class A(HasTraits): - x = Int(10) - - @default('x') - def _default_x(self): - return 11 - - class B(A): - x = Int(20) - - class C(A): - - @default('x') - def _default_x(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - - def test_tag_metadata(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10).tag(b=3, c=4) - self.assertEqual(a.metadata, {'a': 1, 'b': 3, 'c': 4}) - - def test_metadata_localized_instance(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - b = MyIntTT(10) - a.metadata['c'] = 3 - # make sure that changing a's metadata didn't change b's metadata - self.assertNotIn('c', b.metadata) - - def test_union_metadata(self): - class Foo(HasTraits): - bar = (Int().tag(ta=1) | Dict().tag(ta=2, ti='b')).tag(ti='a') - foo = Foo() - # At this point, no value has been set for bar, so value-specific - # is not set. - self.assertEqual(foo.trait_metadata('bar', 'ta'), None) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - foo.bar = {} - self.assertEqual(foo.trait_metadata('bar', 'ta'), 2) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'b') - foo.bar = 1 - self.assertEqual(foo.trait_metadata('bar', 'ta'), 1) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - - def test_union_default_value(self): - class Foo(HasTraits): - bar = Union([Dict(), Int()], default_value=1) - foo = Foo() - self.assertEqual(foo.bar, 1) - - def test_deprecated_metadata_access(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - with expected_warnings(["use the instance .metadata dictionary directly"]*2): - a.set_metadata('key', 'value') - v = a.get_metadata('key') - self.assertEqual(v, 'value') - with expected_warnings(["use the instance .help string directly"]*2): - a.set_metadata('help', 'some help') - v = a.get_metadata('help') - self.assertEqual(v, 'some help') - - def test_trait_types_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Int - - def test_trait_types_list_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = List(Int) - - def test_trait_types_tuple_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Tuple(Int) - - def test_trait_types_dict_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Dict(Int) - -class TestHasDescriptorsMeta(TestCase): - - def test_metaclass(self): - self.assertEqual(type(HasTraits), MetaHasTraits) - - class A(HasTraits): - a = Int() - - a = A() - self.assertEqual(type(a.__class__), MetaHasTraits) - self.assertEqual(a.a,0) - a.a = 10 - self.assertEqual(a.a,10) - - class B(HasTraits): - b = Int() - - b = B() - self.assertEqual(b.b,0) - b.b = 10 - self.assertEqual(b.b,10) - - class C(HasTraits): - c = Int(30) - - c = C() - self.assertEqual(c.c,30) - c.c = 10 - self.assertEqual(c.c,10) - - def test_this_class(self): - class A(HasTraits): - t = This() - tt = This() - class B(A): - tt = This() - ttt = This() - self.assertEqual(A.t.this_class, A) - self.assertEqual(B.t.this_class, A) - self.assertEqual(B.tt.this_class, B) - self.assertEqual(B.ttt.this_class, B) - -class TestHasDescriptors(TestCase): - - def test_setup_instance(self): - - class FooDescriptor(BaseDescriptor): - - def instance_init(self, inst): - foo = inst.foo # instance should have the attr - - class HasFooDescriptors(HasDescriptors): - - fd = FooDescriptor() - - def setup_instance(self, *args, **kwargs): - self.foo = kwargs.get('foo', None) - super(HasFooDescriptors, self).setup_instance(*args, **kwargs) - - hfd = HasFooDescriptors(foo='bar') - -class TestHasTraitsNotify(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def notify2(self, name, old, new): - self._notify2.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - a.b = 10.0 - self.assertTrue(('b',0.0,10.0) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.on_trait_change(self.notify1,remove=True) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.on_trait_change(self.notify1, 'a') - b.on_trait_change(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in self._notify1) - self.assertTrue(('b',0.0,10.0) in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - _notify1 = [] - def _a_changed(self, name, old, new): - self._notify1.append((name, old, new)) - - a = A() - a.a = 0 - # This is broken!!! - self.assertEqual(len(a._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in a._notify1) - - class B(A): - b = Float() - _notify2 = [] - def _b_changed(self, name, old, new): - self._notify2.append((name, old, new)) - - b = B() - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in b._notify1) - self.assertTrue(('b',0.0,10.0) in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(name): - self.cb = (name,) - def callback2(name, new): - self.cb = (name, new) - def callback3(name, old, new): - self.cb = (name, old, new) - def callback4(name, old, new, obj): - self.cb = (name, old, new, obj) - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.on_trait_change(callback0, 'a', remove=True) - - a.on_trait_change(callback1, 'a') - a.a = 100 - self.assertEqual(self.cb,('a',)) - a.on_trait_change(callback1, 'a', remove=True) - - a.on_trait_change(callback2, 'a') - a.a = 1000 - self.assertEqual(self.cb,('a',1000)) - a.on_trait_change(callback2, 'a', remove=True) - - a.on_trait_change(callback3, 'a') - a.a = 10000 - self.assertEqual(self.cb,('a',1000,10000)) - a.on_trait_change(callback3, 'a', remove=True) - - a.on_trait_change(callback4, 'a') - a.a = 100000 - self.assertEqual(self.cb,('a',10000,100000,a)) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.on_trait_change(callback4, 'a', remove=True) - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.on_trait_change(self.listener1, ['a']) - - def listener1(self, name, old, new): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.on_trait_change(self.listener2) - - def listener2(self, name, old, new): - self.c += 1 - - def _a_changed(self, name, old, new): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - -class TestObserveDecorator(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, change): - self._notify1.append(change) - - def notify2(self, change): - self._notify2.append(change) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - a.b = 10.0 - change = change_dict('b', 0.0, 10.0, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.unobserve(self.notify1) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.observe(self.notify1, 'a') - b.observe(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in self._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - b = Int() - _notify1 = [] - _notify_any = [] - - @observe('a') - def _a_changed(self, change): - self._notify1.append(change) - - @observe(All) - def _any_changed(self, change): - self._notify_any.append(change) - - a = A() - a.a = 0 - self.assertEqual(len(a._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in a._notify1) - a.b = 1 - self.assertEqual(len(a._notify_any), 2) - change = change_dict('b', 0, 1, a, 'change') - self.assertTrue(change in a._notify_any) - - class B(A): - b = Float() - _notify2 = [] - @observe('b') - def _b_changed(self, change): - self._notify2.append(change) - - b = B() - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in b._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(change): - self.cb = change - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.unobserve(callback0, 'a') - - a.observe(callback1, 'a') - a.a = 100 - change = change_dict('a', 10, 100, a, 'change') - self.assertEqual(self.cb, change) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.unobserve(callback1, 'a') - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.observe(self.listener1, ['a']) - - def listener1(self, change): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.observe(self.listener2) - - def listener2(self, change): - self.c += 1 - - @observe('a') - def _a_changed(self, change): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - - -class TestHasTraits(TestCase): - - def test_trait_names(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(sorted(a.trait_names()),['f','i']) - self.assertEqual(sorted(A.class_trait_names()),['f','i']) - self.assertTrue(a.has_trait('f')) - self.assertFalse(a.has_trait('g')) - - def test_trait_metadata_deprecated(self): - with expected_warnings(['metadata should be set using the \.tag\(\) method']): - class A(HasTraits): - i = Int(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata_default(self): - class A(HasTraits): - i = Int() - a = A() - self.assertEqual(a.trait_metadata('i', 'config_key'), None) - self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default') - - def test_traits(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f)) - self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f)) - - def test_traits_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='VALUE1', other_thing='VALUE2') - f = Float().tag(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - def test_traits_metadata_deprecated(self): - with expected_warnings(['metadata should be set using the \.tag\(\) method']*2): - class A(HasTraits): - i = Int(config_key='VALUE1', other_thing='VALUE2') - f = Float(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - - def test_init(self): - class A(HasTraits): - i = Int() - x = Float() - a = A(i=1, x=10.0) - self.assertEqual(a.i, 1) - self.assertEqual(a.x, 10.0) - - def test_positional_args(self): - class A(HasTraits): - i = Int(0) - def __init__(self, i): - super(A, self).__init__() - self.i = i - - a = A(5) - self.assertEqual(a.i, 5) - # should raise TypeError if no positional arg given - self.assertRaises(TypeError, A) - -#----------------------------------------------------------------------------- -# Tests for specific trait types -#----------------------------------------------------------------------------- - - -class TestType(TestCase): - - def test_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type(allow_none=True) - - a = A() - self.assertEqual(a.klass, object) - - a.klass = B - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_default_options(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - # Different possible combinations of options for default_value - # and klass. default_value=None is only valid with allow_none=True. - k1 = Type() - k2 = Type(None, allow_none=True) - k3 = Type(B) - k4 = Type(klass=B) - k5 = Type(default_value=None, klass=B, allow_none=True) - k6 = Type(default_value=C, klass=B) - - self.assertIs(A.k1.default_value, object) - self.assertIs(A.k1.klass, object) - self.assertIs(A.k2.default_value, None) - self.assertIs(A.k2.klass, object) - self.assertIs(A.k3.default_value, B) - self.assertIs(A.k3.klass, B) - self.assertIs(A.k4.default_value, B) - self.assertIs(A.k4.klass, B) - self.assertIs(A.k5.default_value, None) - self.assertIs(A.k5.klass, B) - self.assertIs(A.k6.default_value, C) - self.assertIs(A.k6.klass, B) - - a = A() - self.assertIs(a.k1, object) - self.assertIs(a.k2, None) - self.assertIs(a.k3, B) - self.assertIs(a.k4, B) - self.assertIs(a.k5, None) - self.assertIs(a.k6, C) - - def test_value(self): - - class B(object): pass - class C(object): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', C) - self.assertRaises(TraitError, setattr, a, 'klass', object) - a.klass = B - - def test_allow_none(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', None) - a.klass = C - self.assertEqual(a.klass, C) - - def test_validate_klass(self): - - class A(HasTraits): - klass = Type('no strings allowed') - - self.assertRaises(ImportError, A) - - class A(HasTraits): - klass = Type('rub.adub.Duck') - - self.assertRaises(ImportError, A) - - def test_validate_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type('bad default', B) - - self.assertRaises(ImportError, A) - - class C(HasTraits): - klass = Type(None, B) - - self.assertRaises(TraitError, C) - - def test_str_klass(self): - - class A(HasTraits): - klass = Type('ipython_genutils.ipstruct.Struct') - - from ipython_genutils.ipstruct import Struct - a = A() - a.klass = Struct - self.assertEqual(a.klass, Struct) - - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_set_str_klass(self): - - class A(HasTraits): - klass = Type() - - a = A(klass='ipython_genutils.ipstruct.Struct') - from ipython_genutils.ipstruct import Struct - self.assertEqual(a.klass, Struct) - -class TestInstance(TestCase): - - def test_basic(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class A(HasTraits): - inst = Instance(Foo, allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_default_klass(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class FooInstance(Instance): - klass = Foo - - class A(HasTraits): - inst = FooInstance(allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_unique_default_value(self): - class Foo(object): pass - class A(HasTraits): - inst = Instance(Foo,(),{}) - - a = A() - b = A() - self.assertTrue(a.inst is not b.inst) - - def test_args_kw(self): - class Foo(object): - def __init__(self, c): self.c = c - class Bar(object): pass - class Bah(object): - def __init__(self, c, d): - self.c = c; self.d = d - - class A(HasTraits): - inst = Instance(Foo, (10,)) - a = A() - self.assertEqual(a.inst.c, 10) - - class B(HasTraits): - inst = Instance(Bah, args=(10,), kw=dict(d=20)) - b = B() - self.assertEqual(b.inst.c, 10) - self.assertEqual(b.inst.d, 20) - - class C(HasTraits): - inst = Instance(Foo, allow_none=True) - c = C() - self.assertTrue(c.inst is None) - - def test_bad_default(self): - class Foo(object): pass - - class A(HasTraits): - inst = Instance(Foo) - - a = A() - with self.assertRaises(TraitError): - a.inst - - def test_instance(self): - class Foo(object): pass - - def inner(): - class A(HasTraits): - inst = Instance(Foo()) - - self.assertRaises(TraitError, inner) - - -class TestThis(TestCase): - - def test_this_class(self): - class Foo(HasTraits): - this = This() - - f = Foo() - self.assertEqual(f.this, None) - g = Foo() - f.this = g - self.assertEqual(f.this, g) - self.assertRaises(TraitError, setattr, f, 'this', 10) - - def test_this_inst(self): - class Foo(HasTraits): - this = This() - - f = Foo() - f.this = Foo() - self.assertTrue(isinstance(f.this, Foo)) - - def test_subclass(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - pass - f = Foo() - b = Bar() - f.t = b - b.t = f - self.assertEqual(f.t, b) - self.assertEqual(b.t, f) - - def test_subclass_override(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - t = This() - f = Foo() - b = Bar() - f.t = b - self.assertEqual(f.t, b) - self.assertRaises(TraitError, setattr, b, 't', f) - - def test_this_in_container(self): - - class Tree(HasTraits): - value = Unicode() - leaves = List(This()) - - tree = Tree( - value='foo', - leaves=[Tree(value='bar'), Tree(value='buzz')] - ) - - with self.assertRaises(TraitError): - tree.leaves = [1, 2] - -class TraitTestBase(TestCase): - """A best testing class for basic trait types.""" - - def assign(self, value): - self.obj.value = value - - def coerce(self, value): - return value - - def test_good_values(self): - if hasattr(self, '_good_values'): - for value in self._good_values: - self.assign(value) - self.assertEqual(self.obj.value, self.coerce(value)) - - def test_bad_values(self): - if hasattr(self, '_bad_values'): - for value in self._bad_values: - try: - self.assertRaises(TraitError, self.assign, value) - except AssertionError: - assert False, value - - def test_default_value(self): - if hasattr(self, '_default_value'): - self.assertEqual(self._default_value, self.obj.value) - - def test_allow_none(self): - if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and - None in self._bad_values): - trait=self.obj.traits()['value'] - try: - trait.allow_none = True - self._bad_values.remove(None) - #skip coerce. Allow None casts None to None. - self.assign(None) - self.assertEqual(self.obj.value,None) - self.test_good_values() - self.test_bad_values() - finally: - #tear down - trait.allow_none = False - self._bad_values.append(None) - - def tearDown(self): - # restore default value after tests, if set - if hasattr(self, '_default_value'): - self.obj.value = self._default_value - - -class AnyTrait(HasTraits): - - value = Any() - -class AnyTraitTest(TraitTestBase): - - obj = AnyTrait() - - _default_value = None - _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j] - _bad_values = [] - -class UnionTrait(HasTraits): - - value = Union([Type(), Bool()]) - -class UnionTraitTest(TraitTestBase): - - obj = UnionTrait(value='ipython_genutils.ipstruct.Struct') - _good_values = [int, float, True] - _bad_values = [[], (0,), 1j] - -class OrTrait(HasTraits): - - value = Bool() | Unicode() - -class OrTraitTest(TraitTestBase): - - obj = OrTrait() - _good_values = [True, False, 'ten'] - _bad_values = [[], (0,), 1j] - -class IntTrait(HasTraits): - - value = Int(99, min=-100) - -class TestInt(TraitTestBase): - - obj = IntTrait() - _default_value = 99 - _good_values = [10, -10] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, - 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L', - u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', -200] - if not six.PY3: - _bad_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) - - -class CIntTrait(HasTraits): - value = CInt('5') - -class TestCInt(TraitTestBase): - obj = CIntTrait() - - _default_value = 5 - _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, '10.1', u'10.1'] - - def coerce(self, n): - return int(n) - - -class MinBoundCIntTrait(HasTraits): - value = CInt('5', min=3) - -class TestMinBoundCInt(TestCInt): - obj = MinBoundCIntTrait() - - _default_value = 5 - _good_values = [3, 3.0, '3'] - _bad_values = [2.6, 2, -3, -3.0] - - -class LongTrait(HasTraits): - - value = Long(99 if six.PY3 else long(99)) - -class TestLong(TraitTestBase): - - obj = LongTrait() - - _default_value = 99 if six.PY3 else long(99) - _good_values = [10, -10] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', - '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1', - u'-10.1'] - if not six.PY3: - # maxint undefined on py3, because int == long - _good_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) - _bad_values.extend([[long(10)], (long(10),)]) - - @mark.skipif(six.PY3, reason="not relevant on py3") - def test_cast_small(self): - """Long casts ints to long""" - self.obj.value = 10 - self.assertEqual(type(self.obj.value), long) - - -class MinBoundLongTrait(HasTraits): - value = Long(99 if six.PY3 else long(99), min=5) - -class TestMinBoundLong(TraitTestBase): - obj = MinBoundLongTrait() - - _default_value = 99 if six.PY3 else long(99) - _good_values = [5, 10] - _bad_values = [4, -10] - - -class MaxBoundLongTrait(HasTraits): - value = Long(5 if six.PY3 else long(5), max=10) - -class TestMaxBoundLong(TraitTestBase): - obj = MaxBoundLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = [10, -2] - _bad_values = [11, 20] - - -class CLongTrait(HasTraits): - value = CLong('5') - -class TestCLong(TraitTestBase): - obj = CLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), - None, 1j, '10.1', u'10.1'] - - def coerce(self, n): - return int(n) if six.PY3 else long(n) - - -class MaxBoundCLongTrait(HasTraits): - value = CLong('5', max=10) - -class TestMaxBoundCLong(TestCLong): - obj = MaxBoundCLongTrait() - - _default_value = 5 if six.PY3 else long(5) - _good_values = [10, '10', 10.3] - _bad_values = [11.0, '11'] - - -class IntegerTrait(HasTraits): - value = Integer(1) - -class TestInteger(TestLong): - obj = IntegerTrait() - _default_value = 1 - - def coerce(self, n): - return int(n) - - @mark.skipif(six.PY3, reason="not relevant on py3") - def test_cast_small(self): - """Integer casts small longs to int""" - - self.obj.value = long(100) - self.assertEqual(type(self.obj.value), int) - - -class MinBoundIntegerTrait(HasTraits): - value = Integer(5, min=3) - -class TestMinBoundInteger(TraitTestBase): - obj = MinBoundIntegerTrait() - - _default_value = 5 - _good_values = 3, 20 - _bad_values = [2, -10] - - -class MaxBoundIntegerTrait(HasTraits): - value = Integer(1, max=3) - -class TestMaxBoundInteger(TraitTestBase): - obj = MaxBoundIntegerTrait() - - _default_value = 1 - _good_values = 3, -2 - _bad_values = [4, 10] - - -class FloatTrait(HasTraits): - - value = Float(99.0, max=200.0) - -class TestFloat(TraitTestBase): - - obj = FloatTrait() - - _default_value = 99.0 - _good_values = [10, -10, 10.1, -10.1] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, - 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10', - u'-10', u'10L', u'-10L', u'10.1', u'-10.1', 201.0] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class CFloatTrait(HasTraits): - - value = CFloat('99.0', max=200.0) - -class TestCFloat(TraitTestBase): - - obj = CFloatTrait() - - _default_value = 99.0 - _good_values = [10, 10.0, 10.5, '10.0', '10', '-10', '10.0', u'10'] - _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, - 200.1, '200.1'] - - def coerce(self, v): - return float(v) - - -class ComplexTrait(HasTraits): - - value = Complex(99.0-99.0j) - -class TestComplex(TraitTestBase): - - obj = ComplexTrait() - - _default_value = 99.0-99.0j - _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j, - 10.1j, 10.1+10.1j, 10.1-10.1j] - _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class BytesTrait(HasTraits): - - value = Bytes(b'string') - -class TestBytes(TraitTestBase): - - obj = BytesTrait() - - _default_value = b'string' - _good_values = [b'10', b'-10', b'10L', - b'-10L', b'10.1', b'-10.1', b'string'] - _bad_values = [10, -10, 10.1, -10.1, 1j, [10], - ['ten'],{'ten': 10},(10,), None, u'string'] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class UnicodeTrait(HasTraits): - - value = Unicode(u'unicode') - -class TestUnicode(TraitTestBase): - - obj = UnicodeTrait() - - _default_value = u'unicode' - _good_values = ['10', '-10', '10L', '-10L', '10.1', - '-10.1', '', u'', 'string', u'string', u"€"] - _bad_values = [10, -10, 10.1, -10.1, 1j, - [10], ['ten'], [u'ten'], {'ten': 10},(10,), None] - if not six.PY3: - _bad_values.extend([long(10), long(-10)]) - - -class ObjectNameTrait(HasTraits): - value = ObjectName("abc") - -class TestObjectName(TraitTestBase): - obj = ObjectNameTrait() - - _default_value = "abc" - _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"] - _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]", - None, object(), object] - if sys.version_info[0] < 3: - _bad_values.append(u"þ") - else: - _good_values.append(u"þ") # þ=1 is valid in Python 3 (PEP 3131). - - -class DottedObjectNameTrait(HasTraits): - value = DottedObjectName("a.b") - -class TestDottedObjectName(TraitTestBase): - obj = DottedObjectNameTrait() - - _default_value = "a.b" - _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"] - _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc.", None] - if sys.version_info[0] < 3: - _bad_values.append(u"t.þ") - else: - _good_values.append(u"t.þ") - - -class TCPAddressTrait(HasTraits): - value = TCPAddress() - -class TestTCPAddress(TraitTestBase): - - obj = TCPAddressTrait() - - _default_value = ('127.0.0.1',0) - _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] - _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None] - -class ListTrait(HasTraits): - - value = List(Int()) - -class TestList(TraitTestBase): - - obj = ListTrait() - - _default_value = [] - _good_values = [[], [1], list(range(10)), (1,2)] - _bad_values = [10, [1,'a'], 'a'] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - -class Foo(object): - pass - -class NoneInstanceListTrait(HasTraits): - - value = List(Instance(Foo)) - -class TestNoneInstanceList(TraitTestBase): - - obj = NoneInstanceListTrait() - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [[None], [Foo(), None]] - - -class InstanceListTrait(HasTraits): - - value = List(Instance(__name__+'.Foo')) - -class TestInstanceList(TraitTestBase): - - obj = InstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, Foo) - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [['1', 2,], '1', [Foo], None] - -class UnionListTrait(HasTraits): - - value = List(Int() | Bool()) - -class TestUnionListTrait(HasTraits): - - obj = UnionListTrait() - - _default_value = [] - _good_values = [[True, 1], [False, True]] - _bad_values = [[1, 'True'], False] - - -class LenListTrait(HasTraits): - - value = List(Int(), [0], minlen=1, maxlen=2) - -class TestLenList(TraitTestBase): - - obj = LenListTrait() - - _default_value = [0] - _good_values = [[1], [1,2], (1,2)] - _bad_values = [10, [1,'a'], 'a', [], list(range(3))] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - -class TupleTrait(HasTraits): - - value = Tuple(Int(allow_none=True), default_value=(1,)) - -class TestTupleTrait(TraitTestBase): - - obj = TupleTrait() - - _default_value = (1,) - _good_values = [(1,), (0,), [1]] - _bad_values = [10, (1, 2), ('a'), (), None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - -class LooseTupleTrait(HasTraits): - - value = Tuple((1,2,3)) - -class TestLooseTupleTrait(TraitTestBase): - - obj = LooseTupleTrait() - - _default_value = (1,2,3) - _good_values = [(1,), [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] - _bad_values = [10, 'hello', {}, None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - - -class MultiTupleTrait(HasTraits): - - value = Tuple(Int(), Bytes(), default_value=[99,b'bottles']) - -class TestMultiTuple(TraitTestBase): - - obj = MultiTupleTrait() - - _default_value = (99,b'bottles') - _good_values = [(1,b'a'), (2,b'b')] - _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a')) - -class CRegExpTrait(HasTraits): - - value = CRegExp(r'') - -class TestCRegExp(TraitTestBase): - - def coerce(self, value): - return re.compile(value) - - obj = CRegExpTrait() - - _default_value = re.compile(r'') - _good_values = [r'\d+', re.compile(r'\d+')] - _bad_values = ['(', None, ()] - -class DictTrait(HasTraits): - value = Dict() - -def test_dict_assignment(): - d = dict() - c = DictTrait() - c.value = d - d['a'] = 5 - assert d == c.value - assert c.value is d - - -class UniformlyValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), - default_value={'foo': '1'}) - - -class TestInstanceUniformlyValidatedDict(TraitTestBase): - - obj = UniformlyValidatedDictTrait() - - _default_value = {'foo': '1'} - _good_values = [{'foo': '0', 'bar': '1'}] - _bad_values = [{'foo': 0, 'bar': '1'}] - - -class KeyValidatedDictTrait(HasTraits): - - value = Dict(traits={'foo': Int()}, - default_value={'foo': 1}) - - -class TestInstanceKeyValidatedDict(TraitTestBase): - - obj = KeyValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 0, 'bar': 1}] - _bad_values = [{'foo': '0', 'bar': '1'}] - - -class FullyValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), - traits={'foo': Int()}, - default_value={'foo': 1}) - - -class TestInstanceFullyValidatedDict(TraitTestBase): - - obj = FullyValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 1, 'bar': '2'}] - _bad_values = [{'foo': 0, 'bar': 1}, {'foo': '0', 'bar': '1'}] - - -def test_dict_default_value(): - """Check that the `{}` default value of the Dict traitlet constructor is - actually copied.""" - - class Foo(HasTraits): - d1 = Dict() - d2 = Dict() - - foo = Foo() - assert foo.d1 == {} - assert foo.d2 == {} - assert foo.d1 is not foo.d2 - - -class TestValidationHook(TestCase): - - def test_parity_trait(self): - """Verify that the early validation hook is effective""" - - class Parity(HasTraits): - - value = Int(0) - parity = Enum(['odd', 'even'], default_value='even') - - @validate('value') - def _value_validate(self, proposal): - value = proposal['value'] - if self.parity == 'even' and value % 2: - raise TraitError('Expected an even number') - if self.parity == 'odd' and (value % 2 == 0): - raise TraitError('Expected an odd number') - return value - - u = Parity() - u.parity = 'odd' - u.value = 1 # OK - with self.assertRaises(TraitError): - u.value = 2 # Trait Error - - u.parity = 'even' - u.value = 2 # OK - - def test_multiple_validate(self): - """Verify that we can register the same validator to multiple names""" - - class OddEven(HasTraits): - - odd = Int(1) - even = Int(0) - - @validate('odd', 'even') - def check_valid(self, proposal): - if proposal['trait'].name == 'odd' and not proposal['value'] % 2: - raise TraitError('odd should be odd') - if proposal['trait'].name == 'even' and proposal['value'] % 2: - raise TraitError('even should be even') - - u = OddEven() - u.odd = 3 # OK - with self.assertRaises(TraitError): - u.odd = 2 # Trait Error - - u.even = 2 # OK - with self.assertRaises(TraitError): - u.even = 3 # Trait Error - - - -class TestLink(TestCase): - - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.value) - b.value = 6 - self.assertEqual(a.value, b.value) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.count) - b.count = 4 - self.assertEqual(a.value, b.count) - - def test_unlink(self): - """Verify two linked traitlets can be unlinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) - - def test_callbacks(self): - """Verify two linked traitlets have their callbacks called once.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Register callbacks that count. - callback_count = [] - def a_callback(name, old, new): - callback_count.append('a') - a.on_trait_change(a_callback, 'value') - def b_callback(name, old, new): - callback_count.append('b') - b.on_trait_change(b_callback, 'count') - - # Connect the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure b's count was set to a's value once. - self.assertEqual(''.join(callback_count), 'b') - del callback_count[:] - - # Make sure a's value was set to b's count once. - b.count = 5 - self.assertEqual(''.join(callback_count), 'ba') - del callback_count[:] - - # Make sure b's count was set to a's value once. - a.value = 4 - self.assertEqual(''.join(callback_count), 'ab') - del callback_count[:] - -class TestDirectionalLink(TestCase): - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using directional_link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.value, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_tranform(self): - """Test transform link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value'), lambda x: 2 * x) - - # Make sure the values are correct at the point of linking. - self.assertEqual(b.value, 2 * a.value) - - # Change one the value of the source and check that it modifies the target. - a.value = 5 - self.assertEqual(b.value, 10) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.count, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_unlink(self): - """Verify two linked traitlets can be unlinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = directional_link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) - -class Pickleable(HasTraits): - - i = Int() - @observe('i') - def _i_changed(self, change): pass - @validate('i') - def _i_validate(self, commit): - return commit['value'] - - j = Int() - - def __init__(self): - with self.hold_trait_notifications(): - self.i = 1 - self.on_trait_change(self._i_changed, 'i') - -def test_pickle_hastraits(): - c = Pickleable() - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - c.i = 5 - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - -def test_hold_trait_notifications(): - changes = [] - - class Test(HasTraits): - a = Integer(0) - b = Integer(0) - - def _a_changed(self, name, old, new): - changes.append((old, new)) - - def _b_validate(self, value, trait): - if value != 0: - raise TraitError('Only 0 is a valid value') - return value - - # Test context manager and nesting - t = Test() - with t.hold_trait_notifications(): - with t.hold_trait_notifications(): - t.a = 1 - assert t.a == 1 - assert changes == [] - t.a = 2 - assert t.a == 2 - with t.hold_trait_notifications(): - t.a = 3 - assert t.a == 3 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - - assert changes == [(0, 4)] - # Test roll-back - try: - with t.hold_trait_notifications(): - t.b = 1 # raises a Trait error - except: - pass - assert t.b == 0 - - -class RollBack(HasTraits): - bar = Int() - def _bar_validate(self, value, trait): - if value: - raise TraitError('foobar') - return value - - -class TestRollback(TestCase): - - def test_roll_back(self): - - def assign_rollback(): - RollBack(bar=1) - - self.assertRaises(TraitError, assign_rollback) - - -class CacheModification(HasTraits): - foo = Int() - bar = Int() - - def _bar_validate(self, value, trait): - self.foo = value - return value - - def _foo_validate(self, value, trait): - self.bar = value - return value - - -def test_cache_modification(): - CacheModification(foo=1) - CacheModification(bar=1) - - -class OrderTraits(HasTraits): - notified = Dict() - - a = Unicode() - b = Unicode() - c = Unicode() - d = Unicode() - e = Unicode() - f = Unicode() - g = Unicode() - h = Unicode() - i = Unicode() - j = Unicode() - k = Unicode() - l = Unicode() - - def _notify(self, name, old, new): - """check the value of all traits when each trait change is triggered - - This verifies that the values are not sensitive - to dict ordering when loaded from kwargs - """ - # check the value of the other traits - # when a given trait change notification fires - self.notified[name] = { - c: getattr(self, c) for c in 'abcdefghijkl' - } - - def __init__(self, **kwargs): - self.on_trait_change(self._notify) - super(OrderTraits, self).__init__(**kwargs) - -def test_notification_order(): - d = {c:c for c in 'abcdefghijkl'} - obj = OrderTraits() - assert obj.notified == {} - obj = OrderTraits(**d) - notifications = { - c: d for c in 'abcdefghijkl' - } - assert obj.notified == notifications - - - -### -# Traits for Forward Declaration Tests -### -class ForwardDeclaredInstanceTrait(HasTraits): - - value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredTypeTrait(HasTraits): - - value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredInstanceListTrait(HasTraits): - - value = List(ForwardDeclaredInstance('ForwardDeclaredBar')) - -class ForwardDeclaredTypeListTrait(HasTraits): - - value = List(ForwardDeclaredType('ForwardDeclaredBar')) -### -# End Traits for Forward Declaration Tests -### - -### -# Classes for Forward Declaration Tests -### -class ForwardDeclaredBar(object): - pass - -class ForwardDeclaredBarSub(ForwardDeclaredBar): - pass -### -# End Classes for Forward Declaration Tests -### - -### -# Forward Declaration Tests -### -class TestForwardDeclaredInstanceTrait(TraitTestBase): - - obj = ForwardDeclaredInstanceTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub] - -class TestForwardDeclaredTypeTrait(TraitTestBase): - - obj = ForwardDeclaredTypeTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub] - _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - -class TestForwardDeclaredInstanceList(TraitTestBase): - - obj = ForwardDeclaredInstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar(), ForwardDeclaredBarSub()], - [], - ] - _bad_values = [ - ForwardDeclaredBar(), - [ForwardDeclaredBar(), 3, None], - '1', - # Note that this is the type, not an instance. - [ForwardDeclaredBar], - [None], - None, - ] - -class TestForwardDeclaredTypeList(TraitTestBase): - - obj = ForwardDeclaredTypeListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar, ForwardDeclaredBarSub], - [], - ] - _bad_values = [ - ForwardDeclaredBar, - [ForwardDeclaredBar, 3], - '1', - # Note that this is an instance, not the type. - [ForwardDeclaredBar()], - [None], - None, - ] -### -# End Forward Declaration Tests -### - -class TestDynamicTraits(TestCase): - - def setUp(self): - self._notify1 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - pass - - a = A() - self.assertTrue(not hasattr(a, 'x')) - self.assertTrue(not hasattr(a, 'y')) - - # Dynamically add trait x. - a.add_traits(x=Int()) - self.assertTrue(hasattr(a, 'x')) - self.assertTrue(isinstance(a, (A, ))) - - # Dynamically add trait y. - a.add_traits(y=Float()) - self.assertTrue(hasattr(a, 'y')) - self.assertTrue(isinstance(a, (A, ))) - self.assertEqual(a.__class__.__name__, A.__name__) - - # Create a new instance and verify that x and y - # aren't defined. - b = A() - self.assertTrue(not hasattr(b, 'x')) - self.assertTrue(not hasattr(b, 'y')) - - # Verify that notification works like normal. - a.on_trait_change(self.notify1) - a.x = 0 - self.assertEqual(len(self._notify1), 0) - a.y = 0.0 - self.assertEqual(len(self._notify1), 0) - a.x = 10 - self.assertTrue(('x', 0, 10) in self._notify1) - a.y = 10.0 - self.assertTrue(('y', 0.0, 10.0) in self._notify1) - self.assertRaises(TraitError, setattr, a, 'x', 'bad string') - self.assertRaises(TraitError, setattr, a, 'y', 'bad string') - self._notify1 = [] - a.on_trait_change(self.notify1, remove=True) - a.x = 20 - a.y = 20.0 - self.assertEqual(len(self._notify1), 0) - - -def test_enum_no_default(): - class C(HasTraits): - t = Enum(['a', 'b']) - - c = C() - c.t = 'a' - assert c.t == 'a' - - c = C() - - with pytest.raises(TraitError): - t = c.t - - c = C(t='b') - assert c.t == 'b' - - -def test_default_value_repr(): - class C(HasTraits): - t = Type('traitlets.HasTraits') - t2 = Type(HasTraits) - n = Integer(0) - lis = List() - d = Dict() - - assert C.t.default_value_repr() == "'traitlets.HasTraits'" - assert C.t2.default_value_repr() == "'traitlets.traitlets.HasTraits'" - assert C.n.default_value_repr() == '0' - assert C.lis.default_value_repr() == '[]' - assert C.d.default_value_repr() == '{}' - - -class TransitionalClass(HasTraits): - - d = Any() - @default('d') - def _d_default(self): - return TransitionalClass - - parent_super = False - calls_super = Integer(0) - - @default('calls_super') - def _calls_super_default(self): - return -1 - - @observe('calls_super') - @observe_compat - def _calls_super_changed(self, change): - self.parent_super = change - - parent_override = False - overrides = Integer(0) - - @observe('overrides') - @observe_compat - def _overrides_changed(self, change): - self.parent_override = change - - -class SubClass(TransitionalClass): - def _d_default(self): - return SubClass - - subclass_super = False - def _calls_super_changed(self, name, old, new): - self.subclass_super = True - super(SubClass, self)._calls_super_changed(name, old, new) - - subclass_override = False - def _overrides_changed(self, name, old, new): - self.subclass_override = True - - -def test_subclass_compat(): - obj = SubClass() - obj.calls_super = 5 - assert obj.parent_super - assert obj.subclass_super - obj.overrides = 5 - assert obj.subclass_override - assert not obj.parent_override - assert obj.d is SubClass - - -class DefinesHandler(HasTraits): - parent_called = False - - trait = Integer() - @observe('trait') - def handler(self, change): - self.parent_called = True - - -class OverridesHandler(DefinesHandler): - child_called = False - - @observe('trait') - def handler(self, change): - self.child_called = True - - -def test_subclass_override_observer(): - obj = OverridesHandler() - obj.trait = 5 - assert obj.child_called - assert not obj.parent_called - - -class DoesntRegisterHandler(DefinesHandler): - child_called = False - - def handler(self, change): - self.child_called = True - - -def test_subclass_override_not_registered(): - """Subclass that overrides observer and doesn't re-register unregisters both""" - obj = DoesntRegisterHandler() - obj.trait = 5 - assert not obj.child_called - assert not obj.parent_called - - -class AddsHandler(DefinesHandler): - child_called = False - - @observe('trait') - def child_handler(self, change): - self.child_called = True - -def test_subclass_add_observer(): - obj = AddsHandler() - obj.trait = 5 - assert obj.child_called - assert obj.parent_called - - -def test_observe_iterables(): - - class C(HasTraits): - i = Integer() - s = Unicode() - - c = C() - recorded = {} - def record(change): - recorded['change'] = change - - # observe with names=set - c.observe(record, names={'i', 's'}) - c.i = 5 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 5 - c.s = 'hi' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'hi' - - # observe with names=custom container with iter, contains - class MyContainer(object): - def __init__(self, container): - self.container = container - - def __iter__(self): - return iter(self.container) - - def __contains__(self, key): - return key in self.container - - c.observe(record, names=MyContainer({'i', 's'})) - c.i = 10 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 10 - c.s = 'ok' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'ok' - - -def test_super_args(): - class SuperRecorder(object): - def __init__(self, *args, **kwargs): - self.super_args = args - self.super_kwargs = kwargs - - class SuperHasTraits(HasTraits, SuperRecorder): - i = Integer() - - obj = SuperHasTraits('a1', 'a2', b=10, i=5, c='x') - assert obj.i == 5 - assert not hasattr(obj, 'b') - assert not hasattr(obj, 'c') - assert obj.super_args == ('a1' , 'a2') - assert obj.super_kwargs == {'b': 10 , 'c': 'x'} - -def test_super_bad_args(): - class SuperHasTraits(HasTraits): - a = Integer() - - if sys.version_info < (3,): - # Legacy Python, object.__init__ warns itself, instead of raising - w = ['object.__init__'] - else: - w = ["Passing unrecoginized arguments"] - with expected_warnings(w): - obj = SuperHasTraits(a=1, b=2) - assert obj.a == 1 - assert not hasattr(obj, 'b') + +from unittest import TestCase +import pytest +from pytest import mark + +from traitlets import ( + HasTraits, MetaHasTraits, TraitType, Any, Bool, CBytes, Dict, Enum, + Int, CInt, Long, CLong, Integer, Float, CFloat, Complex, Bytes, Unicode, + 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, +) + +import six + +def change_dict(*ordered_values): + change_names = ('name', 'old', 'new', 'owner', 'type') + return dict(zip(change_names, ordered_values)) + +#----------------------------------------------------------------------------- +# Helper classes for testing +#----------------------------------------------------------------------------- + + +class HasTraitsStub(HasTraits): + + def notify_change(self, change): + self._notify_name = change['name'] + self._notify_old = change['old'] + self._notify_new = change['new'] + self._notify_type = change['type'] + + +#----------------------------------------------------------------------------- +# Test classes +#----------------------------------------------------------------------------- + + +class TestTraitType(TestCase): + + def test_get_undefined(self): + class A(HasTraits): + a = TraitType + a = A() + with self.assertRaises(TraitError): + a.a + + def test_set(self): + class A(HasTraitsStub): + a = TraitType + + a = A() + a.a = 10 + self.assertEqual(a.a, 10) + self.assertEqual(a._notify_name, 'a') + self.assertEqual(a._notify_old, Undefined) + self.assertEqual(a._notify_new, 10) + + def test_validate(self): + class MyTT(TraitType): + def validate(self, inst, value): + return -1 + class A(HasTraitsStub): + tt = MyTT + + a = A() + a.tt = 10 + self.assertEqual(a.tt, -1) + + def test_default_validate(self): + class MyIntTT(TraitType): + def validate(self, obj, value): + if isinstance(value, int): + return value + self.error(obj, value) + class A(HasTraits): + tt = MyIntTT(10) + a = A() + self.assertEqual(a.tt, 10) + + # Defaults are validated when the HasTraits is instantiated + class B(HasTraits): + tt = MyIntTT('bad default') + self.assertRaises(TraitError, B) + + def test_info(self): + class A(HasTraits): + tt = TraitType + a = A() + self.assertEqual(A.tt.info(), 'any value') + + def test_error(self): + class A(HasTraits): + tt = TraitType + a = A() + self.assertRaises(TraitError, A.tt.error, a, 10) + + def test_deprecated_dynamic_initializer(self): + class A(HasTraits): + x = Int(10) + def _x_default(self): + return 11 + class B(A): + x = Int(20) + class C(A): + def _x_default(self): + return 21 + + a = A() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + b = B() + self.assertEqual(b.x, 20) + self.assertEqual(b._trait_values, {'x': 20}) + c = C() + self.assertEqual(c._trait_values, {}) + self.assertEqual(c.x, 21) + self.assertEqual(c._trait_values, {'x': 21}) + # Ensure that the base class remains unmolested when the _default + # initializer gets overridden in a subclass. + a = A() + c = C() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + + def test_dynamic_initializer(self): + + class A(HasTraits): + x = Int(10) + + @default('x') + def _default_x(self): + return 11 + + class B(A): + x = Int(20) + + class C(A): + + @default('x') + def _default_x(self): + return 21 + + a = A() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + b = B() + self.assertEqual(b.x, 20) + self.assertEqual(b._trait_values, {'x': 20}) + c = C() + self.assertEqual(c._trait_values, {}) + self.assertEqual(c.x, 21) + self.assertEqual(c._trait_values, {'x': 21}) + # Ensure that the base class remains unmolested when the _default + # initializer gets overridden in a subclass. + a = A() + c = C() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + + def test_tag_metadata(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10).tag(b=3, c=4) + self.assertEqual(a.metadata, {'a': 1, 'b': 3, 'c': 4}) + + def test_metadata_localized_instance(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10) + b = MyIntTT(10) + a.metadata['c'] = 3 + # make sure that changing a's metadata didn't change b's metadata + self.assertNotIn('c', b.metadata) + + def test_union_metadata(self): + class Foo(HasTraits): + bar = (Int().tag(ta=1) | Dict().tag(ta=2, ti='b')).tag(ti='a') + foo = Foo() + # At this point, no value has been set for bar, so value-specific + # is not set. + self.assertEqual(foo.trait_metadata('bar', 'ta'), None) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') + foo.bar = {} + self.assertEqual(foo.trait_metadata('bar', 'ta'), 2) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'b') + foo.bar = 1 + self.assertEqual(foo.trait_metadata('bar', 'ta'), 1) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') + + def test_union_default_value(self): + class Foo(HasTraits): + bar = Union([Dict(), Int()], default_value=1) + foo = Foo() + self.assertEqual(foo.bar, 1) + + def test_deprecated_metadata_access(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10) + with expected_warnings(["use the instance .metadata dictionary directly"]*2): + a.set_metadata('key', 'value') + v = a.get_metadata('key') + self.assertEqual(v, 'value') + with expected_warnings(["use the instance .help string directly"]*2): + a.set_metadata('help', 'some help') + v = a.get_metadata('help') + self.assertEqual(v, 'some help') + + def test_trait_types_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Int + + def test_trait_types_list_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = List(Int) + + def test_trait_types_tuple_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Tuple(Int) + + def test_trait_types_dict_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Dict(Int) + +class TestHasDescriptorsMeta(TestCase): + + def test_metaclass(self): + self.assertEqual(type(HasTraits), MetaHasTraits) + + class A(HasTraits): + a = Int() + + a = A() + self.assertEqual(type(a.__class__), MetaHasTraits) + self.assertEqual(a.a,0) + a.a = 10 + self.assertEqual(a.a,10) + + class B(HasTraits): + b = Int() + + b = B() + self.assertEqual(b.b,0) + b.b = 10 + self.assertEqual(b.b,10) + + class C(HasTraits): + c = Int(30) + + c = C() + self.assertEqual(c.c,30) + c.c = 10 + self.assertEqual(c.c,10) + + def test_this_class(self): + class A(HasTraits): + t = This() + tt = This() + class B(A): + tt = This() + ttt = This() + self.assertEqual(A.t.this_class, A) + self.assertEqual(B.t.this_class, A) + self.assertEqual(B.tt.this_class, B) + self.assertEqual(B.ttt.this_class, B) + +class TestHasDescriptors(TestCase): + + def test_setup_instance(self): + + class FooDescriptor(BaseDescriptor): + + def instance_init(self, inst): + foo = inst.foo # instance should have the attr + + class HasFooDescriptors(HasDescriptors): + + fd = FooDescriptor() + + def setup_instance(self, *args, **kwargs): + self.foo = kwargs.get('foo', None) + super(HasFooDescriptors, self).setup_instance(*args, **kwargs) + + hfd = HasFooDescriptors(foo='bar') + +class TestHasTraitsNotify(TestCase): + + def setUp(self): + self._notify1 = [] + self._notify2 = [] + + def notify1(self, name, old, new): + self._notify1.append((name, old, new)) + + def notify2(self, name, old, new): + self._notify2.append((name, old, new)) + + def test_notify_all(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.on_trait_change(self.notify1) + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.b = 0.0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in self._notify1) + a.b = 10.0 + self.assertTrue(('b',0.0,10.0) in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + self.assertRaises(TraitError,setattr,a,'b','bad string') + self._notify1 = [] + a.on_trait_change(self.notify1,remove=True) + a.a = 20 + a.b = 20.0 + self.assertEqual(len(self._notify1),0) + + def test_notify_one(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.on_trait_change(self.notify1, 'a') + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + + def test_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + self.assertEqual(b.a,0) + self.assertEqual(b.b,0.0) + b.a = 100 + b.b = 100.0 + self.assertEqual(b.a,100) + self.assertEqual(b.b,100.0) + + def test_notify_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + b.on_trait_change(self.notify1, 'a') + b.on_trait_change(self.notify2, 'b') + b.a = 0 + b.b = 0.0 + self.assertEqual(len(self._notify1),0) + self.assertEqual(len(self._notify2),0) + b.a = 10 + b.b = 10.0 + self.assertTrue(('a',0,10) in self._notify1) + self.assertTrue(('b',0.0,10.0) in self._notify2) + + def test_static_notify(self): + + class A(HasTraits): + a = Int() + _notify1 = [] + def _a_changed(self, name, old, new): + self._notify1.append((name, old, new)) + + a = A() + a.a = 0 + # This is broken!!! + self.assertEqual(len(a._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in a._notify1) + + class B(A): + b = Float() + _notify2 = [] + def _b_changed(self, name, old, new): + self._notify2.append((name, old, new)) + + b = B() + b.a = 10 + b.b = 10.0 + self.assertTrue(('a',0,10) in b._notify1) + self.assertTrue(('b',0.0,10.0) in b._notify2) + + def test_notify_args(self): + + def callback0(): + self.cb = () + def callback1(name): + self.cb = (name,) + def callback2(name, new): + self.cb = (name, new) + def callback3(name, old, new): + self.cb = (name, old, new) + def callback4(name, old, new, obj): + self.cb = (name, old, new, obj) + + class A(HasTraits): + a = Int() + + a = A() + a.on_trait_change(callback0, 'a') + a.a = 10 + self.assertEqual(self.cb,()) + a.on_trait_change(callback0, 'a', remove=True) + + a.on_trait_change(callback1, 'a') + a.a = 100 + self.assertEqual(self.cb,('a',)) + a.on_trait_change(callback1, 'a', remove=True) + + a.on_trait_change(callback2, 'a') + a.a = 1000 + self.assertEqual(self.cb,('a',1000)) + a.on_trait_change(callback2, 'a', remove=True) + + a.on_trait_change(callback3, 'a') + a.a = 10000 + self.assertEqual(self.cb,('a',1000,10000)) + a.on_trait_change(callback3, 'a', remove=True) + + a.on_trait_change(callback4, 'a') + a.a = 100000 + self.assertEqual(self.cb,('a',10000,100000,a)) + self.assertEqual(len(a._trait_notifiers['a']['change']), 1) + a.on_trait_change(callback4, 'a', remove=True) + + self.assertEqual(len(a._trait_notifiers['a']['change']), 0) + + def test_notify_only_once(self): + + class A(HasTraits): + listen_to = ['a'] + + a = Int(0) + b = 0 + + def __init__(self, **kwargs): + super(A, self).__init__(**kwargs) + self.on_trait_change(self.listener1, ['a']) + + def listener1(self, name, old, new): + self.b += 1 + + class B(A): + + c = 0 + d = 0 + + def __init__(self, **kwargs): + super(B, self).__init__(**kwargs) + self.on_trait_change(self.listener2) + + def listener2(self, name, old, new): + self.c += 1 + + def _a_changed(self, name, old, new): + self.d += 1 + + b = B() + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + +class TestObserveDecorator(TestCase): + + def setUp(self): + self._notify1 = [] + self._notify2 = [] + + def notify1(self, change): + self._notify1.append(change) + + def notify2(self, change): + self._notify2.append(change) + + def test_notify_all(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.observe(self.notify1) + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.b = 0.0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in self._notify1) + a.b = 10.0 + change = change_dict('b', 0.0, 10.0, a, 'change') + self.assertTrue(change in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + self.assertRaises(TraitError,setattr,a,'b','bad string') + self._notify1 = [] + a.unobserve(self.notify1) + a.a = 20 + a.b = 20.0 + self.assertEqual(len(self._notify1),0) + + def test_notify_one(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.observe(self.notify1, 'a') + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + + def test_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + self.assertEqual(b.a,0) + self.assertEqual(b.b,0.0) + b.a = 100 + b.b = 100.0 + self.assertEqual(b.a,100) + self.assertEqual(b.b,100.0) + + def test_notify_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + b.observe(self.notify1, 'a') + b.observe(self.notify2, 'b') + b.a = 0 + b.b = 0.0 + self.assertEqual(len(self._notify1),0) + self.assertEqual(len(self._notify2),0) + b.a = 10 + b.b = 10.0 + change = change_dict('a', 0, 10, b, 'change') + self.assertTrue(change in self._notify1) + change = change_dict('b', 0.0, 10.0, b, 'change') + self.assertTrue(change in self._notify2) + + def test_static_notify(self): + + class A(HasTraits): + a = Int() + b = Int() + _notify1 = [] + _notify_any = [] + + @observe('a') + def _a_changed(self, change): + self._notify1.append(change) + + @observe(All) + def _any_changed(self, change): + self._notify_any.append(change) + + a = A() + a.a = 0 + self.assertEqual(len(a._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in a._notify1) + a.b = 1 + self.assertEqual(len(a._notify_any), 2) + change = change_dict('b', 0, 1, a, 'change') + self.assertTrue(change in a._notify_any) + + class B(A): + b = Float() + _notify2 = [] + @observe('b') + def _b_changed(self, change): + self._notify2.append(change) + + b = B() + b.a = 10 + b.b = 10.0 + change = change_dict('a', 0, 10, b, 'change') + self.assertTrue(change in b._notify1) + change = change_dict('b', 0.0, 10.0, b, 'change') + self.assertTrue(change in b._notify2) + + def test_notify_args(self): + + def callback0(): + self.cb = () + def callback1(change): + self.cb = change + + class A(HasTraits): + a = Int() + + a = A() + a.on_trait_change(callback0, 'a') + a.a = 10 + self.assertEqual(self.cb,()) + a.unobserve(callback0, 'a') + + a.observe(callback1, 'a') + a.a = 100 + change = change_dict('a', 10, 100, a, 'change') + self.assertEqual(self.cb, change) + self.assertEqual(len(a._trait_notifiers['a']['change']), 1) + a.unobserve(callback1, 'a') + + self.assertEqual(len(a._trait_notifiers['a']['change']), 0) + + def test_notify_only_once(self): + + class A(HasTraits): + listen_to = ['a'] + + a = Int(0) + b = 0 + + def __init__(self, **kwargs): + super(A, self).__init__(**kwargs) + self.observe(self.listener1, ['a']) + + def listener1(self, change): + self.b += 1 + + class B(A): + + c = 0 + d = 0 + + def __init__(self, **kwargs): + super(B, self).__init__(**kwargs) + self.observe(self.listener2) + + def listener2(self, change): + self.c += 1 + + @observe('a') + def _a_changed(self, change): + self.d += 1 + + b = B() + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + + +class TestHasTraits(TestCase): + + def test_trait_names(self): + class A(HasTraits): + i = Int() + f = Float() + a = A() + self.assertEqual(sorted(a.trait_names()),['f','i']) + self.assertEqual(sorted(A.class_trait_names()),['f','i']) + self.assertTrue(a.has_trait('f')) + self.assertFalse(a.has_trait('g')) + + def test_trait_metadata_deprecated(self): + with expected_warnings(['metadata should be set using the \.tag\(\) method']): + class A(HasTraits): + i = Int(config_key='MY_VALUE') + a = A() + self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') + + def test_trait_metadata(self): + class A(HasTraits): + i = Int().tag(config_key='MY_VALUE') + a = A() + self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') + + def test_trait_metadata_default(self): + class A(HasTraits): + i = Int() + a = A() + self.assertEqual(a.trait_metadata('i', 'config_key'), None) + self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default') + + def test_traits(self): + class A(HasTraits): + i = Int() + f = Float() + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f)) + self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f)) + + def test_traits_metadata(self): + class A(HasTraits): + i = Int().tag(config_key='VALUE1', other_thing='VALUE2') + f = Float().tag(config_key='VALUE3', other_thing='VALUE2') + j = Int(0) + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) + traits = a.traits(config_key='VALUE1', other_thing='VALUE2') + self.assertEqual(traits, dict(i=A.i)) + + # This passes, but it shouldn't because I am replicating a bug in + # traits. + traits = a.traits(config_key=lambda v: True) + self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) + + def test_traits_metadata_deprecated(self): + with expected_warnings(['metadata should be set using the \.tag\(\) method']*2): + class A(HasTraits): + i = Int(config_key='VALUE1', other_thing='VALUE2') + f = Float(config_key='VALUE3', other_thing='VALUE2') + j = Int(0) + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) + traits = a.traits(config_key='VALUE1', other_thing='VALUE2') + self.assertEqual(traits, dict(i=A.i)) + + # This passes, but it shouldn't because I am replicating a bug in + # traits. + traits = a.traits(config_key=lambda v: True) + self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) + + + def test_init(self): + class A(HasTraits): + i = Int() + x = Float() + a = A(i=1, x=10.0) + self.assertEqual(a.i, 1) + self.assertEqual(a.x, 10.0) + + def test_positional_args(self): + class A(HasTraits): + i = Int(0) + def __init__(self, i): + super(A, self).__init__() + self.i = i + + a = A(5) + self.assertEqual(a.i, 5) + # should raise TypeError if no positional arg given + self.assertRaises(TypeError, A) + +#----------------------------------------------------------------------------- +# Tests for specific trait types +#----------------------------------------------------------------------------- + + +class TestType(TestCase): + + def test_default(self): + + class B(object): pass + class A(HasTraits): + klass = Type(allow_none=True) + + a = A() + self.assertEqual(a.klass, object) + + a.klass = B + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', 10) + + def test_default_options(self): + + class B(object): pass + class C(B): pass + class A(HasTraits): + # Different possible combinations of options for default_value + # and klass. default_value=None is only valid with allow_none=True. + k1 = Type() + k2 = Type(None, allow_none=True) + k3 = Type(B) + k4 = Type(klass=B) + k5 = Type(default_value=None, klass=B, allow_none=True) + k6 = Type(default_value=C, klass=B) + + self.assertIs(A.k1.default_value, object) + self.assertIs(A.k1.klass, object) + self.assertIs(A.k2.default_value, None) + self.assertIs(A.k2.klass, object) + self.assertIs(A.k3.default_value, B) + self.assertIs(A.k3.klass, B) + self.assertIs(A.k4.default_value, B) + self.assertIs(A.k4.klass, B) + self.assertIs(A.k5.default_value, None) + self.assertIs(A.k5.klass, B) + self.assertIs(A.k6.default_value, C) + self.assertIs(A.k6.klass, B) + + a = A() + self.assertIs(a.k1, object) + self.assertIs(a.k2, None) + self.assertIs(a.k3, B) + self.assertIs(a.k4, B) + self.assertIs(a.k5, None) + self.assertIs(a.k6, C) + + def test_value(self): + + class B(object): pass + class C(object): pass + class A(HasTraits): + klass = Type(B) + + a = A() + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', C) + self.assertRaises(TraitError, setattr, a, 'klass', object) + a.klass = B + + def test_allow_none(self): + + class B(object): pass + class C(B): pass + class A(HasTraits): + klass = Type(B) + + a = A() + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', None) + a.klass = C + self.assertEqual(a.klass, C) + + def test_validate_klass(self): + + class A(HasTraits): + klass = Type('no strings allowed') + + self.assertRaises(ImportError, A) + + class A(HasTraits): + klass = Type('rub.adub.Duck') + + self.assertRaises(ImportError, A) + + def test_validate_default(self): + + class B(object): pass + class A(HasTraits): + klass = Type('bad default', B) + + self.assertRaises(ImportError, A) + + class C(HasTraits): + klass = Type(None, B) + + self.assertRaises(TraitError, C) + + def test_str_klass(self): + + class A(HasTraits): + klass = Type('ipython_genutils.ipstruct.Struct') + + from ipython_genutils.ipstruct import Struct + a = A() + a.klass = Struct + self.assertEqual(a.klass, Struct) + + self.assertRaises(TraitError, setattr, a, 'klass', 10) + + def test_set_str_klass(self): + + class A(HasTraits): + klass = Type() + + a = A(klass='ipython_genutils.ipstruct.Struct') + from ipython_genutils.ipstruct import Struct + self.assertEqual(a.klass, Struct) + +class TestInstance(TestCase): + + def test_basic(self): + class Foo(object): pass + class Bar(Foo): pass + class Bah(object): pass + + class A(HasTraits): + inst = Instance(Foo, allow_none=True) + + a = A() + self.assertTrue(a.inst is None) + a.inst = Foo() + self.assertTrue(isinstance(a.inst, Foo)) + a.inst = Bar() + self.assertTrue(isinstance(a.inst, Foo)) + self.assertRaises(TraitError, setattr, a, 'inst', Foo) + self.assertRaises(TraitError, setattr, a, 'inst', Bar) + self.assertRaises(TraitError, setattr, a, 'inst', Bah()) + + def test_default_klass(self): + class Foo(object): pass + class Bar(Foo): pass + class Bah(object): pass + + class FooInstance(Instance): + klass = Foo + + class A(HasTraits): + inst = FooInstance(allow_none=True) + + a = A() + self.assertTrue(a.inst is None) + a.inst = Foo() + self.assertTrue(isinstance(a.inst, Foo)) + a.inst = Bar() + self.assertTrue(isinstance(a.inst, Foo)) + self.assertRaises(TraitError, setattr, a, 'inst', Foo) + self.assertRaises(TraitError, setattr, a, 'inst', Bar) + self.assertRaises(TraitError, setattr, a, 'inst', Bah()) + + def test_unique_default_value(self): + class Foo(object): pass + class A(HasTraits): + inst = Instance(Foo,(),{}) + + a = A() + b = A() + self.assertTrue(a.inst is not b.inst) + + def test_args_kw(self): + class Foo(object): + def __init__(self, c): self.c = c + class Bar(object): pass + class Bah(object): + def __init__(self, c, d): + self.c = c; self.d = d + + class A(HasTraits): + inst = Instance(Foo, (10,)) + a = A() + self.assertEqual(a.inst.c, 10) + + class B(HasTraits): + inst = Instance(Bah, args=(10,), kw=dict(d=20)) + b = B() + self.assertEqual(b.inst.c, 10) + self.assertEqual(b.inst.d, 20) + + class C(HasTraits): + inst = Instance(Foo, allow_none=True) + c = C() + self.assertTrue(c.inst is None) + + def test_bad_default(self): + class Foo(object): pass + + class A(HasTraits): + inst = Instance(Foo) + + a = A() + with self.assertRaises(TraitError): + a.inst + + def test_instance(self): + class Foo(object): pass + + def inner(): + class A(HasTraits): + inst = Instance(Foo()) + + self.assertRaises(TraitError, inner) + + +class TestThis(TestCase): + + def test_this_class(self): + class Foo(HasTraits): + this = This() + + f = Foo() + self.assertEqual(f.this, None) + g = Foo() + f.this = g + self.assertEqual(f.this, g) + self.assertRaises(TraitError, setattr, f, 'this', 10) + + def test_this_inst(self): + class Foo(HasTraits): + this = This() + + f = Foo() + f.this = Foo() + self.assertTrue(isinstance(f.this, Foo)) + + def test_subclass(self): + class Foo(HasTraits): + t = This() + class Bar(Foo): + pass + f = Foo() + b = Bar() + f.t = b + b.t = f + self.assertEqual(f.t, b) + self.assertEqual(b.t, f) + + def test_subclass_override(self): + class Foo(HasTraits): + t = This() + class Bar(Foo): + t = This() + f = Foo() + b = Bar() + f.t = b + self.assertEqual(f.t, b) + self.assertRaises(TraitError, setattr, b, 't', f) + + def test_this_in_container(self): + + class Tree(HasTraits): + value = Unicode() + leaves = List(This()) + + tree = Tree( + value='foo', + leaves=[Tree(value='bar'), Tree(value='buzz')] + ) + + with self.assertRaises(TraitError): + tree.leaves = [1, 2] + +class TraitTestBase(TestCase): + """A best testing class for basic trait types.""" + + def assign(self, value): + self.obj.value = value + + def coerce(self, value): + return value + + def test_good_values(self): + if hasattr(self, '_good_values'): + for value in self._good_values: + self.assign(value) + self.assertEqual(self.obj.value, self.coerce(value)) + + def test_bad_values(self): + if hasattr(self, '_bad_values'): + for value in self._bad_values: + try: + self.assertRaises(TraitError, self.assign, value) + except AssertionError: + assert False, value + + def test_default_value(self): + if hasattr(self, '_default_value'): + self.assertEqual(self._default_value, self.obj.value) + + def test_allow_none(self): + if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and + None in self._bad_values): + trait=self.obj.traits()['value'] + try: + trait.allow_none = True + self._bad_values.remove(None) + #skip coerce. Allow None casts None to None. + self.assign(None) + self.assertEqual(self.obj.value,None) + self.test_good_values() + self.test_bad_values() + finally: + #tear down + trait.allow_none = False + self._bad_values.append(None) + + def tearDown(self): + # restore default value after tests, if set + if hasattr(self, '_default_value'): + self.obj.value = self._default_value + + +class AnyTrait(HasTraits): + + value = Any() + +class AnyTraitTest(TraitTestBase): + + obj = AnyTrait() + + _default_value = None + _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j] + _bad_values = [] + +class UnionTrait(HasTraits): + + value = Union([Type(), Bool()]) + +class UnionTraitTest(TraitTestBase): + + obj = UnionTrait(value='ipython_genutils.ipstruct.Struct') + _good_values = [int, float, True] + _bad_values = [[], (0,), 1j] + +class OrTrait(HasTraits): + + value = Bool() | Unicode() + +class OrTraitTest(TraitTestBase): + + obj = OrTrait() + _good_values = [True, False, 'ten'] + _bad_values = [[], (0,), 1j] + +class IntTrait(HasTraits): + + value = Int(99, min=-100) + +class TestInt(TraitTestBase): + + obj = IntTrait() + _default_value = 99 + _good_values = [10, -10] + _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, + 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L', + u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', -200] + if not six.PY3: + _bad_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) + + +class CIntTrait(HasTraits): + value = CInt('5') + +class TestCInt(TraitTestBase): + obj = CIntTrait() + + _default_value = 5 + _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] + _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), + None, 1j, '10.1', u'10.1'] + + def coerce(self, n): + return int(n) + + +class MinBoundCIntTrait(HasTraits): + value = CInt('5', min=3) + +class TestMinBoundCInt(TestCInt): + obj = MinBoundCIntTrait() + + _default_value = 5 + _good_values = [3, 3.0, '3'] + _bad_values = [2.6, 2, -3, -3.0] + + +class LongTrait(HasTraits): + + value = Long(99 if six.PY3 else long(99)) + +class TestLong(TraitTestBase): + + obj = LongTrait() + + _default_value = 99 if six.PY3 else long(99) + _good_values = [10, -10] + _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), + None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', + '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1', + u'-10.1'] + if not six.PY3: + # maxint undefined on py3, because int == long + _good_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint]) + _bad_values.extend([[long(10)], (long(10),)]) + + @mark.skipif(six.PY3, reason="not relevant on py3") + def test_cast_small(self): + """Long casts ints to long""" + self.obj.value = 10 + self.assertEqual(type(self.obj.value), long) + + +class MinBoundLongTrait(HasTraits): + value = Long(99 if six.PY3 else long(99), min=5) + +class TestMinBoundLong(TraitTestBase): + obj = MinBoundLongTrait() + + _default_value = 99 if six.PY3 else long(99) + _good_values = [5, 10] + _bad_values = [4, -10] + + +class MaxBoundLongTrait(HasTraits): + value = Long(5 if six.PY3 else long(5), max=10) + +class TestMaxBoundLong(TraitTestBase): + obj = MaxBoundLongTrait() + + _default_value = 5 if six.PY3 else long(5) + _good_values = [10, -2] + _bad_values = [11, 20] + + +class CLongTrait(HasTraits): + value = CLong('5') + +class TestCLong(TraitTestBase): + obj = CLongTrait() + + _default_value = 5 if six.PY3 else long(5) + _good_values = ['10', '-10', u'10', u'-10', 10, 10.0, -10.0, 10.1] + _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), + None, 1j, '10.1', u'10.1'] + + def coerce(self, n): + return int(n) if six.PY3 else long(n) + + +class MaxBoundCLongTrait(HasTraits): + value = CLong('5', max=10) + +class TestMaxBoundCLong(TestCLong): + obj = MaxBoundCLongTrait() + + _default_value = 5 if six.PY3 else long(5) + _good_values = [10, '10', 10.3] + _bad_values = [11.0, '11'] + + +class IntegerTrait(HasTraits): + value = Integer(1) + +class TestInteger(TestLong): + obj = IntegerTrait() + _default_value = 1 + + def coerce(self, n): + return int(n) + + @mark.skipif(six.PY3, reason="not relevant on py3") + def test_cast_small(self): + """Integer casts small longs to int""" + + self.obj.value = long(100) + self.assertEqual(type(self.obj.value), int) + + +class MinBoundIntegerTrait(HasTraits): + value = Integer(5, min=3) + +class TestMinBoundInteger(TraitTestBase): + obj = MinBoundIntegerTrait() + + _default_value = 5 + _good_values = 3, 20 + _bad_values = [2, -10] + + +class MaxBoundIntegerTrait(HasTraits): + value = Integer(1, max=3) + +class TestMaxBoundInteger(TraitTestBase): + obj = MaxBoundIntegerTrait() + + _default_value = 1 + _good_values = 3, -2 + _bad_values = [4, 10] + + +class FloatTrait(HasTraits): + + value = Float(99.0, max=200.0) + +class TestFloat(TraitTestBase): + + obj = FloatTrait() + + _default_value = 99.0 + _good_values = [10, -10, 10.1, -10.1] + _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, + 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10', + u'-10', u'10L', u'-10L', u'10.1', u'-10.1', 201.0] + if not six.PY3: + _bad_values.extend([long(10), long(-10)]) + + +class CFloatTrait(HasTraits): + + value = CFloat('99.0', max=200.0) + +class TestCFloat(TraitTestBase): + + obj = CFloatTrait() + + _default_value = 99.0 + _good_values = [10, 10.0, 10.5, '10.0', '10', '-10', '10.0', u'10'] + _bad_values = ['ten', u'ten', [10], {'ten': 10}, (10,), None, 1j, + 200.1, '200.1'] + + def coerce(self, v): + return float(v) + + +class ComplexTrait(HasTraits): + + value = Complex(99.0-99.0j) + +class TestComplex(TraitTestBase): + + obj = ComplexTrait() + + _default_value = 99.0-99.0j + _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j, + 10.1j, 10.1+10.1j, 10.1-10.1j] + _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None] + if not six.PY3: + _bad_values.extend([long(10), long(-10)]) + + +class BytesTrait(HasTraits): + + value = Bytes(b'string') + +class TestBytes(TraitTestBase): + + obj = BytesTrait() + + _default_value = b'string' + _good_values = [b'10', b'-10', b'10L', + b'-10L', b'10.1', b'-10.1', b'string'] + _bad_values = [10, -10, 10.1, -10.1, 1j, [10], + ['ten'],{'ten': 10},(10,), None, u'string'] + if not six.PY3: + _bad_values.extend([long(10), long(-10)]) + + +class UnicodeTrait(HasTraits): + + value = Unicode(u'unicode') + +class TestUnicode(TraitTestBase): + + obj = UnicodeTrait() + + _default_value = u'unicode' + _good_values = ['10', '-10', '10L', '-10L', '10.1', + '-10.1', '', u'', 'string', u'string', u"€"] + _bad_values = [10, -10, 10.1, -10.1, 1j, + [10], ['ten'], [u'ten'], {'ten': 10},(10,), None] + if not six.PY3: + _bad_values.extend([long(10), long(-10)]) + + +class ObjectNameTrait(HasTraits): + value = ObjectName("abc") + +class TestObjectName(TraitTestBase): + obj = ObjectNameTrait() + + _default_value = "abc" + _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"] + _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]", + None, object(), object] + if sys.version_info[0] < 3: + _bad_values.append(u"þ") + else: + _good_values.append(u"þ") # þ=1 is valid in Python 3 (PEP 3131). + + +class DottedObjectNameTrait(HasTraits): + value = DottedObjectName("a.b") + +class TestDottedObjectName(TraitTestBase): + obj = DottedObjectNameTrait() + + _default_value = "a.b" + _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"] + _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc.", None] + if sys.version_info[0] < 3: + _bad_values.append(u"t.þ") + else: + _good_values.append(u"t.þ") + + +class TCPAddressTrait(HasTraits): + value = TCPAddress() + +class TestTCPAddress(TraitTestBase): + + obj = TCPAddressTrait() + + _default_value = ('127.0.0.1',0) + _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] + _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None] + +class ListTrait(HasTraits): + + value = List(Int()) + +class TestList(TraitTestBase): + + obj = ListTrait() + + _default_value = [] + _good_values = [[], [1], list(range(10)), (1,2)] + _bad_values = [10, [1,'a'], 'a'] + + def coerce(self, value): + if value is not None: + value = list(value) + return value + +class Foo(object): + pass + +class NoneInstanceListTrait(HasTraits): + + value = List(Instance(Foo)) + +class TestNoneInstanceList(TraitTestBase): + + obj = NoneInstanceListTrait() + + _default_value = [] + _good_values = [[Foo(), Foo()], []] + _bad_values = [[None], [Foo(), None]] + + +class InstanceListTrait(HasTraits): + + value = List(Instance(__name__+'.Foo')) + +class TestInstanceList(TraitTestBase): + + obj = InstanceListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, Foo) + + _default_value = [] + _good_values = [[Foo(), Foo()], []] + _bad_values = [['1', 2,], '1', [Foo], None] + +class UnionListTrait(HasTraits): + + value = List(Int() | Bool()) + +class TestUnionListTrait(HasTraits): + + obj = UnionListTrait() + + _default_value = [] + _good_values = [[True, 1], [False, True]] + _bad_values = [[1, 'True'], False] + + +class LenListTrait(HasTraits): + + value = List(Int(), [0], minlen=1, maxlen=2) + +class TestLenList(TraitTestBase): + + obj = LenListTrait() + + _default_value = [0] + _good_values = [[1], [1,2], (1,2)] + _bad_values = [10, [1,'a'], 'a', [], list(range(3))] + + def coerce(self, value): + if value is not None: + value = list(value) + return value + +class TupleTrait(HasTraits): + + value = Tuple(Int(allow_none=True), default_value=(1,)) + +class TestTupleTrait(TraitTestBase): + + obj = TupleTrait() + + _default_value = (1,) + _good_values = [(1,), (0,), [1]] + _bad_values = [10, (1, 2), ('a'), (), None] + + def coerce(self, value): + if value is not None: + value = tuple(value) + return value + + def test_invalid_args(self): + self.assertRaises(TypeError, Tuple, 5) + self.assertRaises(TypeError, Tuple, default_value='hello') + t = Tuple(Int(), CBytes(), default_value=(1,5)) + +class LooseTupleTrait(HasTraits): + + value = Tuple((1,2,3)) + +class TestLooseTupleTrait(TraitTestBase): + + obj = LooseTupleTrait() + + _default_value = (1,2,3) + _good_values = [(1,), [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] + _bad_values = [10, 'hello', {}, None] + + def coerce(self, value): + if value is not None: + value = tuple(value) + return value + + def test_invalid_args(self): + self.assertRaises(TypeError, Tuple, 5) + self.assertRaises(TypeError, Tuple, default_value='hello') + t = Tuple(Int(), CBytes(), default_value=(1,5)) + + +class MultiTupleTrait(HasTraits): + + value = Tuple(Int(), Bytes(), default_value=[99,b'bottles']) + +class TestMultiTuple(TraitTestBase): + + obj = MultiTupleTrait() + + _default_value = (99,b'bottles') + _good_values = [(1,b'a'), (2,b'b')] + _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a')) + +class CRegExpTrait(HasTraits): + + value = CRegExp(r'') + +class TestCRegExp(TraitTestBase): + + def coerce(self, value): + return re.compile(value) + + obj = CRegExpTrait() + + _default_value = re.compile(r'') + _good_values = [r'\d+', re.compile(r'\d+')] + _bad_values = ['(', None, ()] + +class DictTrait(HasTraits): + value = Dict() + +def test_dict_assignment(): + d = dict() + c = DictTrait() + c.value = d + d['a'] = 5 + assert d == c.value + assert c.value is d + + +class UniformlyValidatedDictTrait(HasTraits): + + value = Dict(trait=Unicode(), + default_value={'foo': '1'}) + + +class TestInstanceUniformlyValidatedDict(TraitTestBase): + + obj = UniformlyValidatedDictTrait() + + _default_value = {'foo': '1'} + _good_values = [{'foo': '0', 'bar': '1'}] + _bad_values = [{'foo': 0, 'bar': '1'}] + + +class KeyValidatedDictTrait(HasTraits): + + value = Dict(traits={'foo': Int()}, + default_value={'foo': 1}) + + +class TestInstanceKeyValidatedDict(TraitTestBase): + + obj = KeyValidatedDictTrait() + + _default_value = {'foo': 1} + _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 0, 'bar': 1}] + _bad_values = [{'foo': '0', 'bar': '1'}] + + +class FullyValidatedDictTrait(HasTraits): + + value = Dict(trait=Unicode(), + traits={'foo': Int()}, + default_value={'foo': 1}) + + +class TestInstanceFullyValidatedDict(TraitTestBase): + + obj = FullyValidatedDictTrait() + + _default_value = {'foo': 1} + _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 1, 'bar': '2'}] + _bad_values = [{'foo': 0, 'bar': 1}, {'foo': '0', 'bar': '1'}] + + +def test_dict_default_value(): + """Check that the `{}` default value of the Dict traitlet constructor is + actually copied.""" + + class Foo(HasTraits): + d1 = Dict() + d2 = Dict() + + foo = Foo() + assert foo.d1 == {} + assert foo.d2 == {} + assert foo.d1 is not foo.d2 + + +class TestValidationHook(TestCase): + + def test_parity_trait(self): + """Verify that the early validation hook is effective""" + + class Parity(HasTraits): + + value = Int(0) + parity = Enum(['odd', 'even'], default_value='even') + + @validate('value') + def _value_validate(self, proposal): + value = proposal['value'] + if self.parity == 'even' and value % 2: + raise TraitError('Expected an even number') + if self.parity == 'odd' and (value % 2 == 0): + raise TraitError('Expected an odd number') + return value + + u = Parity() + u.parity = 'odd' + u.value = 1 # OK + with self.assertRaises(TraitError): + u.value = 2 # Trait Error + + u.parity = 'even' + u.value = 2 # OK + + def test_multiple_validate(self): + """Verify that we can register the same validator to multiple names""" + + class OddEven(HasTraits): + + odd = Int(1) + even = Int(0) + + @validate('odd', 'even') + def check_valid(self, proposal): + if proposal['trait'].name == 'odd' and not proposal['value'] % 2: + raise TraitError('odd should be odd') + if proposal['trait'].name == 'even' and proposal['value'] % 2: + raise TraitError('even should be even') + + u = OddEven() + u.odd = 3 # OK + with self.assertRaises(TraitError): + u.odd = 2 # Trait Error + + u.even = 2 # OK + with self.assertRaises(TraitError): + u.even = 3 # Trait Error + + + +class TestLink(TestCase): + + def test_connect_same(self): + """Verify two traitlets of the same type can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = link((a, 'value'), (b, 'value')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.value) + + # Change one of the values to make sure they stay in sync. + a.value = 5 + self.assertEqual(a.value, b.value) + b.value = 6 + self.assertEqual(a.value, b.value) + + def test_link_different(self): + """Verify two traitlets of different types can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Conenct the two classes. + c = link((a, 'value'), (b, 'count')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.count) + + # Change one of the values to make sure they stay in sync. + a.value = 5 + self.assertEqual(a.value, b.count) + b.count = 4 + self.assertEqual(a.value, b.count) + + def test_unlink(self): + """Verify two linked traitlets can be unlinked.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Connect the two classes. + c = link((a, 'value'), (b, 'value')) + a.value = 4 + c.unlink() + + # Change one of the values to make sure they don't stay in sync. + a.value = 5 + self.assertNotEqual(a.value, b.value) + + def test_callbacks(self): + """Verify two linked traitlets have their callbacks called once.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Register callbacks that count. + callback_count = [] + def a_callback(name, old, new): + callback_count.append('a') + a.on_trait_change(a_callback, 'value') + def b_callback(name, old, new): + callback_count.append('b') + b.on_trait_change(b_callback, 'count') + + # Connect the two classes. + c = link((a, 'value'), (b, 'count')) + + # Make sure b's count was set to a's value once. + self.assertEqual(''.join(callback_count), 'b') + del callback_count[:] + + # Make sure a's value was set to b's count once. + b.count = 5 + self.assertEqual(''.join(callback_count), 'ba') + del callback_count[:] + + # Make sure b's count was set to a's value once. + a.value = 4 + self.assertEqual(''.join(callback_count), 'ab') + del callback_count[:] + +class TestDirectionalLink(TestCase): + def test_connect_same(self): + """Verify two traitlets of the same type can be linked together using directional_link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'value')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.value) + + # Change one the value of the source and check that it synchronizes the target. + a.value = 5 + self.assertEqual(b.value, 5) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + + def test_tranform(self): + """Test transform link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'value'), lambda x: 2 * x) + + # Make sure the values are correct at the point of linking. + self.assertEqual(b.value, 2 * a.value) + + # Change one the value of the source and check that it modifies the target. + a.value = 5 + self.assertEqual(b.value, 10) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + + def test_link_different(self): + """Verify two traitlets of different types can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'count')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.count) + + # Change one the value of the source and check that it synchronizes the target. + a.value = 5 + self.assertEqual(b.count, 5) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + + def test_unlink(self): + """Verify two linked traitlets can be unlinked.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Connect the two classes. + c = directional_link((a, 'value'), (b, 'value')) + a.value = 4 + c.unlink() + + # Change one of the values to make sure they don't stay in sync. + a.value = 5 + self.assertNotEqual(a.value, b.value) + +class Pickleable(HasTraits): + + i = Int() + @observe('i') + def _i_changed(self, change): pass + @validate('i') + def _i_validate(self, commit): + return commit['value'] + + j = Int() + + def __init__(self): + with self.hold_trait_notifications(): + self.i = 1 + self.on_trait_change(self._i_changed, 'i') + +def test_pickle_hastraits(): + c = Pickleable() + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(c, protocol) + c2 = pickle.loads(p) + assert c2.i == c.i + assert c2.j == c.j + + c.i = 5 + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(c, protocol) + c2 = pickle.loads(p) + assert c2.i == c.i + assert c2.j == c.j + + +def test_hold_trait_notifications(): + changes = [] + + class Test(HasTraits): + a = Integer(0) + b = Integer(0) + + def _a_changed(self, name, old, new): + changes.append((old, new)) + + def _b_validate(self, value, trait): + if value != 0: + raise TraitError('Only 0 is a valid value') + return value + + # Test context manager and nesting + t = Test() + with t.hold_trait_notifications(): + with t.hold_trait_notifications(): + t.a = 1 + assert t.a == 1 + assert changes == [] + t.a = 2 + assert t.a == 2 + with t.hold_trait_notifications(): + t.a = 3 + assert t.a == 3 + assert changes == [] + t.a = 4 + assert t.a == 4 + assert changes == [] + t.a = 4 + assert t.a == 4 + assert changes == [] + + assert changes == [(0, 4)] + # Test roll-back + try: + with t.hold_trait_notifications(): + t.b = 1 # raises a Trait error + except: + pass + assert t.b == 0 + + +class RollBack(HasTraits): + bar = Int() + def _bar_validate(self, value, trait): + if value: + raise TraitError('foobar') + return value + + +class TestRollback(TestCase): + + def test_roll_back(self): + + def assign_rollback(): + RollBack(bar=1) + + self.assertRaises(TraitError, assign_rollback) + + +class CacheModification(HasTraits): + foo = Int() + bar = Int() + + def _bar_validate(self, value, trait): + self.foo = value + return value + + def _foo_validate(self, value, trait): + self.bar = value + return value + + +def test_cache_modification(): + CacheModification(foo=1) + CacheModification(bar=1) + + +class OrderTraits(HasTraits): + notified = Dict() + + a = Unicode() + b = Unicode() + c = Unicode() + d = Unicode() + e = Unicode() + f = Unicode() + g = Unicode() + h = Unicode() + i = Unicode() + j = Unicode() + k = Unicode() + l = Unicode() + + def _notify(self, name, old, new): + """check the value of all traits when each trait change is triggered + + This verifies that the values are not sensitive + to dict ordering when loaded from kwargs + """ + # check the value of the other traits + # when a given trait change notification fires + self.notified[name] = { + c: getattr(self, c) for c in 'abcdefghijkl' + } + + def __init__(self, **kwargs): + self.on_trait_change(self._notify) + super(OrderTraits, self).__init__(**kwargs) + +def test_notification_order(): + d = {c:c for c in 'abcdefghijkl'} + obj = OrderTraits() + assert obj.notified == {} + obj = OrderTraits(**d) + notifications = { + c: d for c in 'abcdefghijkl' + } + assert obj.notified == notifications + + + +### +# Traits for Forward Declaration Tests +### +class ForwardDeclaredInstanceTrait(HasTraits): + + value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True) + +class ForwardDeclaredTypeTrait(HasTraits): + + value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True) + +class ForwardDeclaredInstanceListTrait(HasTraits): + + value = List(ForwardDeclaredInstance('ForwardDeclaredBar')) + +class ForwardDeclaredTypeListTrait(HasTraits): + + value = List(ForwardDeclaredType('ForwardDeclaredBar')) +### +# End Traits for Forward Declaration Tests +### + +### +# Classes for Forward Declaration Tests +### +class ForwardDeclaredBar(object): + pass + +class ForwardDeclaredBarSub(ForwardDeclaredBar): + pass +### +# End Classes for Forward Declaration Tests +### + +### +# Forward Declaration Tests +### +class TestForwardDeclaredInstanceTrait(TraitTestBase): + + obj = ForwardDeclaredInstanceTrait() + _default_value = None + _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()] + _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub] + +class TestForwardDeclaredTypeTrait(TraitTestBase): + + obj = ForwardDeclaredTypeTrait() + _default_value = None + _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub] + _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()] + +class TestForwardDeclaredInstanceList(TraitTestBase): + + obj = ForwardDeclaredInstanceListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) + + _default_value = [] + _good_values = [ + [ForwardDeclaredBar(), ForwardDeclaredBarSub()], + [], + ] + _bad_values = [ + ForwardDeclaredBar(), + [ForwardDeclaredBar(), 3, None], + '1', + # Note that this is the type, not an instance. + [ForwardDeclaredBar], + [None], + None, + ] + +class TestForwardDeclaredTypeList(TraitTestBase): + + obj = ForwardDeclaredTypeListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) + + _default_value = [] + _good_values = [ + [ForwardDeclaredBar, ForwardDeclaredBarSub], + [], + ] + _bad_values = [ + ForwardDeclaredBar, + [ForwardDeclaredBar, 3], + '1', + # Note that this is an instance, not the type. + [ForwardDeclaredBar()], + [None], + None, + ] +### +# End Forward Declaration Tests +### + +class TestDynamicTraits(TestCase): + + def setUp(self): + self._notify1 = [] + + def notify1(self, name, old, new): + self._notify1.append((name, old, new)) + + def test_notify_all(self): + + class A(HasTraits): + pass + + a = A() + self.assertTrue(not hasattr(a, 'x')) + self.assertTrue(not hasattr(a, 'y')) + + # Dynamically add trait x. + a.add_traits(x=Int()) + self.assertTrue(hasattr(a, 'x')) + self.assertTrue(isinstance(a, (A, ))) + + # Dynamically add trait y. + a.add_traits(y=Float()) + self.assertTrue(hasattr(a, 'y')) + self.assertTrue(isinstance(a, (A, ))) + self.assertEqual(a.__class__.__name__, A.__name__) + + # Create a new instance and verify that x and y + # aren't defined. + b = A() + self.assertTrue(not hasattr(b, 'x')) + self.assertTrue(not hasattr(b, 'y')) + + # Verify that notification works like normal. + a.on_trait_change(self.notify1) + a.x = 0 + self.assertEqual(len(self._notify1), 0) + a.y = 0.0 + self.assertEqual(len(self._notify1), 0) + a.x = 10 + self.assertTrue(('x', 0, 10) in self._notify1) + a.y = 10.0 + self.assertTrue(('y', 0.0, 10.0) in self._notify1) + self.assertRaises(TraitError, setattr, a, 'x', 'bad string') + self.assertRaises(TraitError, setattr, a, 'y', 'bad string') + self._notify1 = [] + a.on_trait_change(self.notify1, remove=True) + a.x = 20 + a.y = 20.0 + self.assertEqual(len(self._notify1), 0) + + +def test_enum_no_default(): + class C(HasTraits): + t = Enum(['a', 'b']) + + c = C() + c.t = 'a' + assert c.t == 'a' + + c = C() + + with pytest.raises(TraitError): + t = c.t + + c = C(t='b') + assert c.t == 'b' + + +def test_default_value_repr(): + class C(HasTraits): + t = Type('traitlets.HasTraits') + t2 = Type(HasTraits) + n = Integer(0) + lis = List() + d = Dict() + + assert C.t.default_value_repr() == "'traitlets.HasTraits'" + assert C.t2.default_value_repr() == "'traitlets.traitlets.HasTraits'" + assert C.n.default_value_repr() == '0' + assert C.lis.default_value_repr() == '[]' + assert C.d.default_value_repr() == '{}' + + +class TransitionalClass(HasTraits): + + d = Any() + @default('d') + def _d_default(self): + return TransitionalClass + + parent_super = False + calls_super = Integer(0) + + @default('calls_super') + def _calls_super_default(self): + return -1 + + @observe('calls_super') + @observe_compat + def _calls_super_changed(self, change): + self.parent_super = change + + parent_override = False + overrides = Integer(0) + + @observe('overrides') + @observe_compat + def _overrides_changed(self, change): + self.parent_override = change + + +class SubClass(TransitionalClass): + def _d_default(self): + return SubClass + + subclass_super = False + def _calls_super_changed(self, name, old, new): + self.subclass_super = True + super(SubClass, self)._calls_super_changed(name, old, new) + + subclass_override = False + def _overrides_changed(self, name, old, new): + self.subclass_override = True + + +def test_subclass_compat(): + obj = SubClass() + obj.calls_super = 5 + assert obj.parent_super + assert obj.subclass_super + obj.overrides = 5 + assert obj.subclass_override + assert not obj.parent_override + assert obj.d is SubClass + + +class DefinesHandler(HasTraits): + parent_called = False + + trait = Integer() + @observe('trait') + def handler(self, change): + self.parent_called = True + + +class OverridesHandler(DefinesHandler): + child_called = False + + @observe('trait') + def handler(self, change): + self.child_called = True + + +def test_subclass_override_observer(): + obj = OverridesHandler() + obj.trait = 5 + assert obj.child_called + assert not obj.parent_called + + +class DoesntRegisterHandler(DefinesHandler): + child_called = False + + def handler(self, change): + self.child_called = True + + +def test_subclass_override_not_registered(): + """Subclass that overrides observer and doesn't re-register unregisters both""" + obj = DoesntRegisterHandler() + obj.trait = 5 + assert not obj.child_called + assert not obj.parent_called + + +class AddsHandler(DefinesHandler): + child_called = False + + @observe('trait') + def child_handler(self, change): + self.child_called = True + +def test_subclass_add_observer(): + obj = AddsHandler() + obj.trait = 5 + assert obj.child_called + assert obj.parent_called + + +def test_observe_iterables(): + + class C(HasTraits): + i = Integer() + s = Unicode() + + c = C() + recorded = {} + def record(change): + recorded['change'] = change + + # observe with names=set + c.observe(record, names={'i', 's'}) + c.i = 5 + assert recorded['change'].name == 'i' + assert recorded['change'].new == 5 + c.s = 'hi' + assert recorded['change'].name == 's' + assert recorded['change'].new == 'hi' + + # observe with names=custom container with iter, contains + class MyContainer(object): + def __init__(self, container): + self.container = container + + def __iter__(self): + return iter(self.container) + + def __contains__(self, key): + return key in self.container + + c.observe(record, names=MyContainer({'i', 's'})) + c.i = 10 + assert recorded['change'].name == 'i' + assert recorded['change'].new == 10 + c.s = 'ok' + assert recorded['change'].name == 's' + assert recorded['change'].new == 'ok' + + +def test_super_args(): + class SuperRecorder(object): + def __init__(self, *args, **kwargs): + self.super_args = args + self.super_kwargs = kwargs + + class SuperHasTraits(HasTraits, SuperRecorder): + i = Integer() + + obj = SuperHasTraits('a1', 'a2', b=10, i=5, c='x') + assert obj.i == 5 + assert not hasattr(obj, 'b') + assert not hasattr(obj, 'c') + assert obj.super_args == ('a1' , 'a2') + assert obj.super_kwargs == {'b': 10 , 'c': 'x'} + +def test_super_bad_args(): + class SuperHasTraits(HasTraits): + a = Integer() + + if sys.version_info < (3,): + # Legacy Python, object.__init__ warns itself, instead of raising + w = ['object.__init__'] + else: + w = ["Passing unrecoginized arguments"] + with expected_warnings(w): + obj = SuperHasTraits(a=1, b=2) + assert obj.a == 1 + assert not hasattr(obj, 'b') diff --git a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py index 234466ac99..82259ae6c5 100644 --- a/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py +++ b/contrib/python/traitlets/py2/traitlets/tests/test_traitlets_enum.py @@ -1,181 +1,181 @@ -# -*- coding: UTF-8 -*- -# pylint: disable=missing-docstring, too-few-public-methods -""" -Test the trait-type ``UseEnum``. -""" - -import unittest -import enum -from ipython_genutils.py3compat import string_types -from traitlets import HasTraits, TraitError, UseEnum - - -# ----------------------------------------------------------------------------- -# TEST SUPPORT: -# ----------------------------------------------------------------------------- -class Color(enum.Enum): - red = 1 - green = 2 - blue = 3 - yellow = 4 - -class OtherColor(enum.Enum): - red = 0 - green = 1 - - -# ----------------------------------------------------------------------------- -# TESTSUITE: -# ----------------------------------------------------------------------------- -class TestUseEnum(unittest.TestCase): - # pylint: disable=invalid-name - - class Example(HasTraits): - color = UseEnum(Color, help="Color enum") - - def test_assign_enum_value(self): - example = self.Example() - example.color = Color.green - self.assertEqual(example.color, Color.green) - - def test_assign_all_enum_values(self): - # pylint: disable=no-member - enum_values = [value for value in Color.__members__.values()] - for value in enum_values: - self.assertIsInstance(value, Color) - example = self.Example() - example.color = value - self.assertEqual(example.color, value) - self.assertIsInstance(value, Color) - - def test_assign_enum_value__with_other_enum_raises_error(self): - example = self.Example() - with self.assertRaises(TraitError): - example.color = OtherColor.green - - def test_assign_enum_name_1(self): - # -- CONVERT: string => Enum value (item) - example = self.Example() - example.color = "red" - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - # pylint: disable=no-member - enum_names = [enum_val.name for enum_val in Color.__members__.values()] - for value in enum_names: - self.assertIsInstance(value, string_types) - example = self.Example() - enum_value = Color.__members__.get(value) - example.color = value - self.assertIs(example.color, enum_value) - self.assertEqual(example.color.name, value) - - def test_assign_scoped_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - scoped_names = ["Color.red", "Color.green", "Color.blue", "Color.yellow"] - for value in scoped_names: - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) - self.assertEqual(str(example.color), value) - - def test_assign_bad_enum_value_name__raises_error(self): - # -- CONVERT: string => Enum value (item) - bad_enum_names = ["UNKNOWN_COLOR", "RED", "Green", "blue2"] - for value in bad_enum_names: - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_assign_enum_value_number_1(self): - # -- CONVERT: number => Enum value (item) - example = self.Example() - example.color = 1 # == Color.red.value - example.color = Color.red.value - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_number(self): - # -- CONVERT: number => Enum value (item) - # pylint: disable=no-member - enum_numbers = [enum_val.value - for enum_val in Color.__members__.values()] - for value in enum_numbers: - self.assertIsInstance(value, int) - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) - self.assertEqual(example.color.value, value) - - def test_assign_bad_enum_value_number__raises_error(self): - # -- CONVERT: number => Enum value (item) - bad_numbers = [-1, 0, 5] - for value in bad_numbers: - self.assertIsInstance(value, int) - assert UseEnum(Color).select_by_number(value, None) is None - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_ctor_without_default_value(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color = UseEnum(Color) - - example = Example2() - self.assertEqual(example.color, Color.red) - - def test_ctor_with_default_value_as_enum_value(self): - # -- CONVERT: number => Enum value (item) - class Example2(HasTraits): - color = UseEnum(Color, default_value=Color.green) - - example = Example2() - self.assertEqual(example.color, Color.green) - - - def test_ctor_with_default_value_none_and_not_allow_none(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=False) - color2 = UseEnum(Color, default_value=None) - example = Example2() - self.assertEqual(example.color1, Color.red) - self.assertEqual(example.color2, Color.red) - - def test_ctor_with_default_value_none_and_allow_none(self): - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=True) - color2 = UseEnum(Color, allow_none=True) - - example = Example2() - self.assertIs(example.color1, None) - self.assertIs(example.color2, None) - - def test_assign_none_without_allow_none_resets_to_default_value(self): - class Example2(HasTraits): - color1 = UseEnum(Color, allow_none=False) - color2 = UseEnum(Color) - - example = Example2() - example.color1 = None - example.color2 = None - self.assertIs(example.color1, Color.red) - self.assertIs(example.color2, Color.red) - - def test_assign_none_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - example.color = None - self.assertIs(example.color, None) - - def test_assign_bad_value_with_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - with self.assertRaises(TraitError): - example.color = "BAD_VALUE" - +# -*- coding: UTF-8 -*- +# pylint: disable=missing-docstring, too-few-public-methods +""" +Test the trait-type ``UseEnum``. +""" + +import unittest +import enum +from ipython_genutils.py3compat import string_types +from traitlets import HasTraits, TraitError, UseEnum + + +# ----------------------------------------------------------------------------- +# TEST SUPPORT: +# ----------------------------------------------------------------------------- +class Color(enum.Enum): + red = 1 + green = 2 + blue = 3 + yellow = 4 + +class OtherColor(enum.Enum): + red = 0 + green = 1 + + +# ----------------------------------------------------------------------------- +# TESTSUITE: +# ----------------------------------------------------------------------------- +class TestUseEnum(unittest.TestCase): + # pylint: disable=invalid-name + + class Example(HasTraits): + color = UseEnum(Color, help="Color enum") + + def test_assign_enum_value(self): + example = self.Example() + example.color = Color.green + self.assertEqual(example.color, Color.green) + + def test_assign_all_enum_values(self): + # pylint: disable=no-member + enum_values = [value for value in Color.__members__.values()] + for value in enum_values: + self.assertIsInstance(value, Color) + example = self.Example() + example.color = value + self.assertEqual(example.color, value) + self.assertIsInstance(value, Color) + + def test_assign_enum_value__with_other_enum_raises_error(self): + example = self.Example() + with self.assertRaises(TraitError): + example.color = OtherColor.green + + def test_assign_enum_name_1(self): + # -- CONVERT: string => Enum value (item) + example = self.Example() + example.color = "red" + self.assertEqual(example.color, Color.red) + + def test_assign_enum_value_name(self): + # -- CONVERT: string => Enum value (item) + # pylint: disable=no-member + enum_names = [enum_val.name for enum_val in Color.__members__.values()] + for value in enum_names: + self.assertIsInstance(value, string_types) + example = self.Example() + enum_value = Color.__members__.get(value) + example.color = value + self.assertIs(example.color, enum_value) + self.assertEqual(example.color.name, value) + + def test_assign_scoped_enum_value_name(self): + # -- CONVERT: string => Enum value (item) + scoped_names = ["Color.red", "Color.green", "Color.blue", "Color.yellow"] + for value in scoped_names: + example = self.Example() + example.color = value + self.assertIsInstance(example.color, Color) + self.assertEqual(str(example.color), value) + + def test_assign_bad_enum_value_name__raises_error(self): + # -- CONVERT: string => Enum value (item) + bad_enum_names = ["UNKNOWN_COLOR", "RED", "Green", "blue2"] + for value in bad_enum_names: + example = self.Example() + with self.assertRaises(TraitError): + example.color = value + + def test_assign_enum_value_number_1(self): + # -- CONVERT: number => Enum value (item) + example = self.Example() + example.color = 1 # == Color.red.value + example.color = Color.red.value + self.assertEqual(example.color, Color.red) + + def test_assign_enum_value_number(self): + # -- CONVERT: number => Enum value (item) + # pylint: disable=no-member + enum_numbers = [enum_val.value + for enum_val in Color.__members__.values()] + for value in enum_numbers: + self.assertIsInstance(value, int) + example = self.Example() + example.color = value + self.assertIsInstance(example.color, Color) + self.assertEqual(example.color.value, value) + + def test_assign_bad_enum_value_number__raises_error(self): + # -- CONVERT: number => Enum value (item) + bad_numbers = [-1, 0, 5] + for value in bad_numbers: + self.assertIsInstance(value, int) + assert UseEnum(Color).select_by_number(value, None) is None + example = self.Example() + with self.assertRaises(TraitError): + example.color = value + + def test_ctor_without_default_value(self): + # -- IMPLICIT: default_value = Color.red (first enum-value) + class Example2(HasTraits): + color = UseEnum(Color) + + example = Example2() + self.assertEqual(example.color, Color.red) + + def test_ctor_with_default_value_as_enum_value(self): + # -- CONVERT: number => Enum value (item) + class Example2(HasTraits): + color = UseEnum(Color, default_value=Color.green) + + example = Example2() + self.assertEqual(example.color, Color.green) + + + def test_ctor_with_default_value_none_and_not_allow_none(self): + # -- IMPLICIT: default_value = Color.red (first enum-value) + class Example2(HasTraits): + color1 = UseEnum(Color, default_value=None, allow_none=False) + color2 = UseEnum(Color, default_value=None) + example = Example2() + self.assertEqual(example.color1, Color.red) + self.assertEqual(example.color2, Color.red) + + def test_ctor_with_default_value_none_and_allow_none(self): + class Example2(HasTraits): + color1 = UseEnum(Color, default_value=None, allow_none=True) + color2 = UseEnum(Color, allow_none=True) + + example = Example2() + self.assertIs(example.color1, None) + self.assertIs(example.color2, None) + + def test_assign_none_without_allow_none_resets_to_default_value(self): + class Example2(HasTraits): + color1 = UseEnum(Color, allow_none=False) + color2 = UseEnum(Color) + + example = Example2() + example.color1 = None + example.color2 = None + self.assertIs(example.color1, Color.red) + self.assertIs(example.color2, Color.red) + + def test_assign_none_to_enum_or_none(self): + class Example2(HasTraits): + color = UseEnum(Color, allow_none=True) + + example = Example2() + example.color = None + self.assertIs(example.color, None) + + def test_assign_bad_value_with_to_enum_or_none(self): + class Example2(HasTraits): + color = UseEnum(Color, allow_none=True) + + example = Example2() + with self.assertRaises(TraitError): + example.color = "BAD_VALUE" + diff --git a/contrib/python/traitlets/py2/traitlets/tests/utils.py b/contrib/python/traitlets/py2/traitlets/tests/utils.py index 4efe85358a..88845d8519 100644 --- a/contrib/python/traitlets/py2/traitlets/tests/utils.py +++ b/contrib/python/traitlets/py2/traitlets/tests/utils.py @@ -1,39 +1,39 @@ -import sys - -from subprocess import Popen, PIPE - -def get_output_error_code(cmd): - """Get stdout, stderr, and exit code from running a command""" +import sys + +from subprocess import Popen, PIPE + +def get_output_error_code(cmd): + """Get stdout, stderr, and exit code from running a command""" p = Popen(cmd, stdout=PIPE, stderr=PIPE) - out, err = p.communicate() - out = out.decode('utf8', 'replace') - err = err.decode('utf8', 'replace') - return out, err, p.returncode - - -def check_help_output(pkg, subcommand=None): - """test that `python -m PKG [subcommand] -h` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('-h') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out - assert "--help-all" in out - return out, err - - -def check_help_all_output(pkg, subcommand=None): - """test that `python -m PKG --help-all` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('--help-all') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out - assert "Class parameters" in out - return out, err + out, err = p.communicate() + out = out.decode('utf8', 'replace') + err = err.decode('utf8', 'replace') + return out, err, p.returncode + + +def check_help_output(pkg, subcommand=None): + """test that `python -m PKG [subcommand] -h` works""" + cmd = [sys.executable, '-m', pkg] + if subcommand: + cmd.extend(subcommand) + cmd.append('-h') + out, err, rc = get_output_error_code(cmd) + assert rc == 0, err + assert "Traceback" not in err + assert "Options" in out + assert "--help-all" in out + return out, err + + +def check_help_all_output(pkg, subcommand=None): + """test that `python -m PKG --help-all` works""" + cmd = [sys.executable, '-m', pkg] + if subcommand: + cmd.extend(subcommand) + cmd.append('--help-all') + out, err, rc = get_output_error_code(cmd) + assert rc == 0, err + assert "Traceback" not in err + assert "Options" in out + assert "Class parameters" in out + return out, err diff --git a/contrib/python/traitlets/py2/ya.make b/contrib/python/traitlets/py2/ya.make index ed668b5d34..4a60107101 100644 --- a/contrib/python/traitlets/py2/ya.make +++ b/contrib/python/traitlets/py2/ya.make @@ -44,7 +44,7 @@ RESOURCE_FILES( ) END() - -RECURSE_FOR_TESTS( + +RECURSE_FOR_TESTS( tests -) +) diff --git a/contrib/python/traitlets/py3/traitlets/tests/_warnings.py b/contrib/python/traitlets/py3/traitlets/tests/_warnings.py index eb80d0a936..05e916806f 100644 --- a/contrib/python/traitlets/py3/traitlets/tests/_warnings.py +++ b/contrib/python/traitlets/py3/traitlets/tests/_warnings.py @@ -1,110 +1,110 @@ -# From scikit-image: https://github.com/scikit-image/scikit-image/blob/c2f8c4ab123ebe5f7b827bc495625a32bb225c10/skimage/_shared/_warnings.py -# Licensed under modified BSD license - -__all__ = ['all_warnings', 'expected_warnings'] - +# From scikit-image: https://github.com/scikit-image/scikit-image/blob/c2f8c4ab123ebe5f7b827bc495625a32bb225c10/skimage/_shared/_warnings.py +# Licensed under modified BSD license + +__all__ = ['all_warnings', 'expected_warnings'] + import inspect import os import re -import sys -import warnings +import sys +import warnings from contextlib import contextmanager from unittest import mock - - -@contextmanager -def all_warnings(): - """ - Context for use in testing to ensure that all warnings are raised. - Examples - -------- - >>> import warnings - >>> def foo(): - ... warnings.warn(RuntimeWarning("bar")) - We raise the warning once, while the warning filter is set to "once". - Hereafter, the warning is invisible, even with custom filters: - >>> with warnings.catch_warnings(): - ... warnings.simplefilter('once') - ... foo() - We can now run ``foo()`` without a warning being raised: - >>> from numpy.testing import assert_warns - >>> foo() - To catch the warning, we call in the help of ``all_warnings``: - >>> with all_warnings(): - ... assert_warns(RuntimeWarning, foo) - """ - - # Whenever a warning is triggered, Python adds a __warningregistry__ - # member to the *calling* module. The exercize here is to find - # and eradicate all those breadcrumbs that were left lying around. - # - # We proceed by first searching all parent calling frames and explicitly - # clearing their warning registries (necessary for the doctests above to - # pass). Then, we search for all submodules of skimage and clear theirs - # as well (necessary for the skimage test suite to pass). - - frame = inspect.currentframe() - if frame: - for f in inspect.getouterframes(frame): - f[0].f_locals['__warningregistry__'] = {} - del frame - - for mod_name, mod in list(sys.modules.items()): - try: - mod.__warningregistry__.clear() - except AttributeError: - pass - + + +@contextmanager +def all_warnings(): + """ + Context for use in testing to ensure that all warnings are raised. + Examples + -------- + >>> import warnings + >>> def foo(): + ... warnings.warn(RuntimeWarning("bar")) + We raise the warning once, while the warning filter is set to "once". + Hereafter, the warning is invisible, even with custom filters: + >>> with warnings.catch_warnings(): + ... warnings.simplefilter('once') + ... foo() + We can now run ``foo()`` without a warning being raised: + >>> from numpy.testing import assert_warns + >>> foo() + To catch the warning, we call in the help of ``all_warnings``: + >>> with all_warnings(): + ... assert_warns(RuntimeWarning, foo) + """ + + # Whenever a warning is triggered, Python adds a __warningregistry__ + # member to the *calling* module. The exercize here is to find + # and eradicate all those breadcrumbs that were left lying around. + # + # We proceed by first searching all parent calling frames and explicitly + # clearing their warning registries (necessary for the doctests above to + # pass). Then, we search for all submodules of skimage and clear theirs + # as well (necessary for the skimage test suite to pass). + + frame = inspect.currentframe() + if frame: + for f in inspect.getouterframes(frame): + f[0].f_locals['__warningregistry__'] = {} + del frame + + for mod_name, mod in list(sys.modules.items()): + try: + mod.__warningregistry__.clear() + except AttributeError: + pass + with warnings.catch_warnings(record=True) as w, \ mock.patch.dict(os.environ, {'TRAITLETS_ALL_DEPRECATIONS': '1'}): - warnings.simplefilter("always") - yield w - - -@contextmanager -def expected_warnings(matching): + warnings.simplefilter("always") + yield w + + +@contextmanager +def expected_warnings(matching): r"""Context for use in testing to catch known warnings matching regexes - Parameters - ---------- - matching : list of strings or compiled regexes - Regexes for the desired warning to catch + Parameters + ---------- + matching : list of strings or compiled regexes + Regexes for the desired warning to catch - Examples - -------- - >>> from skimage import data, img_as_ubyte, img_as_float + Examples + -------- + >>> from skimage import data, img_as_ubyte, img_as_float >>> with expected_warnings(["precision loss"]): - ... d = img_as_ubyte(img_as_float(data.coins())) + ... d = img_as_ubyte(img_as_float(data.coins())) - Notes - ----- - Uses `all_warnings` to ensure all warnings are raised. - Upon exiting, it checks the recorded warnings for the desired matching - pattern(s). - Raises a ValueError if any match was not found or an unexpected - warning was raised. - Allows for three types of behaviors: "and", "or", and "optional" matches. - This is done to accomodate different build enviroments or loop conditions - that may produce different warnings. The behaviors can be combined. - If you pass multiple patterns, you get an orderless "and", where all of the - warnings must be raised. - If you use the "|" operator in a pattern, you can catch one of several warnings. - Finally, you can use "|\A\Z" in a pattern to signify it as optional. - """ - with all_warnings() as w: - # enter context - yield w - # exited user context, check the recorded warnings + Notes + ----- + Uses `all_warnings` to ensure all warnings are raised. + Upon exiting, it checks the recorded warnings for the desired matching + pattern(s). + Raises a ValueError if any match was not found or an unexpected + warning was raised. + Allows for three types of behaviors: "and", "or", and "optional" matches. + This is done to accomodate different build enviroments or loop conditions + that may produce different warnings. The behaviors can be combined. + If you pass multiple patterns, you get an orderless "and", where all of the + warnings must be raised. + If you use the "|" operator in a pattern, you can catch one of several warnings. + Finally, you can use "|\A\Z" in a pattern to signify it as optional. + """ + with all_warnings() as w: + # enter context + yield w + # exited user context, check the recorded warnings remaining = [m for m in matching if not r'\A\Z' in m.split('|')] - for warn in w: - found = False - for match in matching: - if re.search(match, str(warn.message)) is not None: - found = True - if match in remaining: - remaining.remove(match) - if not found: - raise ValueError('Unexpected warning: %s' % str(warn.message)) - if len(remaining) > 0: - msg = 'No warning raised matching:\n%s' % '\n'.join(remaining) - raise ValueError(msg) + for warn in w: + found = False + for match in matching: + if re.search(match, str(warn.message)) is not None: + found = True + if match in remaining: + remaining.remove(match) + if not found: + raise ValueError('Unexpected warning: %s' % str(warn.message)) + if len(remaining) > 0: + msg = 'No warning raised matching:\n%s' % '\n'.join(remaining) + raise ValueError(msg) diff --git a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py b/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py index ece95f6108..e42dbc01d0 100644 --- a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py +++ b/contrib/python/traitlets/py3/traitlets/tests/test_traitlets.py @@ -1,20 +1,20 @@ -# encoding: utf-8 -"""Tests for traitlets.traitlets.""" - -# Copyright (c) IPython Development Team. -# Distributed under the terms of the Modified BSD License. +# encoding: utf-8 +"""Tests for traitlets.traitlets.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. # -# Adapted from enthought.traits, Copyright (c) Enthought, Inc., -# also under the terms of the Modified BSD License. - -import pickle -import re +# Adapted from enthought.traits, Copyright (c) Enthought, Inc., +# also under the terms of the Modified BSD License. + +import pickle +import re from unittest import TestCase - -import pytest - + +import pytest + from traitlets.tests._warnings import expected_warnings -from traitlets import ( +from traitlets import ( HasTraits, MetaHasTraits, TraitType, @@ -60,121 +60,121 @@ from traitlets import ( BaseDescriptor, HasDescriptors, CUnicode, -) +) from traitlets.utils import cast_unicode - - -def change_dict(*ordered_values): - change_names = ('name', 'old', 'new', 'owner', 'type') - return dict(zip(change_names, ordered_values)) - -#----------------------------------------------------------------------------- -# Helper classes for testing -#----------------------------------------------------------------------------- - - -class HasTraitsStub(HasTraits): - - def notify_change(self, change): - self._notify_name = change['name'] - self._notify_old = change['old'] - self._notify_new = change['new'] - self._notify_type = change['type'] - - -#----------------------------------------------------------------------------- -# Test classes -#----------------------------------------------------------------------------- - - -class TestTraitType(TestCase): - - def test_get_undefined(self): - class A(HasTraits): - a = TraitType - a = A() + + +def change_dict(*ordered_values): + change_names = ('name', 'old', 'new', 'owner', 'type') + return dict(zip(change_names, ordered_values)) + +#----------------------------------------------------------------------------- +# Helper classes for testing +#----------------------------------------------------------------------------- + + +class HasTraitsStub(HasTraits): + + def notify_change(self, change): + self._notify_name = change['name'] + self._notify_old = change['old'] + self._notify_new = change['new'] + self._notify_type = change['type'] + + +#----------------------------------------------------------------------------- +# Test classes +#----------------------------------------------------------------------------- + + +class TestTraitType(TestCase): + + def test_get_undefined(self): + class A(HasTraits): + a = TraitType + a = A() assert a.a is Undefined - - def test_set(self): - class A(HasTraitsStub): - a = TraitType - - a = A() - a.a = 10 - self.assertEqual(a.a, 10) - self.assertEqual(a._notify_name, 'a') - self.assertEqual(a._notify_old, Undefined) - self.assertEqual(a._notify_new, 10) - - def test_validate(self): - class MyTT(TraitType): - def validate(self, inst, value): - return -1 - class A(HasTraitsStub): - tt = MyTT - - a = A() - a.tt = 10 - self.assertEqual(a.tt, -1) - - def test_default_validate(self): - class MyIntTT(TraitType): - def validate(self, obj, value): - if isinstance(value, int): - return value - self.error(obj, value) - class A(HasTraits): - tt = MyIntTT(10) - a = A() - self.assertEqual(a.tt, 10) - - # Defaults are validated when the HasTraits is instantiated - class B(HasTraits): - tt = MyIntTT('bad default') + + def test_set(self): + class A(HasTraitsStub): + a = TraitType + + a = A() + a.a = 10 + self.assertEqual(a.a, 10) + self.assertEqual(a._notify_name, 'a') + self.assertEqual(a._notify_old, Undefined) + self.assertEqual(a._notify_new, 10) + + def test_validate(self): + class MyTT(TraitType): + def validate(self, inst, value): + return -1 + class A(HasTraitsStub): + tt = MyTT + + a = A() + a.tt = 10 + self.assertEqual(a.tt, -1) + + def test_default_validate(self): + class MyIntTT(TraitType): + def validate(self, obj, value): + if isinstance(value, int): + return value + self.error(obj, value) + class A(HasTraits): + tt = MyIntTT(10) + a = A() + self.assertEqual(a.tt, 10) + + # Defaults are validated when the HasTraits is instantiated + class B(HasTraits): + tt = MyIntTT('bad default') self.assertRaises(TraitError, getattr, B(), 'tt') - - def test_info(self): - class A(HasTraits): - tt = TraitType - a = A() - self.assertEqual(A.tt.info(), 'any value') - - def test_error(self): - class A(HasTraits): + + def test_info(self): + class A(HasTraits): + tt = TraitType + a = A() + self.assertEqual(A.tt.info(), 'any value') + + def test_error(self): + class A(HasTraits): tt = TraitType() - a = A() - self.assertRaises(TraitError, A.tt.error, a, 10) - - def test_deprecated_dynamic_initializer(self): - class A(HasTraits): - x = Int(10) - def _x_default(self): - return 11 - class B(A): - x = Int(20) - class C(A): - def _x_default(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - + a = A() + self.assertRaises(TraitError, A.tt.error, a, 10) + + def test_deprecated_dynamic_initializer(self): + class A(HasTraits): + x = Int(10) + def _x_default(self): + return 11 + class B(A): + x = Int(20) + class C(A): + def _x_default(self): + return 21 + + a = A() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + b = B() + self.assertEqual(b.x, 20) + self.assertEqual(b._trait_values, {'x': 20}) + c = C() + self.assertEqual(c._trait_values, {}) + self.assertEqual(c.x, 21) + self.assertEqual(c._trait_values, {'x': 21}) + # Ensure that the base class remains unmolested when the _default + # initializer gets overridden in a subclass. + a = A() + c = C() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + def test_deprecated_method_warnings(self): with expected_warnings([]): @@ -215,79 +215,79 @@ class TestTraitType(TestCase): assert obj.x == 5 - def test_dynamic_initializer(self): - - class A(HasTraits): - x = Int(10) - - @default('x') - def _default_x(self): - return 11 - - class B(A): - x = Int(20) - - class C(A): - - @default('x') - def _default_x(self): - return 21 - - a = A() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - b = B() - self.assertEqual(b.x, 20) - self.assertEqual(b._trait_values, {'x': 20}) - c = C() - self.assertEqual(c._trait_values, {}) - self.assertEqual(c.x, 21) - self.assertEqual(c._trait_values, {'x': 21}) - # Ensure that the base class remains unmolested when the _default - # initializer gets overridden in a subclass. - a = A() - c = C() - self.assertEqual(a._trait_values, {}) - self.assertEqual(a.x, 11) - self.assertEqual(a._trait_values, {'x': 11}) - - def test_tag_metadata(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10).tag(b=3, c=4) - self.assertEqual(a.metadata, {'a': 1, 'b': 3, 'c': 4}) - - def test_metadata_localized_instance(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - b = MyIntTT(10) - a.metadata['c'] = 3 - # make sure that changing a's metadata didn't change b's metadata - self.assertNotIn('c', b.metadata) - - def test_union_metadata(self): - class Foo(HasTraits): - bar = (Int().tag(ta=1) | Dict().tag(ta=2, ti='b')).tag(ti='a') - foo = Foo() - # At this point, no value has been set for bar, so value-specific - # is not set. - self.assertEqual(foo.trait_metadata('bar', 'ta'), None) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - foo.bar = {} - self.assertEqual(foo.trait_metadata('bar', 'ta'), 2) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'b') - foo.bar = 1 - self.assertEqual(foo.trait_metadata('bar', 'ta'), 1) - self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') - - def test_union_default_value(self): - class Foo(HasTraits): - bar = Union([Dict(), Int()], default_value=1) - foo = Foo() - self.assertEqual(foo.bar, 1) - + def test_dynamic_initializer(self): + + class A(HasTraits): + x = Int(10) + + @default('x') + def _default_x(self): + return 11 + + class B(A): + x = Int(20) + + class C(A): + + @default('x') + def _default_x(self): + return 21 + + a = A() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + b = B() + self.assertEqual(b.x, 20) + self.assertEqual(b._trait_values, {'x': 20}) + c = C() + self.assertEqual(c._trait_values, {}) + self.assertEqual(c.x, 21) + self.assertEqual(c._trait_values, {'x': 21}) + # Ensure that the base class remains unmolested when the _default + # initializer gets overridden in a subclass. + a = A() + c = C() + self.assertEqual(a._trait_values, {}) + self.assertEqual(a.x, 11) + self.assertEqual(a._trait_values, {'x': 11}) + + def test_tag_metadata(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10).tag(b=3, c=4) + self.assertEqual(a.metadata, {'a': 1, 'b': 3, 'c': 4}) + + def test_metadata_localized_instance(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10) + b = MyIntTT(10) + a.metadata['c'] = 3 + # make sure that changing a's metadata didn't change b's metadata + self.assertNotIn('c', b.metadata) + + def test_union_metadata(self): + class Foo(HasTraits): + bar = (Int().tag(ta=1) | Dict().tag(ta=2, ti='b')).tag(ti='a') + foo = Foo() + # At this point, no value has been set for bar, so value-specific + # is not set. + self.assertEqual(foo.trait_metadata('bar', 'ta'), None) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') + foo.bar = {} + self.assertEqual(foo.trait_metadata('bar', 'ta'), 2) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'b') + foo.bar = 1 + self.assertEqual(foo.trait_metadata('bar', 'ta'), 1) + self.assertEqual(foo.trait_metadata('bar', 'ti'), 'a') + + def test_union_default_value(self): + class Foo(HasTraits): + bar = Union([Dict(), Int()], default_value=1) + foo = Foo() + self.assertEqual(foo.bar, 1) + def test_union_validation_priority(self): class Foo(HasTraits): bar = Union([CInt(), Unicode()]) @@ -301,506 +301,506 @@ class TestTraitType(TestCase): bar = Union([Dict(), Int()]) self.assertEqual(Foo().bar, {}) - def test_deprecated_metadata_access(self): - class MyIntTT(TraitType): - metadata = {'a': 1, 'b': 2} - a = MyIntTT(10) - with expected_warnings(["use the instance .metadata dictionary directly"]*2): - a.set_metadata('key', 'value') - v = a.get_metadata('key') - self.assertEqual(v, 'value') - with expected_warnings(["use the instance .help string directly"]*2): - a.set_metadata('help', 'some help') - v = a.get_metadata('help') - self.assertEqual(v, 'some help') - - def test_trait_types_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Int - - def test_trait_types_list_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = List(Int) - - def test_trait_types_tuple_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Tuple(Int) - - def test_trait_types_dict_deprecated(self): - with expected_warnings(["Traits should be given as instances"]): - class C(HasTraits): - t = Dict(Int) - -class TestHasDescriptorsMeta(TestCase): - - def test_metaclass(self): - self.assertEqual(type(HasTraits), MetaHasTraits) - - class A(HasTraits): - a = Int() - - a = A() - self.assertEqual(type(a.__class__), MetaHasTraits) - self.assertEqual(a.a,0) - a.a = 10 - self.assertEqual(a.a,10) - - class B(HasTraits): - b = Int() - - b = B() - self.assertEqual(b.b,0) - b.b = 10 - self.assertEqual(b.b,10) - - class C(HasTraits): - c = Int(30) - - c = C() - self.assertEqual(c.c,30) - c.c = 10 - self.assertEqual(c.c,10) - - def test_this_class(self): - class A(HasTraits): - t = This() - tt = This() - class B(A): - tt = This() - ttt = This() - self.assertEqual(A.t.this_class, A) - self.assertEqual(B.t.this_class, A) - self.assertEqual(B.tt.this_class, B) - self.assertEqual(B.ttt.this_class, B) - -class TestHasDescriptors(TestCase): - - def test_setup_instance(self): - - class FooDescriptor(BaseDescriptor): - - def instance_init(self, inst): - foo = inst.foo # instance should have the attr - - class HasFooDescriptors(HasDescriptors): - - fd = FooDescriptor() - - def setup_instance(self, *args, **kwargs): - self.foo = kwargs.get('foo', None) - super(HasFooDescriptors, self).setup_instance(*args, **kwargs) - - hfd = HasFooDescriptors(foo='bar') - -class TestHasTraitsNotify(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def notify2(self, name, old, new): - self._notify2.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - a.b = 10.0 - self.assertTrue(('b',0.0,10.0) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.on_trait_change(self.notify1,remove=True) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.on_trait_change(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.on_trait_change(self.notify1, 'a') - b.on_trait_change(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in self._notify1) - self.assertTrue(('b',0.0,10.0) in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - _notify1 = [] - def _a_changed(self, name, old, new): - self._notify1.append((name, old, new)) - - a = A() - a.a = 0 - # This is broken!!! - self.assertEqual(len(a._notify1),0) - a.a = 10 - self.assertTrue(('a',0,10) in a._notify1) - - class B(A): - b = Float() - _notify2 = [] - def _b_changed(self, name, old, new): - self._notify2.append((name, old, new)) - - b = B() - b.a = 10 - b.b = 10.0 - self.assertTrue(('a',0,10) in b._notify1) - self.assertTrue(('b',0.0,10.0) in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(name): - self.cb = (name,) - def callback2(name, new): - self.cb = (name, new) - def callback3(name, old, new): - self.cb = (name, old, new) - def callback4(name, old, new, obj): - self.cb = (name, old, new, obj) - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.on_trait_change(callback0, 'a', remove=True) - - a.on_trait_change(callback1, 'a') - a.a = 100 - self.assertEqual(self.cb,('a',)) - a.on_trait_change(callback1, 'a', remove=True) - - a.on_trait_change(callback2, 'a') - a.a = 1000 - self.assertEqual(self.cb,('a',1000)) - a.on_trait_change(callback2, 'a', remove=True) - - a.on_trait_change(callback3, 'a') - a.a = 10000 - self.assertEqual(self.cb,('a',1000,10000)) - a.on_trait_change(callback3, 'a', remove=True) - - a.on_trait_change(callback4, 'a') - a.a = 100000 - self.assertEqual(self.cb,('a',10000,100000,a)) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.on_trait_change(callback4, 'a', remove=True) - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.on_trait_change(self.listener1, ['a']) - - def listener1(self, name, old, new): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.on_trait_change(self.listener2) - - def listener2(self, name, old, new): - self.c += 1 - - def _a_changed(self, name, old, new): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - -class TestObserveDecorator(TestCase): - - def setUp(self): - self._notify1 = [] - self._notify2 = [] - - def notify1(self, change): - self._notify1.append(change) - - def notify2(self, change): - self._notify2.append(change) - - def test_notify_all(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1) - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.b = 0.0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - a.b = 10.0 - change = change_dict('b', 0.0, 10.0, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - self.assertRaises(TraitError,setattr,a,'b','bad string') - self._notify1 = [] - a.unobserve(self.notify1) - a.a = 20 - a.b = 20.0 - self.assertEqual(len(self._notify1),0) - - def test_notify_one(self): - - class A(HasTraits): - a = Int() - b = Float() - - a = A() - a.observe(self.notify1, 'a') - a.a = 0 - self.assertEqual(len(self._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in self._notify1) - self.assertRaises(TraitError,setattr,a,'a','bad string') - - def test_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - self.assertEqual(b.a,0) - self.assertEqual(b.b,0.0) - b.a = 100 - b.b = 100.0 - self.assertEqual(b.a,100) - self.assertEqual(b.b,100.0) - - def test_notify_subclass(self): - - class A(HasTraits): - a = Int() - - class B(A): - b = Float() - - b = B() - b.observe(self.notify1, 'a') - b.observe(self.notify2, 'b') - b.a = 0 - b.b = 0.0 - self.assertEqual(len(self._notify1),0) - self.assertEqual(len(self._notify2),0) - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in self._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in self._notify2) - - def test_static_notify(self): - - class A(HasTraits): - a = Int() - b = Int() - _notify1 = [] - _notify_any = [] - - @observe('a') - def _a_changed(self, change): - self._notify1.append(change) - - @observe(All) - def _any_changed(self, change): - self._notify_any.append(change) - - a = A() - a.a = 0 - self.assertEqual(len(a._notify1),0) - a.a = 10 - change = change_dict('a', 0, 10, a, 'change') - self.assertTrue(change in a._notify1) - a.b = 1 - self.assertEqual(len(a._notify_any), 2) - change = change_dict('b', 0, 1, a, 'change') - self.assertTrue(change in a._notify_any) - - class B(A): - b = Float() - _notify2 = [] - @observe('b') - def _b_changed(self, change): - self._notify2.append(change) - - b = B() - b.a = 10 - b.b = 10.0 - change = change_dict('a', 0, 10, b, 'change') - self.assertTrue(change in b._notify1) - change = change_dict('b', 0.0, 10.0, b, 'change') - self.assertTrue(change in b._notify2) - - def test_notify_args(self): - - def callback0(): - self.cb = () - def callback1(change): - self.cb = change - - class A(HasTraits): - a = Int() - - a = A() - a.on_trait_change(callback0, 'a') - a.a = 10 - self.assertEqual(self.cb,()) - a.unobserve(callback0, 'a') - - a.observe(callback1, 'a') - a.a = 100 - change = change_dict('a', 10, 100, a, 'change') - self.assertEqual(self.cb, change) - self.assertEqual(len(a._trait_notifiers['a']['change']), 1) - a.unobserve(callback1, 'a') - - self.assertEqual(len(a._trait_notifiers['a']['change']), 0) - - def test_notify_only_once(self): - - class A(HasTraits): - listen_to = ['a'] - - a = Int(0) - b = 0 - - def __init__(self, **kwargs): - super(A, self).__init__(**kwargs) - self.observe(self.listener1, ['a']) - - def listener1(self, change): - self.b += 1 - - class B(A): - - c = 0 - d = 0 - - def __init__(self, **kwargs): - super(B, self).__init__(**kwargs) - self.observe(self.listener2) - - def listener2(self, change): - self.c += 1 - - @observe('a') - def _a_changed(self, change): - self.d += 1 - - b = B() - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - b.a += 1 - self.assertEqual(b.b, b.c) - self.assertEqual(b.b, b.d) - - -class TestHasTraits(TestCase): - - def test_trait_names(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(sorted(a.trait_names()),['f','i']) - self.assertEqual(sorted(A.class_trait_names()),['f','i']) - self.assertTrue(a.has_trait('f')) - self.assertFalse(a.has_trait('g')) - + def test_deprecated_metadata_access(self): + class MyIntTT(TraitType): + metadata = {'a': 1, 'b': 2} + a = MyIntTT(10) + with expected_warnings(["use the instance .metadata dictionary directly"]*2): + a.set_metadata('key', 'value') + v = a.get_metadata('key') + self.assertEqual(v, 'value') + with expected_warnings(["use the instance .help string directly"]*2): + a.set_metadata('help', 'some help') + v = a.get_metadata('help') + self.assertEqual(v, 'some help') + + def test_trait_types_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Int + + def test_trait_types_list_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = List(Int) + + def test_trait_types_tuple_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Tuple(Int) + + def test_trait_types_dict_deprecated(self): + with expected_warnings(["Traits should be given as instances"]): + class C(HasTraits): + t = Dict(Int) + +class TestHasDescriptorsMeta(TestCase): + + def test_metaclass(self): + self.assertEqual(type(HasTraits), MetaHasTraits) + + class A(HasTraits): + a = Int() + + a = A() + self.assertEqual(type(a.__class__), MetaHasTraits) + self.assertEqual(a.a,0) + a.a = 10 + self.assertEqual(a.a,10) + + class B(HasTraits): + b = Int() + + b = B() + self.assertEqual(b.b,0) + b.b = 10 + self.assertEqual(b.b,10) + + class C(HasTraits): + c = Int(30) + + c = C() + self.assertEqual(c.c,30) + c.c = 10 + self.assertEqual(c.c,10) + + def test_this_class(self): + class A(HasTraits): + t = This() + tt = This() + class B(A): + tt = This() + ttt = This() + self.assertEqual(A.t.this_class, A) + self.assertEqual(B.t.this_class, A) + self.assertEqual(B.tt.this_class, B) + self.assertEqual(B.ttt.this_class, B) + +class TestHasDescriptors(TestCase): + + def test_setup_instance(self): + + class FooDescriptor(BaseDescriptor): + + def instance_init(self, inst): + foo = inst.foo # instance should have the attr + + class HasFooDescriptors(HasDescriptors): + + fd = FooDescriptor() + + def setup_instance(self, *args, **kwargs): + self.foo = kwargs.get('foo', None) + super(HasFooDescriptors, self).setup_instance(*args, **kwargs) + + hfd = HasFooDescriptors(foo='bar') + +class TestHasTraitsNotify(TestCase): + + def setUp(self): + self._notify1 = [] + self._notify2 = [] + + def notify1(self, name, old, new): + self._notify1.append((name, old, new)) + + def notify2(self, name, old, new): + self._notify2.append((name, old, new)) + + def test_notify_all(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.on_trait_change(self.notify1) + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.b = 0.0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in self._notify1) + a.b = 10.0 + self.assertTrue(('b',0.0,10.0) in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + self.assertRaises(TraitError,setattr,a,'b','bad string') + self._notify1 = [] + a.on_trait_change(self.notify1,remove=True) + a.a = 20 + a.b = 20.0 + self.assertEqual(len(self._notify1),0) + + def test_notify_one(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.on_trait_change(self.notify1, 'a') + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + + def test_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + self.assertEqual(b.a,0) + self.assertEqual(b.b,0.0) + b.a = 100 + b.b = 100.0 + self.assertEqual(b.a,100) + self.assertEqual(b.b,100.0) + + def test_notify_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + b.on_trait_change(self.notify1, 'a') + b.on_trait_change(self.notify2, 'b') + b.a = 0 + b.b = 0.0 + self.assertEqual(len(self._notify1),0) + self.assertEqual(len(self._notify2),0) + b.a = 10 + b.b = 10.0 + self.assertTrue(('a',0,10) in self._notify1) + self.assertTrue(('b',0.0,10.0) in self._notify2) + + def test_static_notify(self): + + class A(HasTraits): + a = Int() + _notify1 = [] + def _a_changed(self, name, old, new): + self._notify1.append((name, old, new)) + + a = A() + a.a = 0 + # This is broken!!! + self.assertEqual(len(a._notify1),0) + a.a = 10 + self.assertTrue(('a',0,10) in a._notify1) + + class B(A): + b = Float() + _notify2 = [] + def _b_changed(self, name, old, new): + self._notify2.append((name, old, new)) + + b = B() + b.a = 10 + b.b = 10.0 + self.assertTrue(('a',0,10) in b._notify1) + self.assertTrue(('b',0.0,10.0) in b._notify2) + + def test_notify_args(self): + + def callback0(): + self.cb = () + def callback1(name): + self.cb = (name,) + def callback2(name, new): + self.cb = (name, new) + def callback3(name, old, new): + self.cb = (name, old, new) + def callback4(name, old, new, obj): + self.cb = (name, old, new, obj) + + class A(HasTraits): + a = Int() + + a = A() + a.on_trait_change(callback0, 'a') + a.a = 10 + self.assertEqual(self.cb,()) + a.on_trait_change(callback0, 'a', remove=True) + + a.on_trait_change(callback1, 'a') + a.a = 100 + self.assertEqual(self.cb,('a',)) + a.on_trait_change(callback1, 'a', remove=True) + + a.on_trait_change(callback2, 'a') + a.a = 1000 + self.assertEqual(self.cb,('a',1000)) + a.on_trait_change(callback2, 'a', remove=True) + + a.on_trait_change(callback3, 'a') + a.a = 10000 + self.assertEqual(self.cb,('a',1000,10000)) + a.on_trait_change(callback3, 'a', remove=True) + + a.on_trait_change(callback4, 'a') + a.a = 100000 + self.assertEqual(self.cb,('a',10000,100000,a)) + self.assertEqual(len(a._trait_notifiers['a']['change']), 1) + a.on_trait_change(callback4, 'a', remove=True) + + self.assertEqual(len(a._trait_notifiers['a']['change']), 0) + + def test_notify_only_once(self): + + class A(HasTraits): + listen_to = ['a'] + + a = Int(0) + b = 0 + + def __init__(self, **kwargs): + super(A, self).__init__(**kwargs) + self.on_trait_change(self.listener1, ['a']) + + def listener1(self, name, old, new): + self.b += 1 + + class B(A): + + c = 0 + d = 0 + + def __init__(self, **kwargs): + super(B, self).__init__(**kwargs) + self.on_trait_change(self.listener2) + + def listener2(self, name, old, new): + self.c += 1 + + def _a_changed(self, name, old, new): + self.d += 1 + + b = B() + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + +class TestObserveDecorator(TestCase): + + def setUp(self): + self._notify1 = [] + self._notify2 = [] + + def notify1(self, change): + self._notify1.append(change) + + def notify2(self, change): + self._notify2.append(change) + + def test_notify_all(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.observe(self.notify1) + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.b = 0.0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in self._notify1) + a.b = 10.0 + change = change_dict('b', 0.0, 10.0, a, 'change') + self.assertTrue(change in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + self.assertRaises(TraitError,setattr,a,'b','bad string') + self._notify1 = [] + a.unobserve(self.notify1) + a.a = 20 + a.b = 20.0 + self.assertEqual(len(self._notify1),0) + + def test_notify_one(self): + + class A(HasTraits): + a = Int() + b = Float() + + a = A() + a.observe(self.notify1, 'a') + a.a = 0 + self.assertEqual(len(self._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in self._notify1) + self.assertRaises(TraitError,setattr,a,'a','bad string') + + def test_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + self.assertEqual(b.a,0) + self.assertEqual(b.b,0.0) + b.a = 100 + b.b = 100.0 + self.assertEqual(b.a,100) + self.assertEqual(b.b,100.0) + + def test_notify_subclass(self): + + class A(HasTraits): + a = Int() + + class B(A): + b = Float() + + b = B() + b.observe(self.notify1, 'a') + b.observe(self.notify2, 'b') + b.a = 0 + b.b = 0.0 + self.assertEqual(len(self._notify1),0) + self.assertEqual(len(self._notify2),0) + b.a = 10 + b.b = 10.0 + change = change_dict('a', 0, 10, b, 'change') + self.assertTrue(change in self._notify1) + change = change_dict('b', 0.0, 10.0, b, 'change') + self.assertTrue(change in self._notify2) + + def test_static_notify(self): + + class A(HasTraits): + a = Int() + b = Int() + _notify1 = [] + _notify_any = [] + + @observe('a') + def _a_changed(self, change): + self._notify1.append(change) + + @observe(All) + def _any_changed(self, change): + self._notify_any.append(change) + + a = A() + a.a = 0 + self.assertEqual(len(a._notify1),0) + a.a = 10 + change = change_dict('a', 0, 10, a, 'change') + self.assertTrue(change in a._notify1) + a.b = 1 + self.assertEqual(len(a._notify_any), 2) + change = change_dict('b', 0, 1, a, 'change') + self.assertTrue(change in a._notify_any) + + class B(A): + b = Float() + _notify2 = [] + @observe('b') + def _b_changed(self, change): + self._notify2.append(change) + + b = B() + b.a = 10 + b.b = 10.0 + change = change_dict('a', 0, 10, b, 'change') + self.assertTrue(change in b._notify1) + change = change_dict('b', 0.0, 10.0, b, 'change') + self.assertTrue(change in b._notify2) + + def test_notify_args(self): + + def callback0(): + self.cb = () + def callback1(change): + self.cb = change + + class A(HasTraits): + a = Int() + + a = A() + a.on_trait_change(callback0, 'a') + a.a = 10 + self.assertEqual(self.cb,()) + a.unobserve(callback0, 'a') + + a.observe(callback1, 'a') + a.a = 100 + change = change_dict('a', 10, 100, a, 'change') + self.assertEqual(self.cb, change) + self.assertEqual(len(a._trait_notifiers['a']['change']), 1) + a.unobserve(callback1, 'a') + + self.assertEqual(len(a._trait_notifiers['a']['change']), 0) + + def test_notify_only_once(self): + + class A(HasTraits): + listen_to = ['a'] + + a = Int(0) + b = 0 + + def __init__(self, **kwargs): + super(A, self).__init__(**kwargs) + self.observe(self.listener1, ['a']) + + def listener1(self, change): + self.b += 1 + + class B(A): + + c = 0 + d = 0 + + def __init__(self, **kwargs): + super(B, self).__init__(**kwargs) + self.observe(self.listener2) + + def listener2(self, change): + self.c += 1 + + @observe('a') + def _a_changed(self, change): + self.d += 1 + + b = B() + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + b.a += 1 + self.assertEqual(b.b, b.c) + self.assertEqual(b.b, b.d) + + +class TestHasTraits(TestCase): + + def test_trait_names(self): + class A(HasTraits): + i = Int() + f = Float() + a = A() + self.assertEqual(sorted(a.trait_names()),['f','i']) + self.assertEqual(sorted(A.class_trait_names()),['f','i']) + self.assertTrue(a.has_trait('f')) + self.assertFalse(a.has_trait('g')) + def test_trait_has_value(self): class A(HasTraits): i = Int() @@ -813,438 +813,438 @@ class TestHasTraits(TestCase): self.assertTrue(a.trait_has_value('i')) self.assertTrue(a.trait_has_value('f')) - def test_trait_metadata_deprecated(self): + def test_trait_metadata_deprecated(self): with expected_warnings([r'metadata should be set using the \.tag\(\) method']): - class A(HasTraits): - i = Int(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='MY_VALUE') - a = A() - self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') - - def test_trait_metadata_default(self): - class A(HasTraits): - i = Int() - a = A() - self.assertEqual(a.trait_metadata('i', 'config_key'), None) - self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default') - - def test_traits(self): - class A(HasTraits): - i = Int() - f = Float() - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f)) - self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f)) - - def test_traits_metadata(self): - class A(HasTraits): - i = Int().tag(config_key='VALUE1', other_thing='VALUE2') - f = Float().tag(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - def test_traits_metadata_deprecated(self): + class A(HasTraits): + i = Int(config_key='MY_VALUE') + a = A() + self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') + + def test_trait_metadata(self): + class A(HasTraits): + i = Int().tag(config_key='MY_VALUE') + a = A() + self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE') + + def test_trait_metadata_default(self): + class A(HasTraits): + i = Int() + a = A() + self.assertEqual(a.trait_metadata('i', 'config_key'), None) + self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default') + + def test_traits(self): + class A(HasTraits): + i = Int() + f = Float() + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f)) + self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f)) + + def test_traits_metadata(self): + class A(HasTraits): + i = Int().tag(config_key='VALUE1', other_thing='VALUE2') + f = Float().tag(config_key='VALUE3', other_thing='VALUE2') + j = Int(0) + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) + traits = a.traits(config_key='VALUE1', other_thing='VALUE2') + self.assertEqual(traits, dict(i=A.i)) + + # This passes, but it shouldn't because I am replicating a bug in + # traits. + traits = a.traits(config_key=lambda v: True) + self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) + + def test_traits_metadata_deprecated(self): with expected_warnings([r'metadata should be set using the \.tag\(\) method']*2): - class A(HasTraits): - i = Int(config_key='VALUE1', other_thing='VALUE2') - f = Float(config_key='VALUE3', other_thing='VALUE2') - j = Int(0) - a = A() - self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) - traits = a.traits(config_key='VALUE1', other_thing='VALUE2') - self.assertEqual(traits, dict(i=A.i)) - - # This passes, but it shouldn't because I am replicating a bug in - # traits. - traits = a.traits(config_key=lambda v: True) - self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) - - - def test_init(self): - class A(HasTraits): - i = Int() - x = Float() - a = A(i=1, x=10.0) - self.assertEqual(a.i, 1) - self.assertEqual(a.x, 10.0) - - def test_positional_args(self): - class A(HasTraits): - i = Int(0) - def __init__(self, i): - super(A, self).__init__() - self.i = i - - a = A(5) - self.assertEqual(a.i, 5) - # should raise TypeError if no positional arg given - self.assertRaises(TypeError, A) - -#----------------------------------------------------------------------------- -# Tests for specific trait types -#----------------------------------------------------------------------------- - - -class TestType(TestCase): - - def test_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type(allow_none=True) - - a = A() - self.assertEqual(a.klass, object) - - a.klass = B - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_default_options(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - # Different possible combinations of options for default_value - # and klass. default_value=None is only valid with allow_none=True. - k1 = Type() - k2 = Type(None, allow_none=True) - k3 = Type(B) - k4 = Type(klass=B) - k5 = Type(default_value=None, klass=B, allow_none=True) - k6 = Type(default_value=C, klass=B) - - self.assertIs(A.k1.default_value, object) - self.assertIs(A.k1.klass, object) - self.assertIs(A.k2.default_value, None) - self.assertIs(A.k2.klass, object) - self.assertIs(A.k3.default_value, B) - self.assertIs(A.k3.klass, B) - self.assertIs(A.k4.default_value, B) - self.assertIs(A.k4.klass, B) - self.assertIs(A.k5.default_value, None) - self.assertIs(A.k5.klass, B) - self.assertIs(A.k6.default_value, C) - self.assertIs(A.k6.klass, B) - - a = A() - self.assertIs(a.k1, object) - self.assertIs(a.k2, None) - self.assertIs(a.k3, B) - self.assertIs(a.k4, B) - self.assertIs(a.k5, None) - self.assertIs(a.k6, C) - - def test_value(self): - - class B(object): pass - class C(object): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', C) - self.assertRaises(TraitError, setattr, a, 'klass', object) - a.klass = B - - def test_allow_none(self): - - class B(object): pass - class C(B): pass - class A(HasTraits): - klass = Type(B) - - a = A() - self.assertEqual(a.klass, B) - self.assertRaises(TraitError, setattr, a, 'klass', None) - a.klass = C - self.assertEqual(a.klass, C) - - def test_validate_klass(self): - - class A(HasTraits): - klass = Type('no strings allowed') - - self.assertRaises(ImportError, A) - - class A(HasTraits): - klass = Type('rub.adub.Duck') - - self.assertRaises(ImportError, A) - - def test_validate_default(self): - - class B(object): pass - class A(HasTraits): - klass = Type('bad default', B) - - self.assertRaises(ImportError, A) - - class C(HasTraits): - klass = Type(None, B) - + class A(HasTraits): + i = Int(config_key='VALUE1', other_thing='VALUE2') + f = Float(config_key='VALUE3', other_thing='VALUE2') + j = Int(0) + a = A() + self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j)) + traits = a.traits(config_key='VALUE1', other_thing='VALUE2') + self.assertEqual(traits, dict(i=A.i)) + + # This passes, but it shouldn't because I am replicating a bug in + # traits. + traits = a.traits(config_key=lambda v: True) + self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j)) + + + def test_init(self): + class A(HasTraits): + i = Int() + x = Float() + a = A(i=1, x=10.0) + self.assertEqual(a.i, 1) + self.assertEqual(a.x, 10.0) + + def test_positional_args(self): + class A(HasTraits): + i = Int(0) + def __init__(self, i): + super(A, self).__init__() + self.i = i + + a = A(5) + self.assertEqual(a.i, 5) + # should raise TypeError if no positional arg given + self.assertRaises(TypeError, A) + +#----------------------------------------------------------------------------- +# Tests for specific trait types +#----------------------------------------------------------------------------- + + +class TestType(TestCase): + + def test_default(self): + + class B(object): pass + class A(HasTraits): + klass = Type(allow_none=True) + + a = A() + self.assertEqual(a.klass, object) + + a.klass = B + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', 10) + + def test_default_options(self): + + class B(object): pass + class C(B): pass + class A(HasTraits): + # Different possible combinations of options for default_value + # and klass. default_value=None is only valid with allow_none=True. + k1 = Type() + k2 = Type(None, allow_none=True) + k3 = Type(B) + k4 = Type(klass=B) + k5 = Type(default_value=None, klass=B, allow_none=True) + k6 = Type(default_value=C, klass=B) + + self.assertIs(A.k1.default_value, object) + self.assertIs(A.k1.klass, object) + self.assertIs(A.k2.default_value, None) + self.assertIs(A.k2.klass, object) + self.assertIs(A.k3.default_value, B) + self.assertIs(A.k3.klass, B) + self.assertIs(A.k4.default_value, B) + self.assertIs(A.k4.klass, B) + self.assertIs(A.k5.default_value, None) + self.assertIs(A.k5.klass, B) + self.assertIs(A.k6.default_value, C) + self.assertIs(A.k6.klass, B) + + a = A() + self.assertIs(a.k1, object) + self.assertIs(a.k2, None) + self.assertIs(a.k3, B) + self.assertIs(a.k4, B) + self.assertIs(a.k5, None) + self.assertIs(a.k6, C) + + def test_value(self): + + class B(object): pass + class C(object): pass + class A(HasTraits): + klass = Type(B) + + a = A() + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', C) + self.assertRaises(TraitError, setattr, a, 'klass', object) + a.klass = B + + def test_allow_none(self): + + class B(object): pass + class C(B): pass + class A(HasTraits): + klass = Type(B) + + a = A() + self.assertEqual(a.klass, B) + self.assertRaises(TraitError, setattr, a, 'klass', None) + a.klass = C + self.assertEqual(a.klass, C) + + def test_validate_klass(self): + + class A(HasTraits): + klass = Type('no strings allowed') + + self.assertRaises(ImportError, A) + + class A(HasTraits): + klass = Type('rub.adub.Duck') + + self.assertRaises(ImportError, A) + + def test_validate_default(self): + + class B(object): pass + class A(HasTraits): + klass = Type('bad default', B) + + self.assertRaises(ImportError, A) + + class C(HasTraits): + klass = Type(None, B) + self.assertRaises(TraitError, getattr, C(), 'klass') - - def test_str_klass(self): - - class A(HasTraits): + + def test_str_klass(self): + + class A(HasTraits): klass = Type("traitlets.config.Config") - + from traitlets.config import Config - a = A() + a = A() a.klass = Config self.assertEqual(a.klass, Config) - - self.assertRaises(TraitError, setattr, a, 'klass', 10) - - def test_set_str_klass(self): - - class A(HasTraits): - klass = Type() - + + self.assertRaises(TraitError, setattr, a, 'klass', 10) + + def test_set_str_klass(self): + + class A(HasTraits): + klass = Type() + a = A(klass="traitlets.config.Config") from traitlets.config import Config - + self.assertEqual(a.klass, Config) -class TestInstance(TestCase): - - def test_basic(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class A(HasTraits): - inst = Instance(Foo, allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_default_klass(self): - class Foo(object): pass - class Bar(Foo): pass - class Bah(object): pass - - class FooInstance(Instance): - klass = Foo - - class A(HasTraits): - inst = FooInstance(allow_none=True) - - a = A() - self.assertTrue(a.inst is None) - a.inst = Foo() - self.assertTrue(isinstance(a.inst, Foo)) - a.inst = Bar() - self.assertTrue(isinstance(a.inst, Foo)) - self.assertRaises(TraitError, setattr, a, 'inst', Foo) - self.assertRaises(TraitError, setattr, a, 'inst', Bar) - self.assertRaises(TraitError, setattr, a, 'inst', Bah()) - - def test_unique_default_value(self): - class Foo(object): pass - class A(HasTraits): - inst = Instance(Foo,(),{}) - - a = A() - b = A() - self.assertTrue(a.inst is not b.inst) - - def test_args_kw(self): - class Foo(object): - def __init__(self, c): self.c = c - class Bar(object): pass - class Bah(object): - def __init__(self, c, d): - self.c = c; self.d = d - - class A(HasTraits): - inst = Instance(Foo, (10,)) - a = A() - self.assertEqual(a.inst.c, 10) - - class B(HasTraits): - inst = Instance(Bah, args=(10,), kw=dict(d=20)) - b = B() - self.assertEqual(b.inst.c, 10) - self.assertEqual(b.inst.d, 20) - - class C(HasTraits): - inst = Instance(Foo, allow_none=True) - c = C() - self.assertTrue(c.inst is None) - - def test_bad_default(self): - class Foo(object): pass - - class A(HasTraits): - inst = Instance(Foo) - - a = A() - with self.assertRaises(TraitError): - a.inst - - def test_instance(self): - class Foo(object): pass - - def inner(): - class A(HasTraits): - inst = Instance(Foo()) - - self.assertRaises(TraitError, inner) - - -class TestThis(TestCase): - - def test_this_class(self): - class Foo(HasTraits): - this = This() - - f = Foo() - self.assertEqual(f.this, None) - g = Foo() - f.this = g - self.assertEqual(f.this, g) - self.assertRaises(TraitError, setattr, f, 'this', 10) - - def test_this_inst(self): - class Foo(HasTraits): - this = This() - - f = Foo() - f.this = Foo() - self.assertTrue(isinstance(f.this, Foo)) - - def test_subclass(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - pass - f = Foo() - b = Bar() - f.t = b - b.t = f - self.assertEqual(f.t, b) - self.assertEqual(b.t, f) - - def test_subclass_override(self): - class Foo(HasTraits): - t = This() - class Bar(Foo): - t = This() - f = Foo() - b = Bar() - f.t = b - self.assertEqual(f.t, b) - self.assertRaises(TraitError, setattr, b, 't', f) - - def test_this_in_container(self): - - class Tree(HasTraits): - value = Unicode() - leaves = List(This()) - - tree = Tree( - value='foo', - leaves=[Tree(value='bar'), Tree(value='buzz')] - ) - - with self.assertRaises(TraitError): - tree.leaves = [1, 2] - -class TraitTestBase(TestCase): - """A best testing class for basic trait types.""" - - def assign(self, value): - self.obj.value = value - - def coerce(self, value): - return value - - def test_good_values(self): - if hasattr(self, '_good_values'): - for value in self._good_values: - self.assign(value) - self.assertEqual(self.obj.value, self.coerce(value)) - - def test_bad_values(self): - if hasattr(self, '_bad_values'): - for value in self._bad_values: - try: - self.assertRaises(TraitError, self.assign, value) - except AssertionError: - assert False, value - - def test_default_value(self): - if hasattr(self, '_default_value'): - self.assertEqual(self._default_value, self.obj.value) - - def test_allow_none(self): - if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and - None in self._bad_values): - trait=self.obj.traits()['value'] - try: - trait.allow_none = True - self._bad_values.remove(None) - #skip coerce. Allow None casts None to None. - self.assign(None) - self.assertEqual(self.obj.value,None) - self.test_good_values() - self.test_bad_values() - finally: - #tear down - trait.allow_none = False - self._bad_values.append(None) - - def tearDown(self): - # restore default value after tests, if set - if hasattr(self, '_default_value'): - self.obj.value = self._default_value - - -class AnyTrait(HasTraits): - - value = Any() - -class AnyTraitTest(TraitTestBase): - - obj = AnyTrait() - - _default_value = None +class TestInstance(TestCase): + + def test_basic(self): + class Foo(object): pass + class Bar(Foo): pass + class Bah(object): pass + + class A(HasTraits): + inst = Instance(Foo, allow_none=True) + + a = A() + self.assertTrue(a.inst is None) + a.inst = Foo() + self.assertTrue(isinstance(a.inst, Foo)) + a.inst = Bar() + self.assertTrue(isinstance(a.inst, Foo)) + self.assertRaises(TraitError, setattr, a, 'inst', Foo) + self.assertRaises(TraitError, setattr, a, 'inst', Bar) + self.assertRaises(TraitError, setattr, a, 'inst', Bah()) + + def test_default_klass(self): + class Foo(object): pass + class Bar(Foo): pass + class Bah(object): pass + + class FooInstance(Instance): + klass = Foo + + class A(HasTraits): + inst = FooInstance(allow_none=True) + + a = A() + self.assertTrue(a.inst is None) + a.inst = Foo() + self.assertTrue(isinstance(a.inst, Foo)) + a.inst = Bar() + self.assertTrue(isinstance(a.inst, Foo)) + self.assertRaises(TraitError, setattr, a, 'inst', Foo) + self.assertRaises(TraitError, setattr, a, 'inst', Bar) + self.assertRaises(TraitError, setattr, a, 'inst', Bah()) + + def test_unique_default_value(self): + class Foo(object): pass + class A(HasTraits): + inst = Instance(Foo,(),{}) + + a = A() + b = A() + self.assertTrue(a.inst is not b.inst) + + def test_args_kw(self): + class Foo(object): + def __init__(self, c): self.c = c + class Bar(object): pass + class Bah(object): + def __init__(self, c, d): + self.c = c; self.d = d + + class A(HasTraits): + inst = Instance(Foo, (10,)) + a = A() + self.assertEqual(a.inst.c, 10) + + class B(HasTraits): + inst = Instance(Bah, args=(10,), kw=dict(d=20)) + b = B() + self.assertEqual(b.inst.c, 10) + self.assertEqual(b.inst.d, 20) + + class C(HasTraits): + inst = Instance(Foo, allow_none=True) + c = C() + self.assertTrue(c.inst is None) + + def test_bad_default(self): + class Foo(object): pass + + class A(HasTraits): + inst = Instance(Foo) + + a = A() + with self.assertRaises(TraitError): + a.inst + + def test_instance(self): + class Foo(object): pass + + def inner(): + class A(HasTraits): + inst = Instance(Foo()) + + self.assertRaises(TraitError, inner) + + +class TestThis(TestCase): + + def test_this_class(self): + class Foo(HasTraits): + this = This() + + f = Foo() + self.assertEqual(f.this, None) + g = Foo() + f.this = g + self.assertEqual(f.this, g) + self.assertRaises(TraitError, setattr, f, 'this', 10) + + def test_this_inst(self): + class Foo(HasTraits): + this = This() + + f = Foo() + f.this = Foo() + self.assertTrue(isinstance(f.this, Foo)) + + def test_subclass(self): + class Foo(HasTraits): + t = This() + class Bar(Foo): + pass + f = Foo() + b = Bar() + f.t = b + b.t = f + self.assertEqual(f.t, b) + self.assertEqual(b.t, f) + + def test_subclass_override(self): + class Foo(HasTraits): + t = This() + class Bar(Foo): + t = This() + f = Foo() + b = Bar() + f.t = b + self.assertEqual(f.t, b) + self.assertRaises(TraitError, setattr, b, 't', f) + + def test_this_in_container(self): + + class Tree(HasTraits): + value = Unicode() + leaves = List(This()) + + tree = Tree( + value='foo', + leaves=[Tree(value='bar'), Tree(value='buzz')] + ) + + with self.assertRaises(TraitError): + tree.leaves = [1, 2] + +class TraitTestBase(TestCase): + """A best testing class for basic trait types.""" + + def assign(self, value): + self.obj.value = value + + def coerce(self, value): + return value + + def test_good_values(self): + if hasattr(self, '_good_values'): + for value in self._good_values: + self.assign(value) + self.assertEqual(self.obj.value, self.coerce(value)) + + def test_bad_values(self): + if hasattr(self, '_bad_values'): + for value in self._bad_values: + try: + self.assertRaises(TraitError, self.assign, value) + except AssertionError: + assert False, value + + def test_default_value(self): + if hasattr(self, '_default_value'): + self.assertEqual(self._default_value, self.obj.value) + + def test_allow_none(self): + if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and + None in self._bad_values): + trait=self.obj.traits()['value'] + try: + trait.allow_none = True + self._bad_values.remove(None) + #skip coerce. Allow None casts None to None. + self.assign(None) + self.assertEqual(self.obj.value,None) + self.test_good_values() + self.test_bad_values() + finally: + #tear down + trait.allow_none = False + self._bad_values.append(None) + + def tearDown(self): + # restore default value after tests, if set + if hasattr(self, '_default_value'): + self.obj.value = self._default_value + + +class AnyTrait(HasTraits): + + value = Any() + +class AnyTraitTest(TraitTestBase): + + obj = AnyTrait() + + _default_value = None _good_values = [10.0, 'ten', [10], {'ten': 10},(10,), None, 1j] - _bad_values = [] - -class UnionTrait(HasTraits): - - value = Union([Type(), Bool()]) - -class UnionTraitTest(TraitTestBase): - + _bad_values = [] + +class UnionTrait(HasTraits): + + value = Union([Type(), Bool()]) + +class UnionTraitTest(TraitTestBase): + obj = UnionTrait(value="traitlets.config.Config") - _good_values = [int, float, True] - _bad_values = [[], (0,), 1j] - + _good_values = [int, float, True] + _bad_values = [[], (0,), 1j] + class CallableTrait(HasTraits): value = Callable() @@ -1255,407 +1255,407 @@ class CallableTraitTest(TraitTestBase): _good_values = [int, sorted, lambda x: print(x)] _bad_values = [[], 1, ''] -class OrTrait(HasTraits): - - value = Bool() | Unicode() - -class OrTraitTest(TraitTestBase): - - obj = OrTrait() - _good_values = [True, False, 'ten'] - _bad_values = [[], (0,), 1j] - -class IntTrait(HasTraits): - - value = Int(99, min=-100) - -class TestInt(TraitTestBase): - - obj = IntTrait() - _default_value = 99 - _good_values = [10, -10] +class OrTrait(HasTraits): + + value = Bool() | Unicode() + +class OrTraitTest(TraitTestBase): + + obj = OrTrait() + _good_values = [True, False, 'ten'] + _bad_values = [[], (0,), 1j] + +class IntTrait(HasTraits): + + value = Int(99, min=-100) + +class TestInt(TraitTestBase): + + obj = IntTrait() + _default_value = 99 + _good_values = [10, -10] _bad_values = ['ten', [10], {'ten': 10}, (10,), None, 1j, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', '10', '-10', -200] - - -class CIntTrait(HasTraits): - value = CInt('5') - -class TestCInt(TraitTestBase): - obj = CIntTrait() - - _default_value = 5 + + +class CIntTrait(HasTraits): + value = CInt('5') + +class TestCInt(TraitTestBase): + obj = CIntTrait() + + _default_value = 5 _good_values = ['10', '-10', 10, 10.0, -10.0, 10.1] _bad_values = ['ten', [10], {'ten': 10},(10,), None, 1j, '10.1'] - - def coerce(self, n): - return int(n) - - -class MinBoundCIntTrait(HasTraits): - value = CInt('5', min=3) - -class TestMinBoundCInt(TestCInt): - obj = MinBoundCIntTrait() - - _default_value = 5 - _good_values = [3, 3.0, '3'] - _bad_values = [2.6, 2, -3, -3.0] - - -class LongTrait(HasTraits): - + + def coerce(self, n): + return int(n) + + +class MinBoundCIntTrait(HasTraits): + value = CInt('5', min=3) + +class TestMinBoundCInt(TestCInt): + obj = MinBoundCIntTrait() + + _default_value = 5 + _good_values = [3, 3.0, '3'] + _bad_values = [2.6, 2, -3, -3.0] + + +class LongTrait(HasTraits): + value = Long(99) - -class TestLong(TraitTestBase): - - obj = LongTrait() - + +class TestLong(TraitTestBase): + + obj = LongTrait() + _default_value = 99 - _good_values = [10, -10] + _good_values = [10, -10] _bad_values = ['ten', [10], {'ten': 10},(10,), - None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', + None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1', '-10.1'] - - -class MinBoundLongTrait(HasTraits): + + +class MinBoundLongTrait(HasTraits): value = Long(99, min=5) - -class TestMinBoundLong(TraitTestBase): - obj = MinBoundLongTrait() - + +class TestMinBoundLong(TraitTestBase): + obj = MinBoundLongTrait() + _default_value = 99 - _good_values = [5, 10] - _bad_values = [4, -10] - - -class MaxBoundLongTrait(HasTraits): + _good_values = [5, 10] + _bad_values = [4, -10] + + +class MaxBoundLongTrait(HasTraits): value = Long(5, max=10) - -class TestMaxBoundLong(TraitTestBase): - obj = MaxBoundLongTrait() - + +class TestMaxBoundLong(TraitTestBase): + obj = MaxBoundLongTrait() + _default_value = 5 - _good_values = [10, -2] - _bad_values = [11, 20] - - -class CLongTrait(HasTraits): - value = CLong('5') - -class TestCLong(TraitTestBase): - obj = CLongTrait() - + _good_values = [10, -2] + _bad_values = [11, 20] + + +class CLongTrait(HasTraits): + value = CLong('5') + +class TestCLong(TraitTestBase): + obj = CLongTrait() + _default_value = 5 _good_values = ['10', '-10', 10, 10.0, -10.0, 10.1] _bad_values = ['ten', [10], {'ten': 10},(10,), None, 1j, '10.1'] - - def coerce(self, n): + + def coerce(self, n): return int(n) - - -class MaxBoundCLongTrait(HasTraits): - value = CLong('5', max=10) - -class TestMaxBoundCLong(TestCLong): - obj = MaxBoundCLongTrait() - + + +class MaxBoundCLongTrait(HasTraits): + value = CLong('5', max=10) + +class TestMaxBoundCLong(TestCLong): + obj = MaxBoundCLongTrait() + + _default_value = 5 + _good_values = [10, '10', 10.3] + _bad_values = [11.0, '11'] + + +class IntegerTrait(HasTraits): + value = Integer(1) + +class TestInteger(TestLong): + obj = IntegerTrait() + _default_value = 1 + + def coerce(self, n): + return int(n) + + +class MinBoundIntegerTrait(HasTraits): + value = Integer(5, min=3) + +class TestMinBoundInteger(TraitTestBase): + obj = MinBoundIntegerTrait() + _default_value = 5 - _good_values = [10, '10', 10.3] - _bad_values = [11.0, '11'] - - -class IntegerTrait(HasTraits): - value = Integer(1) - -class TestInteger(TestLong): - obj = IntegerTrait() - _default_value = 1 - - def coerce(self, n): - return int(n) - - -class MinBoundIntegerTrait(HasTraits): - value = Integer(5, min=3) - -class TestMinBoundInteger(TraitTestBase): - obj = MinBoundIntegerTrait() - - _default_value = 5 - _good_values = 3, 20 - _bad_values = [2, -10] - - -class MaxBoundIntegerTrait(HasTraits): - value = Integer(1, max=3) - -class TestMaxBoundInteger(TraitTestBase): - obj = MaxBoundIntegerTrait() - - _default_value = 1 - _good_values = 3, -2 - _bad_values = [4, 10] - - -class FloatTrait(HasTraits): - - value = Float(99.0, max=200.0) - -class TestFloat(TraitTestBase): - - obj = FloatTrait() - - _default_value = 99.0 - _good_values = [10, -10, 10.1, -10.1] + _good_values = 3, 20 + _bad_values = [2, -10] + + +class MaxBoundIntegerTrait(HasTraits): + value = Integer(1, max=3) + +class TestMaxBoundInteger(TraitTestBase): + obj = MaxBoundIntegerTrait() + + _default_value = 1 + _good_values = 3, -2 + _bad_values = [4, 10] + + +class FloatTrait(HasTraits): + + value = Float(99.0, max=200.0) + +class TestFloat(TraitTestBase): + + obj = FloatTrait() + + _default_value = 99.0 + _good_values = [10, -10, 10.1, -10.1] _bad_values = ['ten', [10], {'ten': 10}, (10,), None, 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', 201.0] - - -class CFloatTrait(HasTraits): - - value = CFloat('99.0', max=200.0) - -class TestCFloat(TraitTestBase): - - obj = CFloatTrait() - - _default_value = 99.0 + + +class CFloatTrait(HasTraits): + + value = CFloat('99.0', max=200.0) + +class TestCFloat(TraitTestBase): + + obj = CFloatTrait() + + _default_value = 99.0 _good_values = [10, 10.0, 10.5, '10.0', '10', '-10'] _bad_values = ['ten', [10], {'ten': 10}, (10,), None, 1j, - 200.1, '200.1'] - - def coerce(self, v): - return float(v) - - -class ComplexTrait(HasTraits): - - value = Complex(99.0-99.0j) - -class TestComplex(TraitTestBase): - - obj = ComplexTrait() - - _default_value = 99.0-99.0j - _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j, - 10.1j, 10.1+10.1j, 10.1-10.1j] + 200.1, '200.1'] + + def coerce(self, v): + return float(v) + + +class ComplexTrait(HasTraits): + + value = Complex(99.0-99.0j) + +class TestComplex(TraitTestBase): + + obj = ComplexTrait() + + _default_value = 99.0-99.0j + _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j, + 10.1j, 10.1+10.1j, 10.1-10.1j] _bad_values = ['10L', '-10L', 'ten', [10], {'ten': 10},(10,), None] - - -class BytesTrait(HasTraits): - - value = Bytes(b'string') - -class TestBytes(TraitTestBase): - - obj = BytesTrait() - - _default_value = b'string' - _good_values = [b'10', b'-10', b'10L', - b'-10L', b'10.1', b'-10.1', b'string'] - _bad_values = [10, -10, 10.1, -10.1, 1j, [10], + + +class BytesTrait(HasTraits): + + value = Bytes(b'string') + +class TestBytes(TraitTestBase): + + obj = BytesTrait() + + _default_value = b'string' + _good_values = [b'10', b'-10', b'10L', + b'-10L', b'10.1', b'-10.1', b'string'] + _bad_values = [10, -10, 10.1, -10.1, 1j, [10], ['ten'],{'ten': 10},(10,), None, 'string'] - - -class UnicodeTrait(HasTraits): - + + +class UnicodeTrait(HasTraits): + value = Unicode('unicode') - -class TestUnicode(TraitTestBase): - - obj = UnicodeTrait() - + +class TestUnicode(TraitTestBase): + + obj = UnicodeTrait() + _default_value = 'unicode' - _good_values = ['10', '-10', '10L', '-10L', '10.1', + _good_values = ['10', '-10', '10L', '-10L', '10.1', '-10.1', '', 'string', "€", b"bytestring"] - _bad_values = [10, -10, 10.1, -10.1, 1j, + _bad_values = [10, -10, 10.1, -10.1, 1j, [10], ['ten'], {'ten': 10},(10,), None] - + def coerce(self, v): return cast_unicode(v) - - -class ObjectNameTrait(HasTraits): - value = ObjectName("abc") - -class TestObjectName(TraitTestBase): - obj = ObjectNameTrait() - - _default_value = "abc" + + +class ObjectNameTrait(HasTraits): + value = ObjectName("abc") + +class TestObjectName(TraitTestBase): + obj = ObjectNameTrait() + + _default_value = "abc" _good_values = ["a", "gh", "g9", "g_", "_G", "a345_"] _bad_values = [1, "", "€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]", - None, object(), object] + None, object(), object] _good_values.append("þ") # þ=1 is valid in Python 3 (PEP 3131). - - -class DottedObjectNameTrait(HasTraits): - value = DottedObjectName("a.b") - -class TestDottedObjectName(TraitTestBase): - obj = DottedObjectNameTrait() - - _default_value = "a.b" + + +class DottedObjectNameTrait(HasTraits): + value = DottedObjectName("a.b") + +class TestDottedObjectName(TraitTestBase): + obj = DottedObjectNameTrait() + + _default_value = "a.b" _good_values = ["A", "y.t", "y765.__repr__", "os.path.join"] _bad_values = [1, "abc.€", "_.@", ".", ".abc", "abc.", ".abc.", None] - + _good_values.append("t.þ") - - -class TCPAddressTrait(HasTraits): - value = TCPAddress() - -class TestTCPAddress(TraitTestBase): - - obj = TCPAddressTrait() - - _default_value = ('127.0.0.1',0) - _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] - _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None] - - -class ListTrait(HasTraits): - - value = List(Int()) - - -class TestList(TraitTestBase): - - obj = ListTrait() - - _default_value = [] - _good_values = [[], [1], list(range(10)), (1,2)] - _bad_values = [10, [1,'a'], 'a'] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - - -class Foo(object): - pass - -class NoneInstanceListTrait(HasTraits): - - value = List(Instance(Foo)) - -class TestNoneInstanceList(TraitTestBase): - - obj = NoneInstanceListTrait() - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [[None], [Foo(), None]] - - -class InstanceListTrait(HasTraits): - - value = List(Instance(__name__+'.Foo')) - -class TestInstanceList(TraitTestBase): - - obj = InstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, Foo) - - _default_value = [] - _good_values = [[Foo(), Foo()], []] - _bad_values = [['1', 2,], '1', [Foo], None] - -class UnionListTrait(HasTraits): - - value = List(Int() | Bool()) - + + +class TCPAddressTrait(HasTraits): + value = TCPAddress() + +class TestTCPAddress(TraitTestBase): + + obj = TCPAddressTrait() + + _default_value = ('127.0.0.1',0) + _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] + _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None] + + +class ListTrait(HasTraits): + + value = List(Int()) + + +class TestList(TraitTestBase): + + obj = ListTrait() + + _default_value = [] + _good_values = [[], [1], list(range(10)), (1,2)] + _bad_values = [10, [1,'a'], 'a'] + + def coerce(self, value): + if value is not None: + value = list(value) + return value + + +class Foo(object): + pass + +class NoneInstanceListTrait(HasTraits): + + value = List(Instance(Foo)) + +class TestNoneInstanceList(TraitTestBase): + + obj = NoneInstanceListTrait() + + _default_value = [] + _good_values = [[Foo(), Foo()], []] + _bad_values = [[None], [Foo(), None]] + + +class InstanceListTrait(HasTraits): + + value = List(Instance(__name__+'.Foo')) + +class TestInstanceList(TraitTestBase): + + obj = InstanceListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, Foo) + + _default_value = [] + _good_values = [[Foo(), Foo()], []] + _bad_values = [['1', 2,], '1', [Foo], None] + +class UnionListTrait(HasTraits): + + value = List(Int() | Bool()) + class TestUnionListTrait(TraitTestBase): - - obj = UnionListTrait() - - _default_value = [] - _good_values = [[True, 1], [False, True]] - _bad_values = [[1, 'True'], False] - - -class LenListTrait(HasTraits): - - value = List(Int(), [0], minlen=1, maxlen=2) - -class TestLenList(TraitTestBase): - - obj = LenListTrait() - - _default_value = [0] - _good_values = [[1], [1,2], (1,2)] - _bad_values = [10, [1,'a'], 'a', [], list(range(3))] - - def coerce(self, value): - if value is not None: - value = list(value) - return value - -class TupleTrait(HasTraits): - - value = Tuple(Int(allow_none=True), default_value=(1,)) - -class TestTupleTrait(TraitTestBase): - - obj = TupleTrait() - + + obj = UnionListTrait() + + _default_value = [] + _good_values = [[True, 1], [False, True]] + _bad_values = [[1, 'True'], False] + + +class LenListTrait(HasTraits): + + value = List(Int(), [0], minlen=1, maxlen=2) + +class TestLenList(TraitTestBase): + + obj = LenListTrait() + + _default_value = [0] + _good_values = [[1], [1,2], (1,2)] + _bad_values = [10, [1,'a'], 'a', [], list(range(3))] + + def coerce(self, value): + if value is not None: + value = list(value) + return value + +class TupleTrait(HasTraits): + + value = Tuple(Int(allow_none=True), default_value=(1,)) + +class TestTupleTrait(TraitTestBase): + + obj = TupleTrait() + _default_value = (1,) - _good_values = [(1,), (0,), [1]] - _bad_values = [10, (1, 2), ('a'), (), None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - -class LooseTupleTrait(HasTraits): - - value = Tuple((1,2,3)) - -class TestLooseTupleTrait(TraitTestBase): - - obj = LooseTupleTrait() - - _default_value = (1,2,3) - _good_values = [(1,), [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] - _bad_values = [10, 'hello', {}, None] - - def coerce(self, value): - if value is not None: - value = tuple(value) - return value - - def test_invalid_args(self): - self.assertRaises(TypeError, Tuple, 5) - self.assertRaises(TypeError, Tuple, default_value='hello') - t = Tuple(Int(), CBytes(), default_value=(1,5)) - - -class MultiTupleTrait(HasTraits): - - value = Tuple(Int(), Bytes(), default_value=[99,b'bottles']) - -class TestMultiTuple(TraitTestBase): - - obj = MultiTupleTrait() - - _default_value = (99,b'bottles') - _good_values = [(1,b'a'), (2,b'b')] + _good_values = [(1,), (0,), [1]] + _bad_values = [10, (1, 2), ('a'), (), None] + + def coerce(self, value): + if value is not None: + value = tuple(value) + return value + + def test_invalid_args(self): + self.assertRaises(TypeError, Tuple, 5) + self.assertRaises(TypeError, Tuple, default_value='hello') + t = Tuple(Int(), CBytes(), default_value=(1,5)) + +class LooseTupleTrait(HasTraits): + + value = Tuple((1,2,3)) + +class TestLooseTupleTrait(TraitTestBase): + + obj = LooseTupleTrait() + + _default_value = (1,2,3) + _good_values = [(1,), [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] + _bad_values = [10, 'hello', {}, None] + + def coerce(self, value): + if value is not None: + value = tuple(value) + return value + + def test_invalid_args(self): + self.assertRaises(TypeError, Tuple, 5) + self.assertRaises(TypeError, Tuple, default_value='hello') + t = Tuple(Int(), CBytes(), default_value=(1,5)) + + +class MultiTupleTrait(HasTraits): + + value = Tuple(Int(), Bytes(), default_value=[99,b'bottles']) + +class TestMultiTuple(TraitTestBase): + + obj = MultiTupleTrait() + + _default_value = (99,b'bottles') + _good_values = [(1,b'a'), (2,b'b')] _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, 'a')) - + @pytest.mark.parametrize( "Trait", (List, Tuple, Set, Dict, Integer, Unicode,), @@ -1707,64 +1707,64 @@ def test_subclass_default_value(Trait, default_value): assert c.t == default_value -class CRegExpTrait(HasTraits): - - value = CRegExp(r'') - - -class TestCRegExp(TraitTestBase): - - def coerce(self, value): - return re.compile(value) - - obj = CRegExpTrait() - - _default_value = re.compile(r'') - _good_values = [r'\d+', re.compile(r'\d+')] - _bad_values = ['(', None, ()] - -class DictTrait(HasTraits): - value = Dict() - -def test_dict_assignment(): - d = dict() - c = DictTrait() - c.value = d - d['a'] = 5 +class CRegExpTrait(HasTraits): + + value = CRegExp(r'') + + +class TestCRegExp(TraitTestBase): + + def coerce(self, value): + return re.compile(value) + + obj = CRegExpTrait() + + _default_value = re.compile(r'') + _good_values = [r'\d+', re.compile(r'\d+')] + _bad_values = ['(', None, ()] + +class DictTrait(HasTraits): + value = Dict() + +def test_dict_assignment(): + d = dict() + c = DictTrait() + c.value = d + d['a'] = 5 assert d == c.value - assert c.value is d - - + assert c.value is d + + class UniformlyValueValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), - default_value={'foo': '1'}) - - + + value = Dict(trait=Unicode(), + default_value={'foo': '1'}) + + class TestInstanceUniformlyValueValidatedDict(TraitTestBase): - + obj = UniformlyValueValidatedDictTrait() - - _default_value = {'foo': '1'} - _good_values = [{'foo': '0', 'bar': '1'}] - _bad_values = [{'foo': 0, 'bar': '1'}] - - + + _default_value = {'foo': '1'} + _good_values = [{'foo': '0', 'bar': '1'}] + _bad_values = [{'foo': 0, 'bar': '1'}] + + class NonuniformlyValueValidatedDictTrait(HasTraits): - - value = Dict(traits={'foo': Int()}, - default_value={'foo': 1}) - - + + value = Dict(traits={'foo': Int()}, + default_value={'foo': 1}) + + class TestInstanceNonuniformlyValueValidatedDict(TraitTestBase): - + obj = NonuniformlyValueValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 0, 'bar': 1}] - _bad_values = [{'foo': '0', 'bar': '1'}] - - + + _default_value = {'foo': 1} + _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 0, 'bar': 1}] + _bad_values = [{'foo': '0', 'bar': '1'}] + + class KeyValidatedDictTrait(HasTraits): value = Dict(key_trait=Unicode(), @@ -1780,196 +1780,196 @@ class TestInstanceKeyValidatedDict(TraitTestBase): _bad_values = [{'foo': '0', 0: '1'}] -class FullyValidatedDictTrait(HasTraits): - - value = Dict(trait=Unicode(), +class FullyValidatedDictTrait(HasTraits): + + value = Dict(trait=Unicode(), key_trait=Unicode(), - traits={'foo': Int()}, - default_value={'foo': 1}) - - -class TestInstanceFullyValidatedDict(TraitTestBase): - - obj = FullyValidatedDictTrait() - - _default_value = {'foo': 1} - _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 1, 'bar': '2'}] + traits={'foo': Int()}, + default_value={'foo': 1}) + + +class TestInstanceFullyValidatedDict(TraitTestBase): + + obj = FullyValidatedDictTrait() + + _default_value = {'foo': 1} + _good_values = [{'foo': 0, 'bar': '1'}, {'foo': 1, 'bar': '2'}] _bad_values = [{'foo': 0, 'bar': 1}, {'foo': '0', 'bar': '1'}, {'foo': 0, 0: '1'}] - - -def test_dict_default_value(): - """Check that the `{}` default value of the Dict traitlet constructor is - actually copied.""" - - class Foo(HasTraits): - d1 = Dict() - d2 = Dict() - - foo = Foo() - assert foo.d1 == {} - assert foo.d2 == {} - assert foo.d1 is not foo.d2 - - -class TestValidationHook(TestCase): - - def test_parity_trait(self): - """Verify that the early validation hook is effective""" - - class Parity(HasTraits): - - value = Int(0) - parity = Enum(['odd', 'even'], default_value='even') - - @validate('value') - def _value_validate(self, proposal): - value = proposal['value'] - if self.parity == 'even' and value % 2: - raise TraitError('Expected an even number') - if self.parity == 'odd' and (value % 2 == 0): - raise TraitError('Expected an odd number') - return value - - u = Parity() - u.parity = 'odd' - u.value = 1 # OK - with self.assertRaises(TraitError): - u.value = 2 # Trait Error - - u.parity = 'even' - u.value = 2 # OK - - def test_multiple_validate(self): - """Verify that we can register the same validator to multiple names""" - - class OddEven(HasTraits): - - odd = Int(1) - even = Int(0) - - @validate('odd', 'even') - def check_valid(self, proposal): - if proposal['trait'].name == 'odd' and not proposal['value'] % 2: - raise TraitError('odd should be odd') - if proposal['trait'].name == 'even' and proposal['value'] % 2: - raise TraitError('even should be even') - - u = OddEven() - u.odd = 3 # OK - with self.assertRaises(TraitError): - u.odd = 2 # Trait Error - - u.even = 2 # OK - with self.assertRaises(TraitError): - u.even = 3 # Trait Error - - - -class TestLink(TestCase): - - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.value) - b.value = 6 - self.assertEqual(a.value, b.value) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one of the values to make sure they stay in sync. - a.value = 5 - self.assertEqual(a.value, b.count) - b.count = 4 - self.assertEqual(a.value, b.count) - + + +def test_dict_default_value(): + """Check that the `{}` default value of the Dict traitlet constructor is + actually copied.""" + + class Foo(HasTraits): + d1 = Dict() + d2 = Dict() + + foo = Foo() + assert foo.d1 == {} + assert foo.d2 == {} + assert foo.d1 is not foo.d2 + + +class TestValidationHook(TestCase): + + def test_parity_trait(self): + """Verify that the early validation hook is effective""" + + class Parity(HasTraits): + + value = Int(0) + parity = Enum(['odd', 'even'], default_value='even') + + @validate('value') + def _value_validate(self, proposal): + value = proposal['value'] + if self.parity == 'even' and value % 2: + raise TraitError('Expected an even number') + if self.parity == 'odd' and (value % 2 == 0): + raise TraitError('Expected an odd number') + return value + + u = Parity() + u.parity = 'odd' + u.value = 1 # OK + with self.assertRaises(TraitError): + u.value = 2 # Trait Error + + u.parity = 'even' + u.value = 2 # OK + + def test_multiple_validate(self): + """Verify that we can register the same validator to multiple names""" + + class OddEven(HasTraits): + + odd = Int(1) + even = Int(0) + + @validate('odd', 'even') + def check_valid(self, proposal): + if proposal['trait'].name == 'odd' and not proposal['value'] % 2: + raise TraitError('odd should be odd') + if proposal['trait'].name == 'even' and proposal['value'] % 2: + raise TraitError('even should be even') + + u = OddEven() + u.odd = 3 # OK + with self.assertRaises(TraitError): + u.odd = 2 # Trait Error + + u.even = 2 # OK + with self.assertRaises(TraitError): + u.even = 3 # Trait Error + + + +class TestLink(TestCase): + + def test_connect_same(self): + """Verify two traitlets of the same type can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = link((a, 'value'), (b, 'value')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.value) + + # Change one of the values to make sure they stay in sync. + a.value = 5 + self.assertEqual(a.value, b.value) + b.value = 6 + self.assertEqual(a.value, b.value) + + def test_link_different(self): + """Verify two traitlets of different types can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Conenct the two classes. + c = link((a, 'value'), (b, 'count')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.count) + + # Change one of the values to make sure they stay in sync. + a.value = 5 + self.assertEqual(a.value, b.count) + b.count = 4 + self.assertEqual(a.value, b.count) + def test_unlink_link(self): """Verify two linked traitlets can be unlinked and relinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Connect the two classes. + c = link((a, 'value'), (b, 'value')) + a.value = 4 + c.unlink() + + # Change one of the values to make sure they don't stay in sync. + a.value = 5 + self.assertNotEqual(a.value, b.value) c.link() self.assertEqual(a.value, b.value) a.value += 1 self.assertEqual(a.value, b.value) - - def test_callbacks(self): - """Verify two linked traitlets have their callbacks called once.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Register callbacks that count. - callback_count = [] - def a_callback(name, old, new): - callback_count.append('a') - a.on_trait_change(a_callback, 'value') - def b_callback(name, old, new): - callback_count.append('b') - b.on_trait_change(b_callback, 'count') - - # Connect the two classes. - c = link((a, 'value'), (b, 'count')) - - # Make sure b's count was set to a's value once. - self.assertEqual(''.join(callback_count), 'b') - del callback_count[:] - - # Make sure a's value was set to b's count once. - b.count = 5 - self.assertEqual(''.join(callback_count), 'ba') - del callback_count[:] - - # Make sure b's count was set to a's value once. - a.value = 4 - self.assertEqual(''.join(callback_count), 'ab') - del callback_count[:] - + + def test_callbacks(self): + """Verify two linked traitlets have their callbacks called once.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Register callbacks that count. + callback_count = [] + def a_callback(name, old, new): + callback_count.append('a') + a.on_trait_change(a_callback, 'value') + def b_callback(name, old, new): + callback_count.append('b') + b.on_trait_change(b_callback, 'count') + + # Connect the two classes. + c = link((a, 'value'), (b, 'count')) + + # Make sure b's count was set to a's value once. + self.assertEqual(''.join(callback_count), 'b') + del callback_count[:] + + # Make sure a's value was set to b's count once. + b.count = 5 + self.assertEqual(''.join(callback_count), 'ba') + del callback_count[:] + + # Make sure b's count was set to a's value once. + a.value = 4 + self.assertEqual(''.join(callback_count), 'ab') + del callback_count[:] + def test_tranform(self): """Test transform link.""" @@ -2020,610 +2020,610 @@ class TestLink(TestCase): l = link((mc, "i"), (mc, "j")) self.assertRaises(TraitError, setattr, mc, 'j', 2) -class TestDirectionalLink(TestCase): - def test_connect_same(self): - """Verify two traitlets of the same type can be linked together using directional_link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.value) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.value, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_tranform(self): - """Test transform link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'value'), lambda x: 2 * x) - - # Make sure the values are correct at the point of linking. - self.assertEqual(b.value, 2 * a.value) - - # Change one the value of the source and check that it modifies the target. - a.value = 5 - self.assertEqual(b.value, 10) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - - def test_link_different(self): - """Verify two traitlets of different types can be linked together using link.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - class B(HasTraits): - count = Int() - a = A(value=9) - b = B(count=8) - - # Conenct the two classes. - c = directional_link((a, 'value'), (b, 'count')) - - # Make sure the values are the same at the point of linking. - self.assertEqual(a.value, b.count) - - # Change one the value of the source and check that it synchronizes the target. - a.value = 5 - self.assertEqual(b.count, 5) - # Change one the value of the target and check that it has no impact on the source - b.value = 6 - self.assertEqual(a.value, 5) - +class TestDirectionalLink(TestCase): + def test_connect_same(self): + """Verify two traitlets of the same type can be linked together using directional_link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'value')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.value) + + # Change one the value of the source and check that it synchronizes the target. + a.value = 5 + self.assertEqual(b.value, 5) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + + def test_tranform(self): + """Test transform link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'value'), lambda x: 2 * x) + + # Make sure the values are correct at the point of linking. + self.assertEqual(b.value, 2 * a.value) + + # Change one the value of the source and check that it modifies the target. + a.value = 5 + self.assertEqual(b.value, 10) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + + def test_link_different(self): + """Verify two traitlets of different types can be linked together using link.""" + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + class B(HasTraits): + count = Int() + a = A(value=9) + b = B(count=8) + + # Conenct the two classes. + c = directional_link((a, 'value'), (b, 'count')) + + # Make sure the values are the same at the point of linking. + self.assertEqual(a.value, b.count) + + # Change one the value of the source and check that it synchronizes the target. + a.value = 5 + self.assertEqual(b.count, 5) + # Change one the value of the target and check that it has no impact on the source + b.value = 6 + self.assertEqual(a.value, 5) + def test_unlink_link(self): """Verify two linked traitlets can be unlinked and relinked.""" - - # Create two simple classes with Int traitlets. - class A(HasTraits): - value = Int() - a = A(value=9) - b = A(value=8) - - # Connect the two classes. - c = directional_link((a, 'value'), (b, 'value')) - a.value = 4 - c.unlink() - - # Change one of the values to make sure they don't stay in sync. - a.value = 5 - self.assertNotEqual(a.value, b.value) + + # Create two simple classes with Int traitlets. + class A(HasTraits): + value = Int() + a = A(value=9) + b = A(value=8) + + # Connect the two classes. + c = directional_link((a, 'value'), (b, 'value')) + a.value = 4 + c.unlink() + + # Change one of the values to make sure they don't stay in sync. + a.value = 5 + self.assertNotEqual(a.value, b.value) c.link() self.assertEqual(a.value, b.value) a.value += 1 self.assertEqual(a.value, b.value) - -class Pickleable(HasTraits): - - i = Int() - @observe('i') - def _i_changed(self, change): pass - @validate('i') - def _i_validate(self, commit): - return commit['value'] - - j = Int() - - def __init__(self): - with self.hold_trait_notifications(): - self.i = 1 - self.on_trait_change(self._i_changed, 'i') - -def test_pickle_hastraits(): - c = Pickleable() - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - c.i = 5 - for protocol in range(pickle.HIGHEST_PROTOCOL + 1): - p = pickle.dumps(c, protocol) - c2 = pickle.loads(p) - assert c2.i == c.i - assert c2.j == c.j - - -def test_hold_trait_notifications(): - changes = [] - - class Test(HasTraits): - a = Integer(0) - b = Integer(0) - - def _a_changed(self, name, old, new): - changes.append((old, new)) - - def _b_validate(self, value, trait): - if value != 0: - raise TraitError('Only 0 is a valid value') - return value - - # Test context manager and nesting - t = Test() - with t.hold_trait_notifications(): - with t.hold_trait_notifications(): - t.a = 1 - assert t.a == 1 - assert changes == [] - t.a = 2 - assert t.a == 2 - with t.hold_trait_notifications(): - t.a = 3 - assert t.a == 3 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - t.a = 4 - assert t.a == 4 - assert changes == [] - - assert changes == [(0, 4)] - # Test roll-back - try: - with t.hold_trait_notifications(): - t.b = 1 # raises a Trait error - except: - pass - assert t.b == 0 - - -class RollBack(HasTraits): - bar = Int() - def _bar_validate(self, value, trait): - if value: - raise TraitError('foobar') - return value - - -class TestRollback(TestCase): - - def test_roll_back(self): - - def assign_rollback(): - RollBack(bar=1) - - self.assertRaises(TraitError, assign_rollback) - - -class CacheModification(HasTraits): - foo = Int() - bar = Int() - - def _bar_validate(self, value, trait): - self.foo = value - return value - - def _foo_validate(self, value, trait): - self.bar = value - return value - - -def test_cache_modification(): - CacheModification(foo=1) - CacheModification(bar=1) - - -class OrderTraits(HasTraits): - notified = Dict() - - a = Unicode() - b = Unicode() - c = Unicode() - d = Unicode() - e = Unicode() - f = Unicode() - g = Unicode() - h = Unicode() - i = Unicode() - j = Unicode() - k = Unicode() - l = Unicode() - - def _notify(self, name, old, new): - """check the value of all traits when each trait change is triggered - - This verifies that the values are not sensitive - to dict ordering when loaded from kwargs - """ - # check the value of the other traits - # when a given trait change notification fires - self.notified[name] = { - c: getattr(self, c) for c in 'abcdefghijkl' - } - - def __init__(self, **kwargs): - self.on_trait_change(self._notify) - super(OrderTraits, self).__init__(**kwargs) - -def test_notification_order(): - d = {c:c for c in 'abcdefghijkl'} - obj = OrderTraits() - assert obj.notified == {} - obj = OrderTraits(**d) - notifications = { - c: d for c in 'abcdefghijkl' - } - assert obj.notified == notifications - - - -### -# Traits for Forward Declaration Tests -### -class ForwardDeclaredInstanceTrait(HasTraits): - - value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredTypeTrait(HasTraits): - - value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True) - -class ForwardDeclaredInstanceListTrait(HasTraits): - - value = List(ForwardDeclaredInstance('ForwardDeclaredBar')) - -class ForwardDeclaredTypeListTrait(HasTraits): - - value = List(ForwardDeclaredType('ForwardDeclaredBar')) -### -# End Traits for Forward Declaration Tests -### - -### -# Classes for Forward Declaration Tests -### -class ForwardDeclaredBar(object): - pass - -class ForwardDeclaredBarSub(ForwardDeclaredBar): - pass -### -# End Classes for Forward Declaration Tests -### - -### -# Forward Declaration Tests -### -class TestForwardDeclaredInstanceTrait(TraitTestBase): - - obj = ForwardDeclaredInstanceTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub] - -class TestForwardDeclaredTypeTrait(TraitTestBase): - - obj = ForwardDeclaredTypeTrait() - _default_value = None - _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub] - _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()] - -class TestForwardDeclaredInstanceList(TraitTestBase): - - obj = ForwardDeclaredInstanceListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar(), ForwardDeclaredBarSub()], - [], - ] - _bad_values = [ - ForwardDeclaredBar(), - [ForwardDeclaredBar(), 3, None], - '1', - # Note that this is the type, not an instance. - [ForwardDeclaredBar], - [None], - None, - ] - -class TestForwardDeclaredTypeList(TraitTestBase): - - obj = ForwardDeclaredTypeListTrait() - - def test_klass(self): - """Test that the instance klass is properly assigned.""" - self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) - - _default_value = [] - _good_values = [ - [ForwardDeclaredBar, ForwardDeclaredBarSub], - [], - ] - _bad_values = [ - ForwardDeclaredBar, - [ForwardDeclaredBar, 3], - '1', - # Note that this is an instance, not the type. - [ForwardDeclaredBar()], - [None], - None, - ] -### -# End Forward Declaration Tests -### - -class TestDynamicTraits(TestCase): - - def setUp(self): - self._notify1 = [] - - def notify1(self, name, old, new): - self._notify1.append((name, old, new)) - - def test_notify_all(self): - - class A(HasTraits): - pass - - a = A() - self.assertTrue(not hasattr(a, 'x')) - self.assertTrue(not hasattr(a, 'y')) - - # Dynamically add trait x. - a.add_traits(x=Int()) - self.assertTrue(hasattr(a, 'x')) - self.assertTrue(isinstance(a, (A, ))) - - # Dynamically add trait y. - a.add_traits(y=Float()) - self.assertTrue(hasattr(a, 'y')) - self.assertTrue(isinstance(a, (A, ))) - self.assertEqual(a.__class__.__name__, A.__name__) - - # Create a new instance and verify that x and y - # aren't defined. - b = A() - self.assertTrue(not hasattr(b, 'x')) - self.assertTrue(not hasattr(b, 'y')) - - # Verify that notification works like normal. - a.on_trait_change(self.notify1) - a.x = 0 - self.assertEqual(len(self._notify1), 0) - a.y = 0.0 - self.assertEqual(len(self._notify1), 0) - a.x = 10 - self.assertTrue(('x', 0, 10) in self._notify1) - a.y = 10.0 - self.assertTrue(('y', 0.0, 10.0) in self._notify1) - self.assertRaises(TraitError, setattr, a, 'x', 'bad string') - self.assertRaises(TraitError, setattr, a, 'y', 'bad string') - self._notify1 = [] - a.on_trait_change(self.notify1, remove=True) - a.x = 20 - a.y = 20.0 - self.assertEqual(len(self._notify1), 0) - - -def test_enum_no_default(): - class C(HasTraits): - t = Enum(['a', 'b']) - - c = C() - c.t = 'a' - assert c.t == 'a' - - c = C() - - with pytest.raises(TraitError): - t = c.t - - c = C(t='b') - assert c.t == 'b' - - -def test_default_value_repr(): - class C(HasTraits): - t = Type('traitlets.HasTraits') - t2 = Type(HasTraits) - n = Integer(0) - lis = List() - d = Dict() - - assert C.t.default_value_repr() == "'traitlets.HasTraits'" - assert C.t2.default_value_repr() == "'traitlets.traitlets.HasTraits'" - assert C.n.default_value_repr() == '0' - assert C.lis.default_value_repr() == '[]' - assert C.d.default_value_repr() == '{}' - - -class TransitionalClass(HasTraits): - - d = Any() - @default('d') - def _d_default(self): - return TransitionalClass - - parent_super = False - calls_super = Integer(0) - - @default('calls_super') - def _calls_super_default(self): - return -1 - - @observe('calls_super') - @observe_compat - def _calls_super_changed(self, change): - self.parent_super = change - - parent_override = False - overrides = Integer(0) - - @observe('overrides') - @observe_compat - def _overrides_changed(self, change): - self.parent_override = change - - -class SubClass(TransitionalClass): - def _d_default(self): - return SubClass - - subclass_super = False - def _calls_super_changed(self, name, old, new): - self.subclass_super = True - super(SubClass, self)._calls_super_changed(name, old, new) - - subclass_override = False - def _overrides_changed(self, name, old, new): - self.subclass_override = True - - -def test_subclass_compat(): - obj = SubClass() - obj.calls_super = 5 - assert obj.parent_super - assert obj.subclass_super - obj.overrides = 5 - assert obj.subclass_override - assert not obj.parent_override - assert obj.d is SubClass - - -class DefinesHandler(HasTraits): - parent_called = False - - trait = Integer() - @observe('trait') - def handler(self, change): - self.parent_called = True - - -class OverridesHandler(DefinesHandler): - child_called = False - - @observe('trait') - def handler(self, change): - self.child_called = True - - -def test_subclass_override_observer(): - obj = OverridesHandler() - obj.trait = 5 - assert obj.child_called - assert not obj.parent_called - - -class DoesntRegisterHandler(DefinesHandler): - child_called = False - - def handler(self, change): - self.child_called = True - - -def test_subclass_override_not_registered(): - """Subclass that overrides observer and doesn't re-register unregisters both""" - obj = DoesntRegisterHandler() - obj.trait = 5 - assert not obj.child_called - assert not obj.parent_called - - -class AddsHandler(DefinesHandler): - child_called = False - - @observe('trait') - def child_handler(self, change): - self.child_called = True - -def test_subclass_add_observer(): - obj = AddsHandler() - obj.trait = 5 - assert obj.child_called - assert obj.parent_called - - -def test_observe_iterables(): - - class C(HasTraits): - i = Integer() - s = Unicode() - - c = C() - recorded = {} - def record(change): - recorded['change'] = change - - # observe with names=set - c.observe(record, names={'i', 's'}) - c.i = 5 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 5 - c.s = 'hi' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'hi' - - # observe with names=custom container with iter, contains - class MyContainer(object): - def __init__(self, container): - self.container = container - - def __iter__(self): - return iter(self.container) - - def __contains__(self, key): - return key in self.container - - c.observe(record, names=MyContainer({'i', 's'})) - c.i = 10 - assert recorded['change'].name == 'i' - assert recorded['change'].new == 10 - c.s = 'ok' - assert recorded['change'].name == 's' - assert recorded['change'].new == 'ok' - - -def test_super_args(): - class SuperRecorder(object): - def __init__(self, *args, **kwargs): - self.super_args = args - self.super_kwargs = kwargs - - class SuperHasTraits(HasTraits, SuperRecorder): - i = Integer() - - obj = SuperHasTraits('a1', 'a2', b=10, i=5, c='x') - assert obj.i == 5 - assert not hasattr(obj, 'b') - assert not hasattr(obj, 'c') - assert obj.super_args == ('a1' , 'a2') - assert obj.super_kwargs == {'b': 10 , 'c': 'x'} - -def test_super_bad_args(): - class SuperHasTraits(HasTraits): - a = Integer() + +class Pickleable(HasTraits): + + i = Int() + @observe('i') + def _i_changed(self, change): pass + @validate('i') + def _i_validate(self, commit): + return commit['value'] + + j = Int() + + def __init__(self): + with self.hold_trait_notifications(): + self.i = 1 + self.on_trait_change(self._i_changed, 'i') + +def test_pickle_hastraits(): + c = Pickleable() + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(c, protocol) + c2 = pickle.loads(p) + assert c2.i == c.i + assert c2.j == c.j + + c.i = 5 + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + p = pickle.dumps(c, protocol) + c2 = pickle.loads(p) + assert c2.i == c.i + assert c2.j == c.j + + +def test_hold_trait_notifications(): + changes = [] + + class Test(HasTraits): + a = Integer(0) + b = Integer(0) + + def _a_changed(self, name, old, new): + changes.append((old, new)) + + def _b_validate(self, value, trait): + if value != 0: + raise TraitError('Only 0 is a valid value') + return value + + # Test context manager and nesting + t = Test() + with t.hold_trait_notifications(): + with t.hold_trait_notifications(): + t.a = 1 + assert t.a == 1 + assert changes == [] + t.a = 2 + assert t.a == 2 + with t.hold_trait_notifications(): + t.a = 3 + assert t.a == 3 + assert changes == [] + t.a = 4 + assert t.a == 4 + assert changes == [] + t.a = 4 + assert t.a == 4 + assert changes == [] + + assert changes == [(0, 4)] + # Test roll-back + try: + with t.hold_trait_notifications(): + t.b = 1 # raises a Trait error + except: + pass + assert t.b == 0 + + +class RollBack(HasTraits): + bar = Int() + def _bar_validate(self, value, trait): + if value: + raise TraitError('foobar') + return value + + +class TestRollback(TestCase): + + def test_roll_back(self): + + def assign_rollback(): + RollBack(bar=1) + + self.assertRaises(TraitError, assign_rollback) + + +class CacheModification(HasTraits): + foo = Int() + bar = Int() + + def _bar_validate(self, value, trait): + self.foo = value + return value + + def _foo_validate(self, value, trait): + self.bar = value + return value + + +def test_cache_modification(): + CacheModification(foo=1) + CacheModification(bar=1) + + +class OrderTraits(HasTraits): + notified = Dict() + + a = Unicode() + b = Unicode() + c = Unicode() + d = Unicode() + e = Unicode() + f = Unicode() + g = Unicode() + h = Unicode() + i = Unicode() + j = Unicode() + k = Unicode() + l = Unicode() + + def _notify(self, name, old, new): + """check the value of all traits when each trait change is triggered + + This verifies that the values are not sensitive + to dict ordering when loaded from kwargs + """ + # check the value of the other traits + # when a given trait change notification fires + self.notified[name] = { + c: getattr(self, c) for c in 'abcdefghijkl' + } + + def __init__(self, **kwargs): + self.on_trait_change(self._notify) + super(OrderTraits, self).__init__(**kwargs) + +def test_notification_order(): + d = {c:c for c in 'abcdefghijkl'} + obj = OrderTraits() + assert obj.notified == {} + obj = OrderTraits(**d) + notifications = { + c: d for c in 'abcdefghijkl' + } + assert obj.notified == notifications + + + +### +# Traits for Forward Declaration Tests +### +class ForwardDeclaredInstanceTrait(HasTraits): + + value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True) + +class ForwardDeclaredTypeTrait(HasTraits): + + value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True) + +class ForwardDeclaredInstanceListTrait(HasTraits): + + value = List(ForwardDeclaredInstance('ForwardDeclaredBar')) + +class ForwardDeclaredTypeListTrait(HasTraits): + + value = List(ForwardDeclaredType('ForwardDeclaredBar')) +### +# End Traits for Forward Declaration Tests +### + +### +# Classes for Forward Declaration Tests +### +class ForwardDeclaredBar(object): + pass + +class ForwardDeclaredBarSub(ForwardDeclaredBar): + pass +### +# End Classes for Forward Declaration Tests +### + +### +# Forward Declaration Tests +### +class TestForwardDeclaredInstanceTrait(TraitTestBase): + + obj = ForwardDeclaredInstanceTrait() + _default_value = None + _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()] + _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub] + +class TestForwardDeclaredTypeTrait(TraitTestBase): + + obj = ForwardDeclaredTypeTrait() + _default_value = None + _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub] + _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()] + +class TestForwardDeclaredInstanceList(TraitTestBase): + + obj = ForwardDeclaredInstanceListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) + + _default_value = [] + _good_values = [ + [ForwardDeclaredBar(), ForwardDeclaredBarSub()], + [], + ] + _bad_values = [ + ForwardDeclaredBar(), + [ForwardDeclaredBar(), 3, None], + '1', + # Note that this is the type, not an instance. + [ForwardDeclaredBar], + [None], + None, + ] + +class TestForwardDeclaredTypeList(TraitTestBase): + + obj = ForwardDeclaredTypeListTrait() + + def test_klass(self): + """Test that the instance klass is properly assigned.""" + self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar) + + _default_value = [] + _good_values = [ + [ForwardDeclaredBar, ForwardDeclaredBarSub], + [], + ] + _bad_values = [ + ForwardDeclaredBar, + [ForwardDeclaredBar, 3], + '1', + # Note that this is an instance, not the type. + [ForwardDeclaredBar()], + [None], + None, + ] +### +# End Forward Declaration Tests +### + +class TestDynamicTraits(TestCase): + + def setUp(self): + self._notify1 = [] + + def notify1(self, name, old, new): + self._notify1.append((name, old, new)) + + def test_notify_all(self): + + class A(HasTraits): + pass + + a = A() + self.assertTrue(not hasattr(a, 'x')) + self.assertTrue(not hasattr(a, 'y')) + + # Dynamically add trait x. + a.add_traits(x=Int()) + self.assertTrue(hasattr(a, 'x')) + self.assertTrue(isinstance(a, (A, ))) + + # Dynamically add trait y. + a.add_traits(y=Float()) + self.assertTrue(hasattr(a, 'y')) + self.assertTrue(isinstance(a, (A, ))) + self.assertEqual(a.__class__.__name__, A.__name__) + + # Create a new instance and verify that x and y + # aren't defined. + b = A() + self.assertTrue(not hasattr(b, 'x')) + self.assertTrue(not hasattr(b, 'y')) + + # Verify that notification works like normal. + a.on_trait_change(self.notify1) + a.x = 0 + self.assertEqual(len(self._notify1), 0) + a.y = 0.0 + self.assertEqual(len(self._notify1), 0) + a.x = 10 + self.assertTrue(('x', 0, 10) in self._notify1) + a.y = 10.0 + self.assertTrue(('y', 0.0, 10.0) in self._notify1) + self.assertRaises(TraitError, setattr, a, 'x', 'bad string') + self.assertRaises(TraitError, setattr, a, 'y', 'bad string') + self._notify1 = [] + a.on_trait_change(self.notify1, remove=True) + a.x = 20 + a.y = 20.0 + self.assertEqual(len(self._notify1), 0) + + +def test_enum_no_default(): + class C(HasTraits): + t = Enum(['a', 'b']) + + c = C() + c.t = 'a' + assert c.t == 'a' + + c = C() + + with pytest.raises(TraitError): + t = c.t + + c = C(t='b') + assert c.t == 'b' + + +def test_default_value_repr(): + class C(HasTraits): + t = Type('traitlets.HasTraits') + t2 = Type(HasTraits) + n = Integer(0) + lis = List() + d = Dict() + + assert C.t.default_value_repr() == "'traitlets.HasTraits'" + assert C.t2.default_value_repr() == "'traitlets.traitlets.HasTraits'" + assert C.n.default_value_repr() == '0' + assert C.lis.default_value_repr() == '[]' + assert C.d.default_value_repr() == '{}' + + +class TransitionalClass(HasTraits): + + d = Any() + @default('d') + def _d_default(self): + return TransitionalClass + + parent_super = False + calls_super = Integer(0) + + @default('calls_super') + def _calls_super_default(self): + return -1 + + @observe('calls_super') + @observe_compat + def _calls_super_changed(self, change): + self.parent_super = change + + parent_override = False + overrides = Integer(0) + + @observe('overrides') + @observe_compat + def _overrides_changed(self, change): + self.parent_override = change + + +class SubClass(TransitionalClass): + def _d_default(self): + return SubClass + + subclass_super = False + def _calls_super_changed(self, name, old, new): + self.subclass_super = True + super(SubClass, self)._calls_super_changed(name, old, new) + + subclass_override = False + def _overrides_changed(self, name, old, new): + self.subclass_override = True + + +def test_subclass_compat(): + obj = SubClass() + obj.calls_super = 5 + assert obj.parent_super + assert obj.subclass_super + obj.overrides = 5 + assert obj.subclass_override + assert not obj.parent_override + assert obj.d is SubClass + + +class DefinesHandler(HasTraits): + parent_called = False + + trait = Integer() + @observe('trait') + def handler(self, change): + self.parent_called = True + + +class OverridesHandler(DefinesHandler): + child_called = False + + @observe('trait') + def handler(self, change): + self.child_called = True + + +def test_subclass_override_observer(): + obj = OverridesHandler() + obj.trait = 5 + assert obj.child_called + assert not obj.parent_called + + +class DoesntRegisterHandler(DefinesHandler): + child_called = False + + def handler(self, change): + self.child_called = True + + +def test_subclass_override_not_registered(): + """Subclass that overrides observer and doesn't re-register unregisters both""" + obj = DoesntRegisterHandler() + obj.trait = 5 + assert not obj.child_called + assert not obj.parent_called + + +class AddsHandler(DefinesHandler): + child_called = False + + @observe('trait') + def child_handler(self, change): + self.child_called = True + +def test_subclass_add_observer(): + obj = AddsHandler() + obj.trait = 5 + assert obj.child_called + assert obj.parent_called + + +def test_observe_iterables(): + + class C(HasTraits): + i = Integer() + s = Unicode() + + c = C() + recorded = {} + def record(change): + recorded['change'] = change + + # observe with names=set + c.observe(record, names={'i', 's'}) + c.i = 5 + assert recorded['change'].name == 'i' + assert recorded['change'].new == 5 + c.s = 'hi' + assert recorded['change'].name == 's' + assert recorded['change'].new == 'hi' + + # observe with names=custom container with iter, contains + class MyContainer(object): + def __init__(self, container): + self.container = container + + def __iter__(self): + return iter(self.container) + + def __contains__(self, key): + return key in self.container + + c.observe(record, names=MyContainer({'i', 's'})) + c.i = 10 + assert recorded['change'].name == 'i' + assert recorded['change'].new == 10 + c.s = 'ok' + assert recorded['change'].name == 's' + assert recorded['change'].new == 'ok' + + +def test_super_args(): + class SuperRecorder(object): + def __init__(self, *args, **kwargs): + self.super_args = args + self.super_kwargs = kwargs + + class SuperHasTraits(HasTraits, SuperRecorder): + i = Integer() + + obj = SuperHasTraits('a1', 'a2', b=10, i=5, c='x') + assert obj.i == 5 + assert not hasattr(obj, 'b') + assert not hasattr(obj, 'c') + assert obj.super_args == ('a1' , 'a2') + assert obj.super_kwargs == {'b': 10 , 'c': 'x'} + +def test_super_bad_args(): + class SuperHasTraits(HasTraits): + a = Integer() w = ["Passing unrecognized arguments"] - with expected_warnings(w): - obj = SuperHasTraits(a=1, b=2) + with expected_warnings(w): + obj = SuperHasTraits(a=1, b=2) assert obj.a == 1 - assert not hasattr(obj, 'b') + assert not hasattr(obj, 'b') def test_default_mro(): diff --git a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py b/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py index 6a074aadfb..769e830b33 100644 --- a/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py +++ b/contrib/python/traitlets/py3/traitlets/tests/test_traitlets_enum.py @@ -1,28 +1,28 @@ -# pylint: disable=missing-docstring, too-few-public-methods -""" -Test the trait-type ``UseEnum``. -""" - -import unittest -import enum +# pylint: disable=missing-docstring, too-few-public-methods +""" +Test the trait-type ``UseEnum``. +""" + +import unittest +import enum from traitlets import HasTraits, TraitError, Enum, UseEnum, CaselessStrEnum, FuzzyEnum - - -# ----------------------------------------------------------------------------- -# TEST SUPPORT: -# ----------------------------------------------------------------------------- - -class Color(enum.Enum): - red = 1 - green = 2 - blue = 3 - yellow = 4 - -class OtherColor(enum.Enum): - red = 0 - green = 1 - - + + +# ----------------------------------------------------------------------------- +# TEST SUPPORT: +# ----------------------------------------------------------------------------- + +class Color(enum.Enum): + red = 1 + green = 2 + blue = 3 + yellow = 4 + +class OtherColor(enum.Enum): + red = 0 + green = 1 + + class CSColor(enum.Enum): red = 1 Green = 2 @@ -33,161 +33,161 @@ class CSColor(enum.Enum): color_choices = 'red Green BLUE YeLLoW'.split() -# ----------------------------------------------------------------------------- -# TESTSUITE: -# ----------------------------------------------------------------------------- -class TestUseEnum(unittest.TestCase): - # pylint: disable=invalid-name - - class Example(HasTraits): - color = UseEnum(Color, help="Color enum") - - def test_assign_enum_value(self): - example = self.Example() - example.color = Color.green - self.assertEqual(example.color, Color.green) - - def test_assign_all_enum_values(self): - # pylint: disable=no-member - enum_values = [value for value in Color.__members__.values()] - for value in enum_values: - self.assertIsInstance(value, Color) - example = self.Example() - example.color = value - self.assertEqual(example.color, value) - self.assertIsInstance(value, Color) - - def test_assign_enum_value__with_other_enum_raises_error(self): - example = self.Example() - with self.assertRaises(TraitError): - example.color = OtherColor.green - - def test_assign_enum_name_1(self): - # -- CONVERT: string => Enum value (item) - example = self.Example() - example.color = "red" - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - # pylint: disable=no-member - enum_names = [enum_val.name for enum_val in Color.__members__.values()] - for value in enum_names: +# ----------------------------------------------------------------------------- +# TESTSUITE: +# ----------------------------------------------------------------------------- +class TestUseEnum(unittest.TestCase): + # pylint: disable=invalid-name + + class Example(HasTraits): + color = UseEnum(Color, help="Color enum") + + def test_assign_enum_value(self): + example = self.Example() + example.color = Color.green + self.assertEqual(example.color, Color.green) + + def test_assign_all_enum_values(self): + # pylint: disable=no-member + enum_values = [value for value in Color.__members__.values()] + for value in enum_values: + self.assertIsInstance(value, Color) + example = self.Example() + example.color = value + self.assertEqual(example.color, value) + self.assertIsInstance(value, Color) + + def test_assign_enum_value__with_other_enum_raises_error(self): + example = self.Example() + with self.assertRaises(TraitError): + example.color = OtherColor.green + + def test_assign_enum_name_1(self): + # -- CONVERT: string => Enum value (item) + example = self.Example() + example.color = "red" + self.assertEqual(example.color, Color.red) + + def test_assign_enum_value_name(self): + # -- CONVERT: string => Enum value (item) + # pylint: disable=no-member + enum_names = [enum_val.name for enum_val in Color.__members__.values()] + for value in enum_names: self.assertIsInstance(value, str) - example = self.Example() - enum_value = Color.__members__.get(value) - example.color = value - self.assertIs(example.color, enum_value) - self.assertEqual(example.color.name, value) - - def test_assign_scoped_enum_value_name(self): - # -- CONVERT: string => Enum value (item) - scoped_names = ["Color.red", "Color.green", "Color.blue", "Color.yellow"] - for value in scoped_names: - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) + example = self.Example() + enum_value = Color.__members__.get(value) + example.color = value + self.assertIs(example.color, enum_value) + self.assertEqual(example.color.name, value) + + def test_assign_scoped_enum_value_name(self): + # -- CONVERT: string => Enum value (item) + scoped_names = ["Color.red", "Color.green", "Color.blue", "Color.yellow"] + for value in scoped_names: + example = self.Example() + example.color = value + self.assertIsInstance(example.color, Color) self.assertEqual(str(example.color), value) - - def test_assign_bad_enum_value_name__raises_error(self): - # -- CONVERT: string => Enum value (item) - bad_enum_names = ["UNKNOWN_COLOR", "RED", "Green", "blue2"] - for value in bad_enum_names: - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_assign_enum_value_number_1(self): - # -- CONVERT: number => Enum value (item) - example = self.Example() - example.color = 1 # == Color.red.value - example.color = Color.red.value - self.assertEqual(example.color, Color.red) - - def test_assign_enum_value_number(self): - # -- CONVERT: number => Enum value (item) - # pylint: disable=no-member - enum_numbers = [enum_val.value - for enum_val in Color.__members__.values()] - for value in enum_numbers: - self.assertIsInstance(value, int) - example = self.Example() - example.color = value - self.assertIsInstance(example.color, Color) - self.assertEqual(example.color.value, value) - - def test_assign_bad_enum_value_number__raises_error(self): - # -- CONVERT: number => Enum value (item) - bad_numbers = [-1, 0, 5] - for value in bad_numbers: - self.assertIsInstance(value, int) - assert UseEnum(Color).select_by_number(value, None) is None - example = self.Example() - with self.assertRaises(TraitError): - example.color = value - - def test_ctor_without_default_value(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color = UseEnum(Color) - - example = Example2() - self.assertEqual(example.color, Color.red) - - def test_ctor_with_default_value_as_enum_value(self): - # -- CONVERT: number => Enum value (item) - class Example2(HasTraits): - color = UseEnum(Color, default_value=Color.green) - - example = Example2() - self.assertEqual(example.color, Color.green) - - - def test_ctor_with_default_value_none_and_not_allow_none(self): - # -- IMPLICIT: default_value = Color.red (first enum-value) - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=False) - color2 = UseEnum(Color, default_value=None) - example = Example2() - self.assertEqual(example.color1, Color.red) - self.assertEqual(example.color2, Color.red) - - def test_ctor_with_default_value_none_and_allow_none(self): - class Example2(HasTraits): - color1 = UseEnum(Color, default_value=None, allow_none=True) - color2 = UseEnum(Color, allow_none=True) - - example = Example2() - self.assertIs(example.color1, None) - self.assertIs(example.color2, None) - - def test_assign_none_without_allow_none_resets_to_default_value(self): - class Example2(HasTraits): - color1 = UseEnum(Color, allow_none=False) - color2 = UseEnum(Color) - - example = Example2() - example.color1 = None - example.color2 = None - self.assertIs(example.color1, Color.red) - self.assertIs(example.color2, Color.red) - - def test_assign_none_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - example.color = None - self.assertIs(example.color, None) - - def test_assign_bad_value_with_to_enum_or_none(self): - class Example2(HasTraits): - color = UseEnum(Color, allow_none=True) - - example = Example2() - with self.assertRaises(TraitError): - example.color = "BAD_VALUE" - + + def test_assign_bad_enum_value_name__raises_error(self): + # -- CONVERT: string => Enum value (item) + bad_enum_names = ["UNKNOWN_COLOR", "RED", "Green", "blue2"] + for value in bad_enum_names: + example = self.Example() + with self.assertRaises(TraitError): + example.color = value + + def test_assign_enum_value_number_1(self): + # -- CONVERT: number => Enum value (item) + example = self.Example() + example.color = 1 # == Color.red.value + example.color = Color.red.value + self.assertEqual(example.color, Color.red) + + def test_assign_enum_value_number(self): + # -- CONVERT: number => Enum value (item) + # pylint: disable=no-member + enum_numbers = [enum_val.value + for enum_val in Color.__members__.values()] + for value in enum_numbers: + self.assertIsInstance(value, int) + example = self.Example() + example.color = value + self.assertIsInstance(example.color, Color) + self.assertEqual(example.color.value, value) + + def test_assign_bad_enum_value_number__raises_error(self): + # -- CONVERT: number => Enum value (item) + bad_numbers = [-1, 0, 5] + for value in bad_numbers: + self.assertIsInstance(value, int) + assert UseEnum(Color).select_by_number(value, None) is None + example = self.Example() + with self.assertRaises(TraitError): + example.color = value + + def test_ctor_without_default_value(self): + # -- IMPLICIT: default_value = Color.red (first enum-value) + class Example2(HasTraits): + color = UseEnum(Color) + + example = Example2() + self.assertEqual(example.color, Color.red) + + def test_ctor_with_default_value_as_enum_value(self): + # -- CONVERT: number => Enum value (item) + class Example2(HasTraits): + color = UseEnum(Color, default_value=Color.green) + + example = Example2() + self.assertEqual(example.color, Color.green) + + + def test_ctor_with_default_value_none_and_not_allow_none(self): + # -- IMPLICIT: default_value = Color.red (first enum-value) + class Example2(HasTraits): + color1 = UseEnum(Color, default_value=None, allow_none=False) + color2 = UseEnum(Color, default_value=None) + example = Example2() + self.assertEqual(example.color1, Color.red) + self.assertEqual(example.color2, Color.red) + + def test_ctor_with_default_value_none_and_allow_none(self): + class Example2(HasTraits): + color1 = UseEnum(Color, default_value=None, allow_none=True) + color2 = UseEnum(Color, allow_none=True) + + example = Example2() + self.assertIs(example.color1, None) + self.assertIs(example.color2, None) + + def test_assign_none_without_allow_none_resets_to_default_value(self): + class Example2(HasTraits): + color1 = UseEnum(Color, allow_none=False) + color2 = UseEnum(Color) + + example = Example2() + example.color1 = None + example.color2 = None + self.assertIs(example.color1, Color.red) + self.assertIs(example.color2, Color.red) + + def test_assign_none_to_enum_or_none(self): + class Example2(HasTraits): + color = UseEnum(Color, allow_none=True) + + example = Example2() + example.color = None + self.assertIs(example.color, None) + + def test_assign_bad_value_with_to_enum_or_none(self): + class Example2(HasTraits): + color = UseEnum(Color, allow_none=True) + + example = Example2() + with self.assertRaises(TraitError): + example.color = "BAD_VALUE" + def test_info(self): import sys diff --git a/contrib/python/traitlets/py3/traitlets/tests/utils.py b/contrib/python/traitlets/py3/traitlets/tests/utils.py index 7cb76bfa2c..c5ac591435 100644 --- a/contrib/python/traitlets/py3/traitlets/tests/utils.py +++ b/contrib/python/traitlets/py3/traitlets/tests/utils.py @@ -1,42 +1,42 @@ from subprocess import Popen, PIPE import sys -import os - - -def get_output_error_code(cmd): - """Get stdout, stderr, and exit code from running a command""" - env = os.environ.copy() - env['Y_PYTHON_ENTRY_POINT'] = ':main' - p = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) - out, err = p.communicate() - out = out.decode('utf8', 'replace') - err = err.decode('utf8', 'replace') - return out, err, p.returncode - - -def check_help_output(pkg, subcommand=None): - """test that `python -m PKG [subcommand] -h` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('-h') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out - assert "--help-all" in out - return out, err - - -def check_help_all_output(pkg, subcommand=None): - """test that `python -m PKG --help-all` works""" - cmd = [sys.executable, '-m', pkg] - if subcommand: - cmd.extend(subcommand) - cmd.append('--help-all') - out, err, rc = get_output_error_code(cmd) - assert rc == 0, err - assert "Traceback" not in err - assert "Options" in out +import os + + +def get_output_error_code(cmd): + """Get stdout, stderr, and exit code from running a command""" + env = os.environ.copy() + env['Y_PYTHON_ENTRY_POINT'] = ':main' + p = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) + out, err = p.communicate() + out = out.decode('utf8', 'replace') + err = err.decode('utf8', 'replace') + return out, err, p.returncode + + +def check_help_output(pkg, subcommand=None): + """test that `python -m PKG [subcommand] -h` works""" + cmd = [sys.executable, '-m', pkg] + if subcommand: + cmd.extend(subcommand) + cmd.append('-h') + out, err, rc = get_output_error_code(cmd) + assert rc == 0, err + assert "Traceback" not in err + assert "Options" in out + assert "--help-all" in out + return out, err + + +def check_help_all_output(pkg, subcommand=None): + """test that `python -m PKG --help-all` works""" + cmd = [sys.executable, '-m', pkg] + if subcommand: + cmd.extend(subcommand) + cmd.append('--help-all') + out, err, rc = get_output_error_code(cmd) + assert rc == 0, err + assert "Traceback" not in err + assert "Options" in out assert "Class options" in out - return out, err + return out, err diff --git a/contrib/python/traitlets/py3/ya.make b/contrib/python/traitlets/py3/ya.make index ea546d53e4..46980f21b3 100644 --- a/contrib/python/traitlets/py3/ya.make +++ b/contrib/python/traitlets/py3/ya.make @@ -44,7 +44,7 @@ RESOURCE_FILES( ) END() - -RECURSE_FOR_TESTS( + +RECURSE_FOR_TESTS( tests -) +) diff --git a/contrib/python/traitlets/ya.make b/contrib/python/traitlets/ya.make index 97655f153c..3156aae8c5 100644 --- a/contrib/python/traitlets/ya.make +++ b/contrib/python/traitlets/ya.make @@ -13,8 +13,8 @@ ENDIF() NO_LINT() END() - + RECURSE( py2 py3 -) +) |