##// END OF EJS Templates
Merge branch 'multitrait'...
MinRK -
r3871:02aee17f merge
parent child Browse files
Show More
@@ -25,9 +25,9 b' Authors:'
25 from unittest import TestCase
25 from unittest import TestCase
26
26
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 HasTraits, MetaHasTraits, TraitType, Any,
28 HasTraits, MetaHasTraits, TraitType, Any, CStr,
29 Int, Long, Float, Complex, Str, Unicode, TraitError,
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 b' class TestTCPAddress(TraitTestBase):'
741 _default_value = ('127.0.0.1',0)
741 _default_value = ('127.0.0.1',0)
742 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
742 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
743 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
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 b' class TraitType(object):'
328 self.info(), repr_type(value))
328 self.info(), repr_type(value))
329 else:
329 else:
330 e = "The '%s' trait must be %s, but a value of %r was specified." \
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 raise TraitError(e)
332 raise TraitError(e)
333
333
334 def get_metadata(self, key):
334 def get_metadata(self, key):
@@ -621,7 +621,15 b' class ClassBasedTraitType(TraitType):'
621 else:
621 else:
622 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
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 class Type(ClassBasedTraitType):
635 class Type(ClassBasedTraitType):
@@ -1055,46 +1063,255 b' class CaselessStrEnum(Enum):'
1055 return v
1063 return v
1056 self.error(obj, value)
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):
1069 To be subclassed by overriding klass.
1060 """An instance of a Python list."""
1070 """
1071 klass = None
1072 _valid_defaults = SequenceTypes
1073 _trait = None
1061
1074
1062 def __init__(self, default_value=None, allow_none=True, **metadata):
1075 def __init__(self, trait=None, default_value=None, allow_none=True,
1063 """Create a list trait type from a list, set, or tuple.
1076 **metadata):
1077 """Create a container trait type from a list, set, or tuple.
1064
1078
1065 The default value is created by doing ``list(default_value)``,
1079 The default value is created by doing ``List(default_value)``,
1066 which creates a copy of the ``default_value``.
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 if default_value is None:
1115 if default_value is None:
1069 args = ((),)
1116 args = ()
1070 elif isinstance(default_value, SequenceTypes):
1117 elif isinstance(default_value, self._valid_defaults):
1071 args = (default_value,)
1118 args = (default_value,)
1072 else:
1119 else:
1073 raise TypeError('default value of List was %s' % default_value)
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(List,self).__init__(klass=list, args=args,
1128 super(Container,self).__init__(klass=self.klass, args=args,
1076 allow_none=allow_none, **metadata)
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):
1136 def validate(self, obj, value):
1080 """An instance of a Python set."""
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):
1141 value = self.validate_elements(obj, value)
1083 """Create a set trait type from a set, list, or tuple.
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 which creates a copy of the ``default_value``.
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 if default_value is None:
1277 if default_value is None:
1089 args = ((),)
1278 args = ()
1090 elif isinstance(default_value, SequenceTypes):
1279 elif isinstance(default_value, self._valid_defaults):
1091 args = (default_value,)
1280 args = (default_value,)
1092 else:
1281 else:
1093 raise TypeError('default value of Set was %s' % default_value)
1282 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1094
1283
1095 super(Set,self).__init__(klass=set, args=args,
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 allow_none=allow_none, **metadata)
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 class Dict(Instance):
1316 class Dict(Instance):
1100 """An instance of a Python dict."""
1317 """An instance of a Python dict."""
@@ -1117,7 +1334,6 b' class Dict(Instance):'
1117 super(Dict,self).__init__(klass=dict, args=args,
1334 super(Dict,self).__init__(klass=dict, args=args,
1118 allow_none=allow_none, **metadata)
1335 allow_none=allow_none, **metadata)
1119
1336
1120
1121 class TCPAddress(TraitType):
1337 class TCPAddress(TraitType):
1122 """A trait for an (ip, port) tuple.
1338 """A trait for an (ip, port) tuple.
1123
1339
General Comments 0
You need to be logged in to leave comments. Login now