##// END OF EJS Templates
Implementation in TraitType directly.
zah -
Show More
@@ -16,7 +16,7 b' import nose.tools as nt'
16 from nose import SkipTest
16 from nose import SkipTest
17
17
18 from IPython.utils.traitlets import (
18 from IPython.utils.traitlets import (
19 HasTraits, MetaHasTraits, TraitType, AllowNone, Any, CBytes, Dict,
19 HasTraits, MetaHasTraits, TraitType, Any, CBytes, Dict,
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
22 ObjectName, DottedObjectName, CRegExp, link
@@ -73,7 +73,7 b' class TestTraitType(TestCase):'
73 self.assertEqual(a.tt, -1)
73 self.assertEqual(a.tt, -1)
74
74
75 def test_default_validate(self):
75 def test_default_validate(self):
76 class MyIntTT(AllowNone):
76 class MyIntTT(TraitType):
77 def validate(self, obj, value):
77 def validate(self, obj, value):
78 if isinstance(value, int):
78 if isinstance(value, int):
79 return value
79 return value
@@ -354,29 +354,29 b' class TestHasTraitsNotify(TestCase):'
354
354
355 class A(HasTraits):
355 class A(HasTraits):
356 listen_to = ['a']
356 listen_to = ['a']
357
357
358 a = Int(0)
358 a = Int(0)
359 b = 0
359 b = 0
360
360
361 def __init__(self, **kwargs):
361 def __init__(self, **kwargs):
362 super(A, self).__init__(**kwargs)
362 super(A, self).__init__(**kwargs)
363 self.on_trait_change(self.listener1, ['a'])
363 self.on_trait_change(self.listener1, ['a'])
364
364
365 def listener1(self, name, old, new):
365 def listener1(self, name, old, new):
366 self.b += 1
366 self.b += 1
367
367
368 class B(A):
368 class B(A):
369
369
370 c = 0
370 c = 0
371 d = 0
371 d = 0
372
372
373 def __init__(self, **kwargs):
373 def __init__(self, **kwargs):
374 super(B, self).__init__(**kwargs)
374 super(B, self).__init__(**kwargs)
375 self.on_trait_change(self.listener2)
375 self.on_trait_change(self.listener2)
376
376
377 def listener2(self, name, old, new):
377 def listener2(self, name, old, new):
378 self.c += 1
378 self.c += 1
379
379
380 def _a_changed(self, name, old, new):
380 def _a_changed(self, name, old, new):
381 self.d += 1
381 self.d += 1
382
382
@@ -442,7 +442,7 b' class TestHasTraits(TestCase):'
442 def __init__(self, i):
442 def __init__(self, i):
443 super(A, self).__init__()
443 super(A, self).__init__()
444 self.i = i
444 self.i = i
445
445
446 a = A(5)
446 a = A(5)
447 self.assertEqual(a.i, 5)
447 self.assertEqual(a.i, 5)
448 # should raise TypeError if no positional arg given
448 # should raise TypeError if no positional arg given
@@ -677,19 +677,19 b' class TraitTestBase(TestCase):'
677 if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and
677 if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and
678 None in self._bad_values):
678 None in self._bad_values):
679 trait=self.obj.traits()['value']
679 trait=self.obj.traits()['value']
680 if isinstance(trait, AllowNone) and not trait._allow_none:
680 try:
681 try:
681 trait.allow_none = True
682 trait._allow_none = True
682 self._bad_values.remove(None)
683 self._bad_values.remove(None)
683 #skip coerce. Allow None casts None to None.
684 #skip coerce. Allow None casts None to None.
684 self.assign(None)
685 self.assign(None)
685 self.assertEqual(self.obj.value,None)
686 self.assertEqual(self.obj.value,None)
686 self.test_good_values()
687 self.test_good_values()
687 self.test_bad_values()
688 self.test_bad_values()
688 finally:
689 finally:
689 #tear down
690 #tear down
690 trait.allow_none = False
691 trait._allow_none = False
691 self._bad_values.append(None)
692 self._bad_values.append(None)
692 print "bad values %s" % self
693
693
694
694
695 def tearDown(self):
695 def tearDown(self):
@@ -894,7 +894,7 b' class TestList(TraitTestBase):'
894 _default_value = []
894 _default_value = []
895 _good_values = [[], [1], list(range(10)), (1,2)]
895 _good_values = [[], [1], list(range(10)), (1,2)]
896 _bad_values = [10, [1,'a'], 'a']
896 _bad_values = [10, [1,'a'], 'a']
897
897
898 def coerce(self, value):
898 def coerce(self, value):
899 if value is not None:
899 if value is not None:
900 value = list(value)
900 value = list(value)
@@ -1073,7 +1073,7 b' class TestLink(TestCase):'
1073 count = Int()
1073 count = Int()
1074 a = A(value=9)
1074 a = A(value=9)
1075 b = B(count=8)
1075 b = B(count=8)
1076
1076
1077 # Register callbacks that count.
1077 # Register callbacks that count.
1078 callback_count = []
1078 callback_count = []
1079 def a_callback(name, old, new):
1079 def a_callback(name, old, new):
@@ -1124,4 +1124,4 b' def test_pickle_hastraits():'
1124 c2 = pickle.loads(p)
1124 c2 = pickle.loads(p)
1125 nt.assert_equal(c2.i, c.i)
1125 nt.assert_equal(c2.i, c.i)
1126 nt.assert_equal(c2.j, c.j)
1126 nt.assert_equal(c2.j, c.j)
1127
1127 No newline at end of file
@@ -220,7 +220,7 b' class link(object):'
220 for obj,attr in self.objects.keys():
220 for obj,attr in self.objects.keys():
221 if obj is not sending_obj or attr != sending_attr:
221 if obj is not sending_obj or attr != sending_attr:
222 setattr(obj, attr, new)
222 setattr(obj, attr, new)
223
223
224 def unlink(self):
224 def unlink(self):
225 for key, callback in self.objects.items():
225 for key, callback in self.objects.items():
226 (obj,attr) = key
226 (obj,attr) = key
@@ -252,13 +252,16 b' class TraitType(object):'
252
252
253 metadata = {}
253 metadata = {}
254 default_value = Undefined
254 default_value = Undefined
255 allow_none = False
255 info_text = 'any value'
256 info_text = 'any value'
256
257
257 def __init__(self, default_value=NoDefaultSpecified, **metadata):
258 def __init__(self, default_value=NoDefaultSpecified, allow_none=None, **metadata):
258 """Create a TraitType.
259 """Create a TraitType.
259 """
260 """
260 if default_value is not NoDefaultSpecified:
261 if default_value is not NoDefaultSpecified:
261 self.default_value = default_value
262 self.default_value = default_value
263 if allow_none is not None:
264 self.allow_none = allow_none
262
265
263 if len(metadata) > 0:
266 if len(metadata) > 0:
264 if len(self.metadata) > 0:
267 if len(self.metadata) > 0:
@@ -371,6 +374,8 b' class TraitType(object):'
371 obj._notify_trait(self.name, old_value, new_value)
374 obj._notify_trait(self.name, old_value, new_value)
372
375
373 def _validate(self, obj, value):
376 def _validate(self, obj, value):
377 if value is None and self.allow_none:
378 return value
374 if hasattr(self, 'validate'):
379 if hasattr(self, 'validate'):
375 return self.validate(obj, value)
380 return self.validate(obj, value)
376 elif hasattr(self, 'is_valid_for'):
381 elif hasattr(self, 'is_valid_for'):
@@ -677,29 +682,16 b' class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):'
677 else:
682 else:
678 return trait.get_metadata(key)
683 return trait.get_metadata(key)
679
684
680 class AllowNone(TraitType):
681 """A trait that can be set to allow None values. It does not provide
682 validation."""
683 def __init__(self, default_value=NoDefaultSpecified, allow_none = False, **metadata):
684 self._allow_none = allow_none
685 super(AllowNone, self).__init__(default_value, **metadata)
686
687 def _none_ok(self, value):
688 """The validate method can return the None value."""
689 return value is None and self._allow_none
690
691
692 #-----------------------------------------------------------------------------
685 #-----------------------------------------------------------------------------
693 # Actual TraitTypes implementations/subclasses
686 # Actual TraitTypes implementations/subclasses
694 #-----------------------------------------------------------------------------
687 #-----------------------------------------------------------------------------
695
688
696
697 #-----------------------------------------------------------------------------
689 #-----------------------------------------------------------------------------
698 # TraitTypes subclasses for handling classes and instances of classes
690 # TraitTypes subclasses for handling classes and instances of classes
699 #-----------------------------------------------------------------------------
691 #-----------------------------------------------------------------------------
700
692
701
693
702 class ClassBasedTraitType(AllowNone):
694 class ClassBasedTraitType(TraitType):
703 """A trait with error reporting for Type, Instance and This."""
695 """A trait with error reporting for Type, Instance and This."""
704
696
705 def error(self, obj, value):
697 def error(self, obj, value):
@@ -767,8 +759,7 b' class Type(ClassBasedTraitType):'
767 if issubclass(value, self.klass):
759 if issubclass(value, self.klass):
768 return value
760 return value
769 except:
761 except:
770 if self._none_ok(value):
762 pass
771 return value
772
763
773 self.error(obj, value)
764 self.error(obj, value)
774
765
@@ -779,7 +770,7 b' class Type(ClassBasedTraitType):'
779 else:
770 else:
780 klass = self.klass.__name__
771 klass = self.klass.__name__
781 result = 'a subclass of ' + klass
772 result = 'a subclass of ' + klass
782 if self._allow_none:
773 if self.allow_none:
783 return result + ' or None'
774 return result + ' or None'
784 return result
775 return result
785
776
@@ -869,11 +860,6 b' class Instance(ClassBasedTraitType):'
869 super(Instance, self).__init__(default_value, allow_none, **metadata)
860 super(Instance, self).__init__(default_value, allow_none, **metadata)
870
861
871 def validate(self, obj, value):
862 def validate(self, obj, value):
872 if value is None:
873 if self._allow_none:
874 return value
875 self.error(obj, value)
876
877 if isinstance(value, self.klass):
863 if isinstance(value, self.klass):
878 return value
864 return value
879 else:
865 else:
@@ -885,7 +871,7 b' class Instance(ClassBasedTraitType):'
885 else:
871 else:
886 klass = self.klass.__name__
872 klass = self.klass.__name__
887 result = class_of(klass)
873 result = class_of(klass)
888 if self._allow_none:
874 if self.allow_none:
889 return result + ' or None'
875 return result + ' or None'
890
876
891 return result
877 return result
@@ -945,14 +931,14 b' class Any(TraitType):'
945 info_text = 'any value'
931 info_text = 'any value'
946
932
947
933
948 class Int(AllowNone):
934 class Int(TraitType):
949 """An int trait."""
935 """An int trait."""
950
936
951 default_value = 0
937 default_value = 0
952 info_text = 'an int'
938 info_text = 'an int'
953
939
954 def validate(self, obj, value):
940 def validate(self, obj, value):
955 if isinstance(value, int) or self._none_ok(value):
941 if isinstance(value, int):
956 return value
942 return value
957 self.error(obj, value)
943 self.error(obj, value)
958
944
@@ -963,22 +949,20 b' class CInt(Int):'
963 try:
949 try:
964 return int(value)
950 return int(value)
965 except:
951 except:
966 if self._none_ok(value):
967 return value
968 self.error(obj, value)
952 self.error(obj, value)
969
953
970 if py3compat.PY3:
954 if py3compat.PY3:
971 Long, CLong = Int, CInt
955 Long, CLong = Int, CInt
972 Integer = Int
956 Integer = Int
973 else:
957 else:
974 class Long(AllowNone):
958 class Long(TraitType):
975 """A long integer trait."""
959 """A long integer trait."""
976
960
977 default_value = 0
961 default_value = 0
978 info_text = 'a long'
962 info_text = 'a long'
979
963
980 def validate(self, obj, value):
964 def validate(self, obj, value):
981 if isinstance(value, long) or self._none_ok(value):
965 if isinstance(value, long):
982 return value
966 return value
983 if isinstance(value, int):
967 if isinstance(value, int):
984 return long(value)
968 return long(value)
@@ -992,11 +976,9 b' else:'
992 try:
976 try:
993 return long(value)
977 return long(value)
994 except:
978 except:
995 if self._none_ok(value):
996 return value
997 self.error(obj, value)
979 self.error(obj, value)
998
980
999 class Integer(AllowNone):
981 class Integer(TraitType):
1000 """An integer trait.
982 """An integer trait.
1001
983
1002 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
984 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
@@ -1005,7 +987,7 b' else:'
1005 info_text = 'an integer'
987 info_text = 'an integer'
1006
988
1007 def validate(self, obj, value):
989 def validate(self, obj, value):
1008 if isinstance(value, int) or self._none_ok(value):
990 if isinstance(value, int):
1009 return value
991 return value
1010 if isinstance(value, long):
992 if isinstance(value, long):
1011 # downcast longs that fit in int:
993 # downcast longs that fit in int:
@@ -1019,14 +1001,14 b' else:'
1019 self.error(obj, value)
1001 self.error(obj, value)
1020
1002
1021
1003
1022 class Float(AllowNone):
1004 class Float(TraitType):
1023 """A float trait."""
1005 """A float trait."""
1024
1006
1025 default_value = 0.0
1007 default_value = 0.0
1026 info_text = 'a float'
1008 info_text = 'a float'
1027
1009
1028 def validate(self, obj, value):
1010 def validate(self, obj, value):
1029 if isinstance(value, float ) or self._none_ok(value):
1011 if isinstance(value, float):
1030 return value
1012 return value
1031 if isinstance(value, int):
1013 if isinstance(value, int):
1032 return float(value)
1014 return float(value)
@@ -1040,18 +1022,16 b' class CFloat(Float):'
1040 try:
1022 try:
1041 return float(value)
1023 return float(value)
1042 except:
1024 except:
1043 if self._none_ok(value):
1044 return value
1045 self.error(obj, value)
1025 self.error(obj, value)
1046
1026
1047 class Complex(AllowNone):
1027 class Complex(TraitType):
1048 """A trait for complex numbers."""
1028 """A trait for complex numbers."""
1049
1029
1050 default_value = 0.0 + 0.0j
1030 default_value = 0.0 + 0.0j
1051 info_text = 'a complex number'
1031 info_text = 'a complex number'
1052
1032
1053 def validate(self, obj, value):
1033 def validate(self, obj, value):
1054 if isinstance(value, complex) or self._none_ok(value):
1034 if isinstance(value, complex):
1055 return value
1035 return value
1056 if isinstance(value, (float, int)):
1036 if isinstance(value, (float, int)):
1057 return complex(value)
1037 return complex(value)
@@ -1065,21 +1045,19 b' class CComplex(Complex):'
1065 try:
1045 try:
1066 return complex(value)
1046 return complex(value)
1067 except:
1047 except:
1068 if self._noe_ok(value):
1069 return value
1070 self.error(obj, value)
1048 self.error(obj, value)
1071
1049
1072 # We should always be explicit about whether we're using bytes or unicode, both
1050 # We should always be explicit about whether we're using bytes or unicode, both
1073 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1051 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1074 # we don't have a Str type.
1052 # we don't have a Str type.
1075 class Bytes(AllowNone):
1053 class Bytes(TraitType):
1076 """A trait for byte strings."""
1054 """A trait for byte strings."""
1077
1055
1078 default_value = b''
1056 default_value = b''
1079 info_text = 'a bytes object'
1057 info_text = 'a bytes object'
1080
1058
1081 def validate(self, obj, value):
1059 def validate(self, obj, value):
1082 if isinstance(value, bytes) or self._none_ok(value):
1060 if isinstance(value, bytes):
1083 return value
1061 return value
1084 self.error(obj, value)
1062 self.error(obj, value)
1085
1063
@@ -1091,19 +1069,17 b' class CBytes(Bytes):'
1091 try:
1069 try:
1092 return bytes(value)
1070 return bytes(value)
1093 except:
1071 except:
1094 if self._none_ok(value):
1095 return value
1096 self.error(obj, value)
1072 self.error(obj, value)
1097
1073
1098
1074
1099 class Unicode(AllowNone):
1075 class Unicode(TraitType):
1100 """A trait for unicode strings."""
1076 """A trait for unicode strings."""
1101
1077
1102 default_value = u''
1078 default_value = u''
1103 info_text = 'a unicode string'
1079 info_text = 'a unicode string'
1104
1080
1105 def validate(self, obj, value):
1081 def validate(self, obj, value):
1106 if isinstance(value, py3compat.unicode_type) or self._none_ok(value):
1082 if isinstance(value, py3compat.unicode_type):
1107 return value
1083 return value
1108 if isinstance(value, bytes):
1084 if isinstance(value, bytes):
1109 try:
1085 try:
@@ -1121,12 +1097,10 b' class CUnicode(Unicode):'
1121 try:
1097 try:
1122 return py3compat.unicode_type(value)
1098 return py3compat.unicode_type(value)
1123 except:
1099 except:
1124 if self._allow_none(value):
1125 return value
1126 self.error(obj, value)
1100 self.error(obj, value)
1127
1101
1128
1102
1129 class ObjectName(AllowNone):
1103 class ObjectName(TraitType):
1130 """A string holding a valid object name in this version of Python.
1104 """A string holding a valid object name in this version of Python.
1131
1105
1132 This does not check that the name exists in any scope."""
1106 This does not check that the name exists in any scope."""
@@ -1148,8 +1122,6 b' class ObjectName(AllowNone):'
1148 return value
1122 return value
1149
1123
1150 def validate(self, obj, value):
1124 def validate(self, obj, value):
1151 if self._none_ok(value):
1152 return value
1153 value = self.coerce_str(obj, value)
1125 value = self.coerce_str(obj, value)
1154
1126
1155 if isinstance(value, str) and py3compat.isidentifier(value):
1127 if isinstance(value, str) and py3compat.isidentifier(value):
@@ -1159,8 +1131,6 b' class ObjectName(AllowNone):'
1159 class DottedObjectName(ObjectName):
1131 class DottedObjectName(ObjectName):
1160 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1132 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1161 def validate(self, obj, value):
1133 def validate(self, obj, value):
1162 if self._none_ok(value):
1163 return value
1164 value = self.coerce_str(obj, value)
1134 value = self.coerce_str(obj, value)
1165
1135
1166 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1136 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
@@ -1168,14 +1138,14 b' class DottedObjectName(ObjectName):'
1168 self.error(obj, value)
1138 self.error(obj, value)
1169
1139
1170
1140
1171 class Bool(AllowNone):
1141 class Bool(TraitType):
1172 """A boolean (True, False) trait."""
1142 """A boolean (True, False) trait."""
1173
1143
1174 default_value = False
1144 default_value = False
1175 info_text = 'a boolean'
1145 info_text = 'a boolean'
1176
1146
1177 def validate(self, obj, value):
1147 def validate(self, obj, value):
1178 if isinstance(value, bool) or self._none_ok(value):
1148 if isinstance(value, bool):
1179 return value
1149 return value
1180 self.error(obj, value)
1150 self.error(obj, value)
1181
1151
@@ -1195,14 +1165,9 b' class Enum(TraitType):'
1195
1165
1196 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1166 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1197 self.values = values
1167 self.values = values
1198 self._allow_none = allow_none
1168 super(Enum, self).__init__(default_value, allow_none, **metadata)
1199 super(Enum, self).__init__(default_value, **metadata)
1200
1169
1201 def validate(self, obj, value):
1170 def validate(self, obj, value):
1202 if value is None:
1203 if self._allow_none:
1204 return value
1205
1206 if value in self.values:
1171 if value in self.values:
1207 return value
1172 return value
1208 self.error(obj, value)
1173 self.error(obj, value)
@@ -1210,7 +1175,7 b' class Enum(TraitType):'
1210 def info(self):
1175 def info(self):
1211 """ Returns a description of the trait."""
1176 """ Returns a description of the trait."""
1212 result = 'any of ' + repr(self.values)
1177 result = 'any of ' + repr(self.values)
1213 if self._allow_none:
1178 if self.allow_none:
1214 return result + ' or None'
1179 return result + ' or None'
1215 return result
1180 return result
1216
1181
@@ -1218,10 +1183,6 b' class CaselessStrEnum(Enum):'
1218 """An enum of strings that are caseless in validate."""
1183 """An enum of strings that are caseless in validate."""
1219
1184
1220 def validate(self, obj, value):
1185 def validate(self, obj, value):
1221 if value is None:
1222 if self._allow_none:
1223 return value
1224
1225 if not isinstance(value, py3compat.string_types):
1186 if not isinstance(value, py3compat.string_types):
1226 self.error(obj, value)
1187 self.error(obj, value)
1227
1188
@@ -1384,7 +1345,7 b' class List(Container):'
1384 self.length_error(obj, value)
1345 self.length_error(obj, value)
1385
1346
1386 return super(List, self).validate_elements(obj, value)
1347 return super(List, self).validate_elements(obj, value)
1387
1348
1388 def validate(self, obj, value):
1349 def validate(self, obj, value):
1389 value = super(List, self).validate(obj, value)
1350 value = super(List, self).validate(obj, value)
1390 if value is None:
1351 if value is None:
@@ -1393,7 +1354,7 b' class List(Container):'
1393 value = self.validate_elements(obj, value)
1354 value = self.validate_elements(obj, value)
1394
1355
1395 return value
1356 return value
1396
1357
1397
1358
1398
1359
1399 class Set(List):
1360 class Set(List):
@@ -1513,7 +1474,7 b' class Dict(Instance):'
1513 super(Dict,self).__init__(klass=dict, args=args,
1474 super(Dict,self).__init__(klass=dict, args=args,
1514 allow_none=allow_none, **metadata)
1475 allow_none=allow_none, **metadata)
1515
1476
1516 class TCPAddress(AllowNone):
1477 class TCPAddress(TraitType):
1517 """A trait for an (ip, port) tuple.
1478 """A trait for an (ip, port) tuple.
1518
1479
1519 This allows for both IPv4 IP addresses as well as hostnames.
1480 This allows for both IPv4 IP addresses as well as hostnames.
@@ -1529,11 +1490,9 b' class TCPAddress(AllowNone):'
1529 port = value[1]
1490 port = value[1]
1530 if port >= 0 and port <= 65535:
1491 if port >= 0 and port <= 65535:
1531 return value
1492 return value
1532 if self._none_ok(value):
1533 return value
1534 self.error(obj, value)
1493 self.error(obj, value)
1535
1494
1536 class CRegExp(AllowNone):
1495 class CRegExp(TraitType):
1537 """A casting compiled regular expression trait.
1496 """A casting compiled regular expression trait.
1538
1497
1539 Accepts both strings and compiled regular expressions. The resulting
1498 Accepts both strings and compiled regular expressions. The resulting
@@ -1545,6 +1504,4 b' class CRegExp(AllowNone):'
1545 try:
1504 try:
1546 return re.compile(value)
1505 return re.compile(value)
1547 except:
1506 except:
1548 if self._none_ok(value):
1549 return value
1550 self.error(obj, value)
1507 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now