##// END OF EJS Templates
Merge pull request #6892 from quantopian/recursive_traits...
Min RK -
r18843:0df1fb74 merge
parent child Browse files
Show More
@@ -20,7 +20,7 b' from IPython.utils.traitlets import ('
20 Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError,
20 Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError,
21 Undefined, Type, This, Instance, TCPAddress, List, Tuple,
21 Undefined, Type, This, Instance, TCPAddress, List, Tuple,
22 ObjectName, DottedObjectName, CRegExp, link, directional_link,
22 ObjectName, DottedObjectName, CRegExp, link, directional_link,
23 EventfulList, EventfulDict
23 EventfulList, EventfulDict, ForwardDeclaredType, ForwardDeclaredInstance,
24 )
24 )
25 from IPython.utils import py3compat
25 from IPython.utils import py3compat
26 from IPython.testing.decorators import skipif
26 from IPython.testing.decorators import skipif
@@ -684,6 +684,20 b' class TestThis(TestCase):'
684 self.assertEqual(f.t, b)
684 self.assertEqual(f.t, b)
685 self.assertRaises(TraitError, setattr, b, 't', f)
685 self.assertRaises(TraitError, setattr, b, 't', f)
686
686
687 def test_this_in_container(self):
688
689 class Tree(HasTraits):
690 value = Unicode()
691 leaves = List(This())
692
693 tree = Tree(
694 value='foo',
695 leaves=[Tree('bar'), Tree('buzz')]
696 )
697
698 with self.assertRaises(TraitError):
699 tree.leaves = [1, 2]
700
687 class TraitTestBase(TestCase):
701 class TraitTestBase(TestCase):
688 """A best testing class for basic trait types."""
702 """A best testing class for basic trait types."""
689
703
@@ -1306,3 +1320,103 b' class TestEventful(TestCase):'
1306
1320
1307 # Is the output correct?
1321 # Is the output correct?
1308 self.assertEqual(a.x, {c: c for c in 'bz'})
1322 self.assertEqual(a.x, {c: c for c in 'bz'})
1323
1324 ###
1325 # Traits for Forward Declaration Tests
1326 ###
1327 class ForwardDeclaredInstanceTrait(HasTraits):
1328
1329 value = ForwardDeclaredInstance('ForwardDeclaredBar')
1330
1331 class ForwardDeclaredTypeTrait(HasTraits):
1332
1333 value = ForwardDeclaredType('ForwardDeclaredBar')
1334
1335 class ForwardDeclaredInstanceListTrait(HasTraits):
1336
1337 value = List(ForwardDeclaredInstance('ForwardDeclaredBar'))
1338
1339 class ForwardDeclaredTypeListTrait(HasTraits):
1340
1341 value = List(ForwardDeclaredType('ForwardDeclaredBar'))
1342 ###
1343 # End Traits for Forward Declaration Tests
1344 ###
1345
1346 ###
1347 # Classes for Forward Declaration Tests
1348 ###
1349 class ForwardDeclaredBar(object):
1350 pass
1351
1352 class ForwardDeclaredBarSub(ForwardDeclaredBar):
1353 pass
1354 ###
1355 # End Classes for Forward Declaration Tests
1356 ###
1357
1358 ###
1359 # Forward Declaration Tests
1360 ###
1361 class TestForwardDeclaredInstanceTrait(TraitTestBase):
1362
1363 obj = ForwardDeclaredInstanceTrait()
1364 _default_value = None
1365 _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()]
1366 _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub]
1367
1368 class TestForwardDeclaredTypeTrait(TraitTestBase):
1369
1370 obj = ForwardDeclaredTypeTrait()
1371 _default_value = None
1372 _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub]
1373 _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()]
1374
1375 class TestForwardDeclaredInstanceList(TraitTestBase):
1376
1377 obj = ForwardDeclaredInstanceListTrait()
1378
1379 def test_klass(self):
1380 """Test that the instance klass is properly assigned."""
1381 self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar)
1382
1383 _default_value = []
1384 _good_values = [
1385 [ForwardDeclaredBar(), ForwardDeclaredBarSub(), None],
1386 [None],
1387 [],
1388 None,
1389 ]
1390 _bad_values = [
1391 ForwardDeclaredBar(),
1392 [ForwardDeclaredBar(), 3],
1393 '1',
1394 # Note that this is the type, not an instance.
1395 [ForwardDeclaredBar]
1396 ]
1397
1398 class TestForwardDeclaredTypeList(TraitTestBase):
1399
1400 obj = ForwardDeclaredTypeListTrait()
1401
1402 def test_klass(self):
1403 """Test that the instance klass is properly assigned."""
1404 self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar)
1405
1406 _default_value = []
1407 _good_values = [
1408 [ForwardDeclaredBar, ForwardDeclaredBarSub, None],
1409 [],
1410 [None],
1411 None,
1412 ]
1413 _bad_values = [
1414 ForwardDeclaredBar,
1415 [ForwardDeclaredBar, 3],
1416 '1',
1417 # Note that this is an instance, not the type.
1418 [ForwardDeclaredBar()]
1419 ]
1420 ###
1421 # End Forward Declaration Tests
1422 ###
@@ -748,7 +748,16 b' class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):'
748
748
749
749
750 class ClassBasedTraitType(TraitType):
750 class ClassBasedTraitType(TraitType):
751 """A trait with error reporting for Type, Instance and This."""
751 """
752 A trait with error reporting and string -> type resolution for Type,
753 Instance and This.
754 """
755
756 def _resolve_string(self, string):
757 """
758 Resolve a string supplied for a type into an actual object.
759 """
760 return import_item(string)
752
761
753 def error(self, obj, value):
762 def error(self, obj, value):
754 kind = type(value)
763 kind = type(value)
@@ -813,7 +822,7 b' class Type(ClassBasedTraitType):'
813 """Validates that the value is a valid object instance."""
822 """Validates that the value is a valid object instance."""
814 if isinstance(value, py3compat.string_types):
823 if isinstance(value, py3compat.string_types):
815 try:
824 try:
816 value = import_item(value)
825 value = self._resolve_string(value)
817 except ImportError:
826 except ImportError:
818 raise TraitError("The '%s' trait of %s instance must be a type, but "
827 raise TraitError("The '%s' trait of %s instance must be a type, but "
819 "%r could not be imported" % (self.name, obj, value))
828 "%r could not be imported" % (self.name, obj, value))
@@ -842,9 +851,9 b' class Type(ClassBasedTraitType):'
842
851
843 def _resolve_classes(self):
852 def _resolve_classes(self):
844 if isinstance(self.klass, py3compat.string_types):
853 if isinstance(self.klass, py3compat.string_types):
845 self.klass = import_item(self.klass)
854 self.klass = self._resolve_string(self.klass)
846 if isinstance(self.default_value, py3compat.string_types):
855 if isinstance(self.default_value, py3compat.string_types):
847 self.default_value = import_item(self.default_value)
856 self.default_value = self._resolve_string(self.default_value)
848
857
849 def get_default_value(self):
858 def get_default_value(self):
850 return self.default_value
859 return self.default_value
@@ -951,7 +960,7 b' class Instance(ClassBasedTraitType):'
951
960
952 def _resolve_classes(self):
961 def _resolve_classes(self):
953 if isinstance(self.klass, py3compat.string_types):
962 if isinstance(self.klass, py3compat.string_types):
954 self.klass = import_item(self.klass)
963 self.klass = self._resolve_string(self.klass)
955
964
956 def get_default_value(self):
965 def get_default_value(self):
957 """Instantiate a default value instance.
966 """Instantiate a default value instance.
@@ -967,6 +976,33 b' class Instance(ClassBasedTraitType):'
967 return dv
976 return dv
968
977
969
978
979 class ForwardDeclaredMixin(object):
980 """
981 Mixin for forward-declared versions of Instance and Type.
982 """
983 def _resolve_string(self, string):
984 """
985 Find the specified class name by looking for it in the module in which
986 our this_class attribute was defined.
987 """
988 modname = self.this_class.__module__
989 return import_item('.'.join([modname, string]))
990
991
992 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
993 """
994 Forward-declared version of Type.
995 """
996 pass
997
998
999 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1000 """
1001 Forward-declared version of Instance.
1002 """
1003 pass
1004
1005
970 class This(ClassBasedTraitType):
1006 class This(ClassBasedTraitType):
971 """A trait for instances of the class containing this trait.
1007 """A trait for instances of the class containing this trait.
972
1008
@@ -1354,8 +1390,10 b' class Container(Instance):'
1354 return self.klass(validated)
1390 return self.klass(validated)
1355
1391
1356 def instance_init(self, obj):
1392 def instance_init(self, obj):
1357 if isinstance(self._trait, Instance):
1393 if isinstance(self._trait, TraitType):
1358 self._trait._resolve_classes()
1394 self._trait.this_class = self.this_class
1395 if hasattr(self._trait, 'instance_init'):
1396 self._trait.instance_init(obj)
1359 super(Container, self).instance_init(obj)
1397 super(Container, self).instance_init(obj)
1360
1398
1361
1399
General Comments 0
You need to be logged in to leave comments. Login now