Show More
@@ -25,9 +25,9 Authors: | |||
|
25 | 25 | from unittest import TestCase |
|
26 | 26 | |
|
27 | 27 | from IPython.utils.traitlets import ( |
|
28 | HasTraits, MetaHasTraits, TraitType, Any, | |
|
28 | HasTraits, MetaHasTraits, TraitType, Any, CStr, | |
|
29 | 29 | Int, Long, Float, Complex, Str, Unicode, TraitError, |
|
30 | Undefined, Type, This, Instance, TCPAddress | |
|
30 | Undefined, Type, This, Instance, TCPAddress, List, Tuple | |
|
31 | 31 | ) |
|
32 | 32 | |
|
33 | 33 | |
@@ -741,3 +741,74 class TestTCPAddress(TraitTestBase): | |||
|
741 | 741 | _default_value = ('127.0.0.1',0) |
|
742 | 742 | _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)] |
|
743 | 743 | _bad_values = [(0,0),('localhost',10.0),('localhost',-1)] |
|
744 | ||
|
745 | class ListTrait(HasTraits): | |
|
746 | ||
|
747 | value = List(Int) | |
|
748 | ||
|
749 | class TestList(TraitTestBase): | |
|
750 | ||
|
751 | obj = ListTrait() | |
|
752 | ||
|
753 | _default_value = [] | |
|
754 | _good_values = [[], [1], range(10)] | |
|
755 | _bad_values = [10, [1,'a'], 'a', (1,2)] | |
|
756 | ||
|
757 | class LenListTrait(HasTraits): | |
|
758 | ||
|
759 | value = List(Int, [0], minlen=1, maxlen=2) | |
|
760 | ||
|
761 | class TestLenList(TraitTestBase): | |
|
762 | ||
|
763 | obj = LenListTrait() | |
|
764 | ||
|
765 | _default_value = [0] | |
|
766 | _good_values = [[1], range(2)] | |
|
767 | _bad_values = [10, [1,'a'], 'a', (1,2), [], range(3)] | |
|
768 | ||
|
769 | class TupleTrait(HasTraits): | |
|
770 | ||
|
771 | value = Tuple(Int) | |
|
772 | ||
|
773 | class TestTupleTrait(TraitTestBase): | |
|
774 | ||
|
775 | obj = TupleTrait() | |
|
776 | ||
|
777 | _default_value = None | |
|
778 | _good_values = [(1,), None,(0,)] | |
|
779 | _bad_values = [10, (1,2), [1],('a'), ()] | |
|
780 | ||
|
781 | def test_invalid_args(self): | |
|
782 | self.assertRaises(TypeError, Tuple, 5) | |
|
783 | self.assertRaises(TypeError, Tuple, default_value='hello') | |
|
784 | t = Tuple(Int, CStr, default_value=(1,5)) | |
|
785 | ||
|
786 | class LooseTupleTrait(HasTraits): | |
|
787 | ||
|
788 | value = Tuple((1,2,3)) | |
|
789 | ||
|
790 | class TestLooseTupleTrait(TraitTestBase): | |
|
791 | ||
|
792 | obj = LooseTupleTrait() | |
|
793 | ||
|
794 | _default_value = (1,2,3) | |
|
795 | _good_values = [(1,), None, (0,), tuple(range(5)), tuple('hello'), ('a',5), ()] | |
|
796 | _bad_values = [10, 'hello', [1], []] | |
|
797 | ||
|
798 | def test_invalid_args(self): | |
|
799 | self.assertRaises(TypeError, Tuple, 5) | |
|
800 | self.assertRaises(TypeError, Tuple, default_value='hello') | |
|
801 | t = Tuple(Int, CStr, default_value=(1,5)) | |
|
802 | ||
|
803 | ||
|
804 | class MultiTupleTrait(HasTraits): | |
|
805 | ||
|
806 | value = Tuple(Int, Str, default_value=[99,'bottles']) | |
|
807 | ||
|
808 | class TestMultiTuple(TraitTestBase): | |
|
809 | ||
|
810 | obj = MultiTupleTrait() | |
|
811 | ||
|
812 | _default_value = (99,'bottles') | |
|
813 | _good_values = [(1,'a'), (2,'b')] | |
|
814 | _bad_values = ((),10, 'a', (1,'a',3), ('a',1)) |
@@ -328,7 +328,7 class TraitType(object): | |||
|
328 | 328 | self.info(), repr_type(value)) |
|
329 | 329 | else: |
|
330 | 330 | e = "The '%s' trait must be %s, but a value of %r was specified." \ |
|
331 |
% (self.name, self.info(), repr_type(value)) |
|
|
331 | % (self.name, self.info(), repr_type(value)) | |
|
332 | 332 | raise TraitError(e) |
|
333 | 333 | |
|
334 | 334 | def get_metadata(self, key): |
@@ -621,7 +621,15 class ClassBasedTraitType(TraitType): | |||
|
621 | 621 | else: |
|
622 | 622 | msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) ) |
|
623 | 623 | |
|
624 | super(ClassBasedTraitType, self).error(obj, msg) | |
|
624 | if obj is not None: | |
|
625 | e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \ | |
|
626 | % (self.name, class_of(obj), | |
|
627 | self.info(), msg) | |
|
628 | else: | |
|
629 | e = "The '%s' trait must be %s, but a value of %r was specified." \ | |
|
630 | % (self.name, self.info(), msg) | |
|
631 | ||
|
632 | raise TraitError(e) | |
|
625 | 633 | |
|
626 | 634 | |
|
627 | 635 | class Type(ClassBasedTraitType): |
@@ -1055,46 +1063,255 class CaselessStrEnum(Enum): | |||
|
1055 | 1063 | return v |
|
1056 | 1064 | self.error(obj, value) |
|
1057 | 1065 | |
|
1066 | class Container(Instance): | |
|
1067 | """An instance of a container (list, set, etc.) | |
|
1058 | 1068 |
|
|
1059 | class List(Instance): | |
|
1060 | """An instance of a Python list.""" | |
|
1069 | To be subclassed by overriding klass. | |
|
1070 | """ | |
|
1071 | klass = None | |
|
1072 | _valid_defaults = SequenceTypes | |
|
1073 | _trait = None | |
|
1061 | 1074 | |
|
1062 |
def __init__(self, default_value=None, allow_none=True, |
|
|
1063 | """Create a list trait type from a list, set, or tuple. | |
|
1075 | def __init__(self, trait=None, default_value=None, allow_none=True, | |
|
1076 | **metadata): | |
|
1077 | """Create a container trait type from a list, set, or tuple. | |
|
1064 | 1078 | |
|
1065 |
The default value is created by doing `` |
|
|
1079 | The default value is created by doing ``List(default_value)``, | |
|
1066 | 1080 | which creates a copy of the ``default_value``. |
|
1081 | ||
|
1082 | ``trait`` can be specified, which restricts the type of elements | |
|
1083 | in the container to that TraitType. | |
|
1084 | ||
|
1085 | If only one arg is given and it is not a Trait, it is taken as | |
|
1086 | ``default_value``: | |
|
1087 | ||
|
1088 | ``c = List([1,2,3])`` | |
|
1089 | ||
|
1090 | Parameters | |
|
1091 | ---------- | |
|
1092 | ||
|
1093 | trait : TraitType [ optional ] | |
|
1094 | the type for restricting the contents of the Container. If unspecified, | |
|
1095 | types are not checked. | |
|
1096 | ||
|
1097 | default_value : SequenceType [ optional ] | |
|
1098 | The default value for the Trait. Must be list/tuple/set, and | |
|
1099 | will be cast to the container type. | |
|
1100 | ||
|
1101 | allow_none : Bool [ default True ] | |
|
1102 | Whether to allow the value to be None | |
|
1103 | ||
|
1104 | **metadata : any | |
|
1105 | further keys for extensions to the Trait (e.g. config) | |
|
1106 | ||
|
1067 | 1107 | """ |
|
1108 | istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType) | |
|
1109 | ||
|
1110 | # allow List([values]): | |
|
1111 | if default_value is None and not istrait(trait): | |
|
1112 | default_value = trait | |
|
1113 | trait = None | |
|
1114 | ||
|
1068 | 1115 | if default_value is None: |
|
1069 |
args = ( |
|
|
1070 |
elif isinstance(default_value, |
|
|
1116 | args = () | |
|
1117 | elif isinstance(default_value, self._valid_defaults): | |
|
1071 | 1118 | args = (default_value,) |
|
1072 | 1119 | else: |
|
1073 |
raise TypeError('default value of |
|
|
1120 | raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value)) | |
|
1121 | ||
|
1122 | if istrait(trait): | |
|
1123 | self._trait = trait() | |
|
1124 | self._trait.name = 'element' | |
|
1125 | elif trait is not None: | |
|
1126 | raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait)) | |
|
1074 | 1127 | |
|
1075 |
super( |
|
|
1128 | super(Container,self).__init__(klass=self.klass, args=args, | |
|
1076 | 1129 | allow_none=allow_none, **metadata) |
|
1077 | 1130 | |
|
1131 | def element_error(self, obj, element, validator): | |
|
1132 | e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \ | |
|
1133 | % (self.name, class_of(obj), validator.info(), repr_type(element)) | |
|
1134 | raise TraitError(e) | |
|
1078 | 1135 | |
|
1079 | class Set(Instance): | |
|
1080 | """An instance of a Python set.""" | |
|
1136 | def validate(self, obj, value): | |
|
1137 | value = super(Container, self).validate(obj, value) | |
|
1138 | if value is None: | |
|
1139 | return value | |
|
1081 | 1140 | |
|
1082 | def __init__(self, default_value=None, allow_none=True, **metadata): | |
|
1083 | """Create a set trait type from a set, list, or tuple. | |
|
1141 | value = self.validate_elements(obj, value) | |
|
1142 | ||
|
1143 | return value | |
|
1144 | ||
|
1145 | def validate_elements(self, obj, value): | |
|
1146 | validated = [] | |
|
1147 | if self._trait is None or isinstance(self._trait, Any): | |
|
1148 | return value | |
|
1149 | for v in value: | |
|
1150 | try: | |
|
1151 | v = self._trait.validate(obj, v) | |
|
1152 | except TraitError: | |
|
1153 | self.element_error(obj, v, self._trait) | |
|
1154 | else: | |
|
1155 | validated.append(v) | |
|
1156 | return self.klass(validated) | |
|
1157 | ||
|
1158 | ||
|
1159 | class List(Container): | |
|
1160 | """An instance of a Python list.""" | |
|
1161 | klass = list | |
|
1084 | 1162 | |
|
1085 | The default value is created by doing ``set(default_value)``, | |
|
1163 | def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint, | |
|
1164 | allow_none=True, **metadata): | |
|
1165 | """Create a List trait type from a list, set, or tuple. | |
|
1166 | ||
|
1167 | The default value is created by doing ``List(default_value)``, | |
|
1086 | 1168 | which creates a copy of the ``default_value``. |
|
1169 | ||
|
1170 | ``trait`` can be specified, which restricts the type of elements | |
|
1171 | in the container to that TraitType. | |
|
1172 | ||
|
1173 | If only one arg is given and it is not a Trait, it is taken as | |
|
1174 | ``default_value``: | |
|
1175 | ||
|
1176 | ``c = List([1,2,3])`` | |
|
1177 | ||
|
1178 | Parameters | |
|
1179 | ---------- | |
|
1180 | ||
|
1181 | trait : TraitType [ optional ] | |
|
1182 | the type for restricting the contents of the Container. If unspecified, | |
|
1183 | types are not checked. | |
|
1184 | ||
|
1185 | default_value : SequenceType [ optional ] | |
|
1186 | The default value for the Trait. Must be list/tuple/set, and | |
|
1187 | will be cast to the container type. | |
|
1188 | ||
|
1189 | minlen : Int [ default 0 ] | |
|
1190 | The minimum length of the input list | |
|
1191 | ||
|
1192 | maxlen : Int [ default sys.maxint ] | |
|
1193 | The maximum length of the input list | |
|
1194 | ||
|
1195 | allow_none : Bool [ default True ] | |
|
1196 | Whether to allow the value to be None | |
|
1197 | ||
|
1198 | **metadata : any | |
|
1199 | further keys for extensions to the Trait (e.g. config) | |
|
1200 | ||
|
1087 | 1201 | """ |
|
1202 | self._minlen = minlen | |
|
1203 | self._maxlen = maxlen | |
|
1204 | super(List, self).__init__(trait=trait, default_value=default_value, | |
|
1205 | allow_none=allow_none, **metadata) | |
|
1206 | ||
|
1207 | def length_error(self, obj, value): | |
|
1208 | e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \ | |
|
1209 | % (self.name, class_of(obj), self._minlen, self._maxlen, value) | |
|
1210 | raise TraitError(e) | |
|
1211 | ||
|
1212 | def validate_elements(self, obj, value): | |
|
1213 | length = len(value) | |
|
1214 | if length < self._minlen or length > self._maxlen: | |
|
1215 | self.length_error(obj, value) | |
|
1216 | ||
|
1217 | return super(List, self).validate_elements(obj, value) | |
|
1218 | ||
|
1219 | ||
|
1220 | class Set(Container): | |
|
1221 | """An instance of a Python set.""" | |
|
1222 | klass = set | |
|
1223 | ||
|
1224 | class Tuple(Container): | |
|
1225 | """An instance of a Python tuple.""" | |
|
1226 | klass = tuple | |
|
1227 | ||
|
1228 | def __init__(self, *traits, **metadata): | |
|
1229 | """Tuple(*traits, default_value=None, allow_none=True, **medatata) | |
|
1230 | ||
|
1231 | Create a tuple from a list, set, or tuple. | |
|
1232 | ||
|
1233 | Create a fixed-type tuple with Traits: | |
|
1234 | ||
|
1235 | ``t = Tuple(Int, Str, CStr)`` | |
|
1236 | ||
|
1237 | would be length 3, with Int,Str,CStr for each element. | |
|
1238 | ||
|
1239 | If only one arg is given and it is not a Trait, it is taken as | |
|
1240 | default_value: | |
|
1241 | ||
|
1242 | ``t = Tuple((1,2,3))`` | |
|
1243 | ||
|
1244 | Otherwise, ``default_value`` *must* be specified by keyword. | |
|
1245 | ||
|
1246 | Parameters | |
|
1247 | ---------- | |
|
1248 | ||
|
1249 | *traits : TraitTypes [ optional ] | |
|
1250 | the tsype for restricting the contents of the Tuple. If unspecified, | |
|
1251 | types are not checked. If specified, then each positional argument | |
|
1252 | corresponds to an element of the tuple. Tuples defined with traits | |
|
1253 | are of fixed length. | |
|
1254 | ||
|
1255 | default_value : SequenceType [ optional ] | |
|
1256 | The default value for the Tuple. Must be list/tuple/set, and | |
|
1257 | will be cast to a tuple. If `traits` are specified, the | |
|
1258 | `default_value` must conform to the shape and type they specify. | |
|
1259 | ||
|
1260 | allow_none : Bool [ default True ] | |
|
1261 | Whether to allow the value to be None | |
|
1262 | ||
|
1263 | **metadata : any | |
|
1264 | further keys for extensions to the Trait (e.g. config) | |
|
1265 | ||
|
1266 | """ | |
|
1267 | default_value = metadata.pop('default_value', None) | |
|
1268 | allow_none = metadata.pop('allow_none', True) | |
|
1269 | ||
|
1270 | istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType) | |
|
1271 | ||
|
1272 | # allow Tuple((values,)): | |
|
1273 | if len(traits) == 1 and default_value is None and not istrait(traits[0]): | |
|
1274 | default_value = traits[0] | |
|
1275 | traits = () | |
|
1276 | ||
|
1088 | 1277 | if default_value is None: |
|
1089 |
args = ( |
|
|
1090 |
elif isinstance(default_value, |
|
|
1278 | args = () | |
|
1279 | elif isinstance(default_value, self._valid_defaults): | |
|
1091 | 1280 | args = (default_value,) |
|
1092 | 1281 | else: |
|
1093 |
raise TypeError('default value of |
|
|
1094 | ||
|
1095 | super(Set,self).__init__(klass=set, args=args, | |
|
1282 | raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value)) | |
|
1283 | ||
|
1284 | self._traits = [] | |
|
1285 | for trait in traits: | |
|
1286 | t = trait() | |
|
1287 | t.name = 'element' | |
|
1288 | self._traits.append(t) | |
|
1289 | ||
|
1290 | if self._traits and default_value is None: | |
|
1291 | # don't allow default to be an empty container if length is specified | |
|
1292 | args = None | |
|
1293 | super(Container,self).__init__(klass=self.klass, args=args, | |
|
1096 | 1294 | allow_none=allow_none, **metadata) |
|
1097 | 1295 | |
|
1296 | def validate_elements(self, obj, value): | |
|
1297 | if not self._traits: | |
|
1298 | # nothing to validate | |
|
1299 | return value | |
|
1300 | if len(value) != len(self._traits): | |
|
1301 | e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \ | |
|
1302 | % (self.name, class_of(obj), len(self._traits), repr_type(value)) | |
|
1303 | raise TraitError(e) | |
|
1304 | ||
|
1305 | validated = [] | |
|
1306 | for t,v in zip(self._traits, value): | |
|
1307 | try: | |
|
1308 | v = t.validate(obj, v) | |
|
1309 | except TraitError: | |
|
1310 | self.element_error(obj, v, t) | |
|
1311 | else: | |
|
1312 | validated.append(v) | |
|
1313 | return tuple(validated) | |
|
1314 | ||
|
1098 | 1315 | |
|
1099 | 1316 | class Dict(Instance): |
|
1100 | 1317 | """An instance of a Python dict.""" |
@@ -1117,7 +1334,6 class Dict(Instance): | |||
|
1117 | 1334 | super(Dict,self).__init__(klass=dict, args=args, |
|
1118 | 1335 | allow_none=allow_none, **metadata) |
|
1119 | 1336 | |
|
1120 | ||
|
1121 | 1337 | class TCPAddress(TraitType): |
|
1122 | 1338 | """A trait for an (ip, port) tuple. |
|
1123 | 1339 |
General Comments 0
You need to be logged in to leave comments.
Login now