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