##// END OF EJS Templates
Merge pull request #2311 from minrk/trait_change...
Min RK -
r8422:82842821 merge
parent child Browse files
Show More
@@ -1,908 +1,920 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Tests for IPython.utils.traitlets.
3 Tests for IPython.utils.traitlets.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Enthought, Inc. Some of the code in this file comes from enthought.traits
8 * Enthought, Inc. Some of the code in this file comes from enthought.traits
9 and is licensed under the BSD license. Also, many of the ideas also come
9 and is licensed under the BSD license. Also, many of the ideas also come
10 from enthought.traits even though our implementation is very different.
10 from enthought.traits even though our implementation is very different.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Copyright (C) 2008-2011 The IPython Development Team
14 # Copyright (C) 2008-2011 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 import re
24 import re
25 import sys
25 import sys
26 from unittest import TestCase
26 from unittest import TestCase
27
27
28 import nose.tools as nt
28 from nose import SkipTest
29 from nose import SkipTest
29
30
30 from IPython.utils.traitlets import (
31 from IPython.utils.traitlets import (
31 HasTraits, MetaHasTraits, TraitType, Any, CBytes,
32 HasTraits, MetaHasTraits, TraitType, Any, CBytes, Dict,
32 Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError,
33 Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError,
33 Undefined, Type, This, Instance, TCPAddress, List, Tuple,
34 Undefined, Type, This, Instance, TCPAddress, List, Tuple,
34 ObjectName, DottedObjectName, CRegExp
35 ObjectName, DottedObjectName, CRegExp
35 )
36 )
36 from IPython.utils import py3compat
37 from IPython.utils import py3compat
37 from IPython.testing.decorators import skipif
38 from IPython.testing.decorators import skipif
38
39
39 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
40 # Helper classes for testing
41 # Helper classes for testing
41 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
42
43
43
44
44 class HasTraitsStub(HasTraits):
45 class HasTraitsStub(HasTraits):
45
46
46 def _notify_trait(self, name, old, new):
47 def _notify_trait(self, name, old, new):
47 self._notify_name = name
48 self._notify_name = name
48 self._notify_old = old
49 self._notify_old = old
49 self._notify_new = new
50 self._notify_new = new
50
51
51
52
52 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
53 # Test classes
54 # Test classes
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
55
56
56
57
57 class TestTraitType(TestCase):
58 class TestTraitType(TestCase):
58
59
59 def test_get_undefined(self):
60 def test_get_undefined(self):
60 class A(HasTraits):
61 class A(HasTraits):
61 a = TraitType
62 a = TraitType
62 a = A()
63 a = A()
63 self.assertEqual(a.a, Undefined)
64 self.assertEqual(a.a, Undefined)
64
65
65 def test_set(self):
66 def test_set(self):
66 class A(HasTraitsStub):
67 class A(HasTraitsStub):
67 a = TraitType
68 a = TraitType
68
69
69 a = A()
70 a = A()
70 a.a = 10
71 a.a = 10
71 self.assertEqual(a.a, 10)
72 self.assertEqual(a.a, 10)
72 self.assertEqual(a._notify_name, 'a')
73 self.assertEqual(a._notify_name, 'a')
73 self.assertEqual(a._notify_old, Undefined)
74 self.assertEqual(a._notify_old, Undefined)
74 self.assertEqual(a._notify_new, 10)
75 self.assertEqual(a._notify_new, 10)
75
76
76 def test_validate(self):
77 def test_validate(self):
77 class MyTT(TraitType):
78 class MyTT(TraitType):
78 def validate(self, inst, value):
79 def validate(self, inst, value):
79 return -1
80 return -1
80 class A(HasTraitsStub):
81 class A(HasTraitsStub):
81 tt = MyTT
82 tt = MyTT
82
83
83 a = A()
84 a = A()
84 a.tt = 10
85 a.tt = 10
85 self.assertEqual(a.tt, -1)
86 self.assertEqual(a.tt, -1)
86
87
87 def test_default_validate(self):
88 def test_default_validate(self):
88 class MyIntTT(TraitType):
89 class MyIntTT(TraitType):
89 def validate(self, obj, value):
90 def validate(self, obj, value):
90 if isinstance(value, int):
91 if isinstance(value, int):
91 return value
92 return value
92 self.error(obj, value)
93 self.error(obj, value)
93 class A(HasTraits):
94 class A(HasTraits):
94 tt = MyIntTT(10)
95 tt = MyIntTT(10)
95 a = A()
96 a = A()
96 self.assertEqual(a.tt, 10)
97 self.assertEqual(a.tt, 10)
97
98
98 # Defaults are validated when the HasTraits is instantiated
99 # Defaults are validated when the HasTraits is instantiated
99 class B(HasTraits):
100 class B(HasTraits):
100 tt = MyIntTT('bad default')
101 tt = MyIntTT('bad default')
101 self.assertRaises(TraitError, B)
102 self.assertRaises(TraitError, B)
102
103
103 def test_is_valid_for(self):
104 def test_is_valid_for(self):
104 class MyTT(TraitType):
105 class MyTT(TraitType):
105 def is_valid_for(self, value):
106 def is_valid_for(self, value):
106 return True
107 return True
107 class A(HasTraits):
108 class A(HasTraits):
108 tt = MyTT
109 tt = MyTT
109
110
110 a = A()
111 a = A()
111 a.tt = 10
112 a.tt = 10
112 self.assertEqual(a.tt, 10)
113 self.assertEqual(a.tt, 10)
113
114
114 def test_value_for(self):
115 def test_value_for(self):
115 class MyTT(TraitType):
116 class MyTT(TraitType):
116 def value_for(self, value):
117 def value_for(self, value):
117 return 20
118 return 20
118 class A(HasTraits):
119 class A(HasTraits):
119 tt = MyTT
120 tt = MyTT
120
121
121 a = A()
122 a = A()
122 a.tt = 10
123 a.tt = 10
123 self.assertEqual(a.tt, 20)
124 self.assertEqual(a.tt, 20)
124
125
125 def test_info(self):
126 def test_info(self):
126 class A(HasTraits):
127 class A(HasTraits):
127 tt = TraitType
128 tt = TraitType
128 a = A()
129 a = A()
129 self.assertEqual(A.tt.info(), 'any value')
130 self.assertEqual(A.tt.info(), 'any value')
130
131
131 def test_error(self):
132 def test_error(self):
132 class A(HasTraits):
133 class A(HasTraits):
133 tt = TraitType
134 tt = TraitType
134 a = A()
135 a = A()
135 self.assertRaises(TraitError, A.tt.error, a, 10)
136 self.assertRaises(TraitError, A.tt.error, a, 10)
136
137
137 def test_dynamic_initializer(self):
138 def test_dynamic_initializer(self):
138 class A(HasTraits):
139 class A(HasTraits):
139 x = Int(10)
140 x = Int(10)
140 def _x_default(self):
141 def _x_default(self):
141 return 11
142 return 11
142 class B(A):
143 class B(A):
143 x = Int(20)
144 x = Int(20)
144 class C(A):
145 class C(A):
145 def _x_default(self):
146 def _x_default(self):
146 return 21
147 return 21
147
148
148 a = A()
149 a = A()
149 self.assertEqual(a._trait_values, {})
150 self.assertEqual(a._trait_values, {})
150 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
151 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
151 self.assertEqual(a.x, 11)
152 self.assertEqual(a.x, 11)
152 self.assertEqual(a._trait_values, {'x': 11})
153 self.assertEqual(a._trait_values, {'x': 11})
153 b = B()
154 b = B()
154 self.assertEqual(b._trait_values, {'x': 20})
155 self.assertEqual(b._trait_values, {'x': 20})
155 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
156 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
156 self.assertEqual(b.x, 20)
157 self.assertEqual(b.x, 20)
157 c = C()
158 c = C()
158 self.assertEqual(c._trait_values, {})
159 self.assertEqual(c._trait_values, {})
159 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
160 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
160 self.assertEqual(c.x, 21)
161 self.assertEqual(c.x, 21)
161 self.assertEqual(c._trait_values, {'x': 21})
162 self.assertEqual(c._trait_values, {'x': 21})
162 # Ensure that the base class remains unmolested when the _default
163 # Ensure that the base class remains unmolested when the _default
163 # initializer gets overridden in a subclass.
164 # initializer gets overridden in a subclass.
164 a = A()
165 a = A()
165 c = C()
166 c = C()
166 self.assertEqual(a._trait_values, {})
167 self.assertEqual(a._trait_values, {})
167 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
168 self.assertEqual(a._trait_dyn_inits.keys(), ['x'])
168 self.assertEqual(a.x, 11)
169 self.assertEqual(a.x, 11)
169 self.assertEqual(a._trait_values, {'x': 11})
170 self.assertEqual(a._trait_values, {'x': 11})
170
171
171
172
172
173
173 class TestHasTraitsMeta(TestCase):
174 class TestHasTraitsMeta(TestCase):
174
175
175 def test_metaclass(self):
176 def test_metaclass(self):
176 self.assertEqual(type(HasTraits), MetaHasTraits)
177 self.assertEqual(type(HasTraits), MetaHasTraits)
177
178
178 class A(HasTraits):
179 class A(HasTraits):
179 a = Int
180 a = Int
180
181
181 a = A()
182 a = A()
182 self.assertEqual(type(a.__class__), MetaHasTraits)
183 self.assertEqual(type(a.__class__), MetaHasTraits)
183 self.assertEqual(a.a,0)
184 self.assertEqual(a.a,0)
184 a.a = 10
185 a.a = 10
185 self.assertEqual(a.a,10)
186 self.assertEqual(a.a,10)
186
187
187 class B(HasTraits):
188 class B(HasTraits):
188 b = Int()
189 b = Int()
189
190
190 b = B()
191 b = B()
191 self.assertEqual(b.b,0)
192 self.assertEqual(b.b,0)
192 b.b = 10
193 b.b = 10
193 self.assertEqual(b.b,10)
194 self.assertEqual(b.b,10)
194
195
195 class C(HasTraits):
196 class C(HasTraits):
196 c = Int(30)
197 c = Int(30)
197
198
198 c = C()
199 c = C()
199 self.assertEqual(c.c,30)
200 self.assertEqual(c.c,30)
200 c.c = 10
201 c.c = 10
201 self.assertEqual(c.c,10)
202 self.assertEqual(c.c,10)
202
203
203 def test_this_class(self):
204 def test_this_class(self):
204 class A(HasTraits):
205 class A(HasTraits):
205 t = This()
206 t = This()
206 tt = This()
207 tt = This()
207 class B(A):
208 class B(A):
208 tt = This()
209 tt = This()
209 ttt = This()
210 ttt = This()
210 self.assertEqual(A.t.this_class, A)
211 self.assertEqual(A.t.this_class, A)
211 self.assertEqual(B.t.this_class, A)
212 self.assertEqual(B.t.this_class, A)
212 self.assertEqual(B.tt.this_class, B)
213 self.assertEqual(B.tt.this_class, B)
213 self.assertEqual(B.ttt.this_class, B)
214 self.assertEqual(B.ttt.this_class, B)
214
215
215 class TestHasTraitsNotify(TestCase):
216 class TestHasTraitsNotify(TestCase):
216
217
217 def setUp(self):
218 def setUp(self):
218 self._notify1 = []
219 self._notify1 = []
219 self._notify2 = []
220 self._notify2 = []
220
221
221 def notify1(self, name, old, new):
222 def notify1(self, name, old, new):
222 self._notify1.append((name, old, new))
223 self._notify1.append((name, old, new))
223
224
224 def notify2(self, name, old, new):
225 def notify2(self, name, old, new):
225 self._notify2.append((name, old, new))
226 self._notify2.append((name, old, new))
226
227
227 def test_notify_all(self):
228 def test_notify_all(self):
228
229
229 class A(HasTraits):
230 class A(HasTraits):
230 a = Int
231 a = Int
231 b = Float
232 b = Float
232
233
233 a = A()
234 a = A()
234 a.on_trait_change(self.notify1)
235 a.on_trait_change(self.notify1)
235 a.a = 0
236 a.a = 0
236 self.assertEqual(len(self._notify1),0)
237 self.assertEqual(len(self._notify1),0)
237 a.b = 0.0
238 a.b = 0.0
238 self.assertEqual(len(self._notify1),0)
239 self.assertEqual(len(self._notify1),0)
239 a.a = 10
240 a.a = 10
240 self.assertTrue(('a',0,10) in self._notify1)
241 self.assertTrue(('a',0,10) in self._notify1)
241 a.b = 10.0
242 a.b = 10.0
242 self.assertTrue(('b',0.0,10.0) in self._notify1)
243 self.assertTrue(('b',0.0,10.0) in self._notify1)
243 self.assertRaises(TraitError,setattr,a,'a','bad string')
244 self.assertRaises(TraitError,setattr,a,'a','bad string')
244 self.assertRaises(TraitError,setattr,a,'b','bad string')
245 self.assertRaises(TraitError,setattr,a,'b','bad string')
245 self._notify1 = []
246 self._notify1 = []
246 a.on_trait_change(self.notify1,remove=True)
247 a.on_trait_change(self.notify1,remove=True)
247 a.a = 20
248 a.a = 20
248 a.b = 20.0
249 a.b = 20.0
249 self.assertEqual(len(self._notify1),0)
250 self.assertEqual(len(self._notify1),0)
250
251
251 def test_notify_one(self):
252 def test_notify_one(self):
252
253
253 class A(HasTraits):
254 class A(HasTraits):
254 a = Int
255 a = Int
255 b = Float
256 b = Float
256
257
257 a = A()
258 a = A()
258 a.on_trait_change(self.notify1, 'a')
259 a.on_trait_change(self.notify1, 'a')
259 a.a = 0
260 a.a = 0
260 self.assertEqual(len(self._notify1),0)
261 self.assertEqual(len(self._notify1),0)
261 a.a = 10
262 a.a = 10
262 self.assertTrue(('a',0,10) in self._notify1)
263 self.assertTrue(('a',0,10) in self._notify1)
263 self.assertRaises(TraitError,setattr,a,'a','bad string')
264 self.assertRaises(TraitError,setattr,a,'a','bad string')
264
265
265 def test_subclass(self):
266 def test_subclass(self):
266
267
267 class A(HasTraits):
268 class A(HasTraits):
268 a = Int
269 a = Int
269
270
270 class B(A):
271 class B(A):
271 b = Float
272 b = Float
272
273
273 b = B()
274 b = B()
274 self.assertEqual(b.a,0)
275 self.assertEqual(b.a,0)
275 self.assertEqual(b.b,0.0)
276 self.assertEqual(b.b,0.0)
276 b.a = 100
277 b.a = 100
277 b.b = 100.0
278 b.b = 100.0
278 self.assertEqual(b.a,100)
279 self.assertEqual(b.a,100)
279 self.assertEqual(b.b,100.0)
280 self.assertEqual(b.b,100.0)
280
281
281 def test_notify_subclass(self):
282 def test_notify_subclass(self):
282
283
283 class A(HasTraits):
284 class A(HasTraits):
284 a = Int
285 a = Int
285
286
286 class B(A):
287 class B(A):
287 b = Float
288 b = Float
288
289
289 b = B()
290 b = B()
290 b.on_trait_change(self.notify1, 'a')
291 b.on_trait_change(self.notify1, 'a')
291 b.on_trait_change(self.notify2, 'b')
292 b.on_trait_change(self.notify2, 'b')
292 b.a = 0
293 b.a = 0
293 b.b = 0.0
294 b.b = 0.0
294 self.assertEqual(len(self._notify1),0)
295 self.assertEqual(len(self._notify1),0)
295 self.assertEqual(len(self._notify2),0)
296 self.assertEqual(len(self._notify2),0)
296 b.a = 10
297 b.a = 10
297 b.b = 10.0
298 b.b = 10.0
298 self.assertTrue(('a',0,10) in self._notify1)
299 self.assertTrue(('a',0,10) in self._notify1)
299 self.assertTrue(('b',0.0,10.0) in self._notify2)
300 self.assertTrue(('b',0.0,10.0) in self._notify2)
300
301
301 def test_static_notify(self):
302 def test_static_notify(self):
302
303
303 class A(HasTraits):
304 class A(HasTraits):
304 a = Int
305 a = Int
305 _notify1 = []
306 _notify1 = []
306 def _a_changed(self, name, old, new):
307 def _a_changed(self, name, old, new):
307 self._notify1.append((name, old, new))
308 self._notify1.append((name, old, new))
308
309
309 a = A()
310 a = A()
310 a.a = 0
311 a.a = 0
311 # This is broken!!!
312 # This is broken!!!
312 self.assertEqual(len(a._notify1),0)
313 self.assertEqual(len(a._notify1),0)
313 a.a = 10
314 a.a = 10
314 self.assertTrue(('a',0,10) in a._notify1)
315 self.assertTrue(('a',0,10) in a._notify1)
315
316
316 class B(A):
317 class B(A):
317 b = Float
318 b = Float
318 _notify2 = []
319 _notify2 = []
319 def _b_changed(self, name, old, new):
320 def _b_changed(self, name, old, new):
320 self._notify2.append((name, old, new))
321 self._notify2.append((name, old, new))
321
322
322 b = B()
323 b = B()
323 b.a = 10
324 b.a = 10
324 b.b = 10.0
325 b.b = 10.0
325 self.assertTrue(('a',0,10) in b._notify1)
326 self.assertTrue(('a',0,10) in b._notify1)
326 self.assertTrue(('b',0.0,10.0) in b._notify2)
327 self.assertTrue(('b',0.0,10.0) in b._notify2)
327
328
328 def test_notify_args(self):
329 def test_notify_args(self):
329
330
330 def callback0():
331 def callback0():
331 self.cb = ()
332 self.cb = ()
332 def callback1(name):
333 def callback1(name):
333 self.cb = (name,)
334 self.cb = (name,)
334 def callback2(name, new):
335 def callback2(name, new):
335 self.cb = (name, new)
336 self.cb = (name, new)
336 def callback3(name, old, new):
337 def callback3(name, old, new):
337 self.cb = (name, old, new)
338 self.cb = (name, old, new)
338
339
339 class A(HasTraits):
340 class A(HasTraits):
340 a = Int
341 a = Int
341
342
342 a = A()
343 a = A()
343 a.on_trait_change(callback0, 'a')
344 a.on_trait_change(callback0, 'a')
344 a.a = 10
345 a.a = 10
345 self.assertEqual(self.cb,())
346 self.assertEqual(self.cb,())
346 a.on_trait_change(callback0, 'a', remove=True)
347 a.on_trait_change(callback0, 'a', remove=True)
347
348
348 a.on_trait_change(callback1, 'a')
349 a.on_trait_change(callback1, 'a')
349 a.a = 100
350 a.a = 100
350 self.assertEqual(self.cb,('a',))
351 self.assertEqual(self.cb,('a',))
351 a.on_trait_change(callback1, 'a', remove=True)
352 a.on_trait_change(callback1, 'a', remove=True)
352
353
353 a.on_trait_change(callback2, 'a')
354 a.on_trait_change(callback2, 'a')
354 a.a = 1000
355 a.a = 1000
355 self.assertEqual(self.cb,('a',1000))
356 self.assertEqual(self.cb,('a',1000))
356 a.on_trait_change(callback2, 'a', remove=True)
357 a.on_trait_change(callback2, 'a', remove=True)
357
358
358 a.on_trait_change(callback3, 'a')
359 a.on_trait_change(callback3, 'a')
359 a.a = 10000
360 a.a = 10000
360 self.assertEqual(self.cb,('a',1000,10000))
361 self.assertEqual(self.cb,('a',1000,10000))
361 a.on_trait_change(callback3, 'a', remove=True)
362 a.on_trait_change(callback3, 'a', remove=True)
362
363
363 self.assertEqual(len(a._trait_notifiers['a']),0)
364 self.assertEqual(len(a._trait_notifiers['a']),0)
364
365
365
366
366 class TestHasTraits(TestCase):
367 class TestHasTraits(TestCase):
367
368
368 def test_trait_names(self):
369 def test_trait_names(self):
369 class A(HasTraits):
370 class A(HasTraits):
370 i = Int
371 i = Int
371 f = Float
372 f = Float
372 a = A()
373 a = A()
373 self.assertEqual(sorted(a.trait_names()),['f','i'])
374 self.assertEqual(sorted(a.trait_names()),['f','i'])
374 self.assertEqual(sorted(A.class_trait_names()),['f','i'])
375 self.assertEqual(sorted(A.class_trait_names()),['f','i'])
375
376
376 def test_trait_metadata(self):
377 def test_trait_metadata(self):
377 class A(HasTraits):
378 class A(HasTraits):
378 i = Int(config_key='MY_VALUE')
379 i = Int(config_key='MY_VALUE')
379 a = A()
380 a = A()
380 self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE')
381 self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE')
381
382
382 def test_traits(self):
383 def test_traits(self):
383 class A(HasTraits):
384 class A(HasTraits):
384 i = Int
385 i = Int
385 f = Float
386 f = Float
386 a = A()
387 a = A()
387 self.assertEqual(a.traits(), dict(i=A.i, f=A.f))
388 self.assertEqual(a.traits(), dict(i=A.i, f=A.f))
388 self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f))
389 self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f))
389
390
390 def test_traits_metadata(self):
391 def test_traits_metadata(self):
391 class A(HasTraits):
392 class A(HasTraits):
392 i = Int(config_key='VALUE1', other_thing='VALUE2')
393 i = Int(config_key='VALUE1', other_thing='VALUE2')
393 f = Float(config_key='VALUE3', other_thing='VALUE2')
394 f = Float(config_key='VALUE3', other_thing='VALUE2')
394 j = Int(0)
395 j = Int(0)
395 a = A()
396 a = A()
396 self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j))
397 self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j))
397 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
398 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
398 self.assertEqual(traits, dict(i=A.i))
399 self.assertEqual(traits, dict(i=A.i))
399
400
400 # This passes, but it shouldn't because I am replicating a bug in
401 # This passes, but it shouldn't because I am replicating a bug in
401 # traits.
402 # traits.
402 traits = a.traits(config_key=lambda v: True)
403 traits = a.traits(config_key=lambda v: True)
403 self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j))
404 self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j))
404
405
405 def test_init(self):
406 def test_init(self):
406 class A(HasTraits):
407 class A(HasTraits):
407 i = Int()
408 i = Int()
408 x = Float()
409 x = Float()
409 a = A(i=1, x=10.0)
410 a = A(i=1, x=10.0)
410 self.assertEqual(a.i, 1)
411 self.assertEqual(a.i, 1)
411 self.assertEqual(a.x, 10.0)
412 self.assertEqual(a.x, 10.0)
412
413
413 #-----------------------------------------------------------------------------
414 #-----------------------------------------------------------------------------
414 # Tests for specific trait types
415 # Tests for specific trait types
415 #-----------------------------------------------------------------------------
416 #-----------------------------------------------------------------------------
416
417
417
418
418 class TestType(TestCase):
419 class TestType(TestCase):
419
420
420 def test_default(self):
421 def test_default(self):
421
422
422 class B(object): pass
423 class B(object): pass
423 class A(HasTraits):
424 class A(HasTraits):
424 klass = Type
425 klass = Type
425
426
426 a = A()
427 a = A()
427 self.assertEqual(a.klass, None)
428 self.assertEqual(a.klass, None)
428
429
429 a.klass = B
430 a.klass = B
430 self.assertEqual(a.klass, B)
431 self.assertEqual(a.klass, B)
431 self.assertRaises(TraitError, setattr, a, 'klass', 10)
432 self.assertRaises(TraitError, setattr, a, 'klass', 10)
432
433
433 def test_value(self):
434 def test_value(self):
434
435
435 class B(object): pass
436 class B(object): pass
436 class C(object): pass
437 class C(object): pass
437 class A(HasTraits):
438 class A(HasTraits):
438 klass = Type(B)
439 klass = Type(B)
439
440
440 a = A()
441 a = A()
441 self.assertEqual(a.klass, B)
442 self.assertEqual(a.klass, B)
442 self.assertRaises(TraitError, setattr, a, 'klass', C)
443 self.assertRaises(TraitError, setattr, a, 'klass', C)
443 self.assertRaises(TraitError, setattr, a, 'klass', object)
444 self.assertRaises(TraitError, setattr, a, 'klass', object)
444 a.klass = B
445 a.klass = B
445
446
446 def test_allow_none(self):
447 def test_allow_none(self):
447
448
448 class B(object): pass
449 class B(object): pass
449 class C(B): pass
450 class C(B): pass
450 class A(HasTraits):
451 class A(HasTraits):
451 klass = Type(B, allow_none=False)
452 klass = Type(B, allow_none=False)
452
453
453 a = A()
454 a = A()
454 self.assertEqual(a.klass, B)
455 self.assertEqual(a.klass, B)
455 self.assertRaises(TraitError, setattr, a, 'klass', None)
456 self.assertRaises(TraitError, setattr, a, 'klass', None)
456 a.klass = C
457 a.klass = C
457 self.assertEqual(a.klass, C)
458 self.assertEqual(a.klass, C)
458
459
459 def test_validate_klass(self):
460 def test_validate_klass(self):
460
461
461 class A(HasTraits):
462 class A(HasTraits):
462 klass = Type('no strings allowed')
463 klass = Type('no strings allowed')
463
464
464 self.assertRaises(ImportError, A)
465 self.assertRaises(ImportError, A)
465
466
466 class A(HasTraits):
467 class A(HasTraits):
467 klass = Type('rub.adub.Duck')
468 klass = Type('rub.adub.Duck')
468
469
469 self.assertRaises(ImportError, A)
470 self.assertRaises(ImportError, A)
470
471
471 def test_validate_default(self):
472 def test_validate_default(self):
472
473
473 class B(object): pass
474 class B(object): pass
474 class A(HasTraits):
475 class A(HasTraits):
475 klass = Type('bad default', B)
476 klass = Type('bad default', B)
476
477
477 self.assertRaises(ImportError, A)
478 self.assertRaises(ImportError, A)
478
479
479 class C(HasTraits):
480 class C(HasTraits):
480 klass = Type(None, B, allow_none=False)
481 klass = Type(None, B, allow_none=False)
481
482
482 self.assertRaises(TraitError, C)
483 self.assertRaises(TraitError, C)
483
484
484 def test_str_klass(self):
485 def test_str_klass(self):
485
486
486 class A(HasTraits):
487 class A(HasTraits):
487 klass = Type('IPython.utils.ipstruct.Struct')
488 klass = Type('IPython.utils.ipstruct.Struct')
488
489
489 from IPython.utils.ipstruct import Struct
490 from IPython.utils.ipstruct import Struct
490 a = A()
491 a = A()
491 a.klass = Struct
492 a.klass = Struct
492 self.assertEqual(a.klass, Struct)
493 self.assertEqual(a.klass, Struct)
493
494
494 self.assertRaises(TraitError, setattr, a, 'klass', 10)
495 self.assertRaises(TraitError, setattr, a, 'klass', 10)
495
496
496 class TestInstance(TestCase):
497 class TestInstance(TestCase):
497
498
498 def test_basic(self):
499 def test_basic(self):
499 class Foo(object): pass
500 class Foo(object): pass
500 class Bar(Foo): pass
501 class Bar(Foo): pass
501 class Bah(object): pass
502 class Bah(object): pass
502
503
503 class A(HasTraits):
504 class A(HasTraits):
504 inst = Instance(Foo)
505 inst = Instance(Foo)
505
506
506 a = A()
507 a = A()
507 self.assertTrue(a.inst is None)
508 self.assertTrue(a.inst is None)
508 a.inst = Foo()
509 a.inst = Foo()
509 self.assertTrue(isinstance(a.inst, Foo))
510 self.assertTrue(isinstance(a.inst, Foo))
510 a.inst = Bar()
511 a.inst = Bar()
511 self.assertTrue(isinstance(a.inst, Foo))
512 self.assertTrue(isinstance(a.inst, Foo))
512 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
513 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
513 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
514 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
514 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
515 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
515
516
516 def test_unique_default_value(self):
517 def test_unique_default_value(self):
517 class Foo(object): pass
518 class Foo(object): pass
518 class A(HasTraits):
519 class A(HasTraits):
519 inst = Instance(Foo,(),{})
520 inst = Instance(Foo,(),{})
520
521
521 a = A()
522 a = A()
522 b = A()
523 b = A()
523 self.assertTrue(a.inst is not b.inst)
524 self.assertTrue(a.inst is not b.inst)
524
525
525 def test_args_kw(self):
526 def test_args_kw(self):
526 class Foo(object):
527 class Foo(object):
527 def __init__(self, c): self.c = c
528 def __init__(self, c): self.c = c
528 class Bar(object): pass
529 class Bar(object): pass
529 class Bah(object):
530 class Bah(object):
530 def __init__(self, c, d):
531 def __init__(self, c, d):
531 self.c = c; self.d = d
532 self.c = c; self.d = d
532
533
533 class A(HasTraits):
534 class A(HasTraits):
534 inst = Instance(Foo, (10,))
535 inst = Instance(Foo, (10,))
535 a = A()
536 a = A()
536 self.assertEqual(a.inst.c, 10)
537 self.assertEqual(a.inst.c, 10)
537
538
538 class B(HasTraits):
539 class B(HasTraits):
539 inst = Instance(Bah, args=(10,), kw=dict(d=20))
540 inst = Instance(Bah, args=(10,), kw=dict(d=20))
540 b = B()
541 b = B()
541 self.assertEqual(b.inst.c, 10)
542 self.assertEqual(b.inst.c, 10)
542 self.assertEqual(b.inst.d, 20)
543 self.assertEqual(b.inst.d, 20)
543
544
544 class C(HasTraits):
545 class C(HasTraits):
545 inst = Instance(Foo)
546 inst = Instance(Foo)
546 c = C()
547 c = C()
547 self.assertTrue(c.inst is None)
548 self.assertTrue(c.inst is None)
548
549
549 def test_bad_default(self):
550 def test_bad_default(self):
550 class Foo(object): pass
551 class Foo(object): pass
551
552
552 class A(HasTraits):
553 class A(HasTraits):
553 inst = Instance(Foo, allow_none=False)
554 inst = Instance(Foo, allow_none=False)
554
555
555 self.assertRaises(TraitError, A)
556 self.assertRaises(TraitError, A)
556
557
557 def test_instance(self):
558 def test_instance(self):
558 class Foo(object): pass
559 class Foo(object): pass
559
560
560 def inner():
561 def inner():
561 class A(HasTraits):
562 class A(HasTraits):
562 inst = Instance(Foo())
563 inst = Instance(Foo())
563
564
564 self.assertRaises(TraitError, inner)
565 self.assertRaises(TraitError, inner)
565
566
566
567
567 class TestThis(TestCase):
568 class TestThis(TestCase):
568
569
569 def test_this_class(self):
570 def test_this_class(self):
570 class Foo(HasTraits):
571 class Foo(HasTraits):
571 this = This
572 this = This
572
573
573 f = Foo()
574 f = Foo()
574 self.assertEqual(f.this, None)
575 self.assertEqual(f.this, None)
575 g = Foo()
576 g = Foo()
576 f.this = g
577 f.this = g
577 self.assertEqual(f.this, g)
578 self.assertEqual(f.this, g)
578 self.assertRaises(TraitError, setattr, f, 'this', 10)
579 self.assertRaises(TraitError, setattr, f, 'this', 10)
579
580
580 def test_this_inst(self):
581 def test_this_inst(self):
581 class Foo(HasTraits):
582 class Foo(HasTraits):
582 this = This()
583 this = This()
583
584
584 f = Foo()
585 f = Foo()
585 f.this = Foo()
586 f.this = Foo()
586 self.assertTrue(isinstance(f.this, Foo))
587 self.assertTrue(isinstance(f.this, Foo))
587
588
588 def test_subclass(self):
589 def test_subclass(self):
589 class Foo(HasTraits):
590 class Foo(HasTraits):
590 t = This()
591 t = This()
591 class Bar(Foo):
592 class Bar(Foo):
592 pass
593 pass
593 f = Foo()
594 f = Foo()
594 b = Bar()
595 b = Bar()
595 f.t = b
596 f.t = b
596 b.t = f
597 b.t = f
597 self.assertEqual(f.t, b)
598 self.assertEqual(f.t, b)
598 self.assertEqual(b.t, f)
599 self.assertEqual(b.t, f)
599
600
600 def test_subclass_override(self):
601 def test_subclass_override(self):
601 class Foo(HasTraits):
602 class Foo(HasTraits):
602 t = This()
603 t = This()
603 class Bar(Foo):
604 class Bar(Foo):
604 t = This()
605 t = This()
605 f = Foo()
606 f = Foo()
606 b = Bar()
607 b = Bar()
607 f.t = b
608 f.t = b
608 self.assertEqual(f.t, b)
609 self.assertEqual(f.t, b)
609 self.assertRaises(TraitError, setattr, b, 't', f)
610 self.assertRaises(TraitError, setattr, b, 't', f)
610
611
611 class TraitTestBase(TestCase):
612 class TraitTestBase(TestCase):
612 """A best testing class for basic trait types."""
613 """A best testing class for basic trait types."""
613
614
614 def assign(self, value):
615 def assign(self, value):
615 self.obj.value = value
616 self.obj.value = value
616
617
617 def coerce(self, value):
618 def coerce(self, value):
618 return value
619 return value
619
620
620 def test_good_values(self):
621 def test_good_values(self):
621 if hasattr(self, '_good_values'):
622 if hasattr(self, '_good_values'):
622 for value in self._good_values:
623 for value in self._good_values:
623 self.assign(value)
624 self.assign(value)
624 self.assertEqual(self.obj.value, self.coerce(value))
625 self.assertEqual(self.obj.value, self.coerce(value))
625
626
626 def test_bad_values(self):
627 def test_bad_values(self):
627 if hasattr(self, '_bad_values'):
628 if hasattr(self, '_bad_values'):
628 for value in self._bad_values:
629 for value in self._bad_values:
629 try:
630 try:
630 self.assertRaises(TraitError, self.assign, value)
631 self.assertRaises(TraitError, self.assign, value)
631 except AssertionError:
632 except AssertionError:
632 assert False, value
633 assert False, value
633
634
634 def test_default_value(self):
635 def test_default_value(self):
635 if hasattr(self, '_default_value'):
636 if hasattr(self, '_default_value'):
636 self.assertEqual(self._default_value, self.obj.value)
637 self.assertEqual(self._default_value, self.obj.value)
637
638
638 def tearDown(self):
639 def tearDown(self):
639 # restore default value after tests, if set
640 # restore default value after tests, if set
640 if hasattr(self, '_default_value'):
641 if hasattr(self, '_default_value'):
641 self.obj.value = self._default_value
642 self.obj.value = self._default_value
642
643
643
644
644 class AnyTrait(HasTraits):
645 class AnyTrait(HasTraits):
645
646
646 value = Any
647 value = Any
647
648
648 class AnyTraitTest(TraitTestBase):
649 class AnyTraitTest(TraitTestBase):
649
650
650 obj = AnyTrait()
651 obj = AnyTrait()
651
652
652 _default_value = None
653 _default_value = None
653 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
654 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
654 _bad_values = []
655 _bad_values = []
655
656
656
657
657 class IntTrait(HasTraits):
658 class IntTrait(HasTraits):
658
659
659 value = Int(99)
660 value = Int(99)
660
661
661 class TestInt(TraitTestBase):
662 class TestInt(TraitTestBase):
662
663
663 obj = IntTrait()
664 obj = IntTrait()
664 _default_value = 99
665 _default_value = 99
665 _good_values = [10, -10]
666 _good_values = [10, -10]
666 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j,
667 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j,
667 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
668 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
668 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
669 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
669 if not py3compat.PY3:
670 if not py3compat.PY3:
670 _bad_values.extend([10L, -10L, 10*sys.maxint, -10*sys.maxint])
671 _bad_values.extend([10L, -10L, 10*sys.maxint, -10*sys.maxint])
671
672
672
673
673 class LongTrait(HasTraits):
674 class LongTrait(HasTraits):
674
675
675 value = Long(99L)
676 value = Long(99L)
676
677
677 class TestLong(TraitTestBase):
678 class TestLong(TraitTestBase):
678
679
679 obj = LongTrait()
680 obj = LongTrait()
680
681
681 _default_value = 99L
682 _default_value = 99L
682 _good_values = [10, -10, 10L, -10L]
683 _good_values = [10, -10, 10L, -10L]
683 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
684 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
684 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
685 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
685 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
686 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
686 u'-10.1']
687 u'-10.1']
687 if not py3compat.PY3:
688 if not py3compat.PY3:
688 # maxint undefined on py3, because int == long
689 # maxint undefined on py3, because int == long
689 _good_values.extend([10*sys.maxint, -10*sys.maxint])
690 _good_values.extend([10*sys.maxint, -10*sys.maxint])
690
691
691 @skipif(py3compat.PY3, "not relevant on py3")
692 @skipif(py3compat.PY3, "not relevant on py3")
692 def test_cast_small(self):
693 def test_cast_small(self):
693 """Long casts ints to long"""
694 """Long casts ints to long"""
694 self.obj.value = 10
695 self.obj.value = 10
695 self.assertEqual(type(self.obj.value), long)
696 self.assertEqual(type(self.obj.value), long)
696
697
697
698
698 class IntegerTrait(HasTraits):
699 class IntegerTrait(HasTraits):
699 value = Integer(1)
700 value = Integer(1)
700
701
701 class TestInteger(TestLong):
702 class TestInteger(TestLong):
702 obj = IntegerTrait()
703 obj = IntegerTrait()
703 _default_value = 1
704 _default_value = 1
704
705
705 def coerce(self, n):
706 def coerce(self, n):
706 return int(n)
707 return int(n)
707
708
708 @skipif(py3compat.PY3, "not relevant on py3")
709 @skipif(py3compat.PY3, "not relevant on py3")
709 def test_cast_small(self):
710 def test_cast_small(self):
710 """Integer casts small longs to int"""
711 """Integer casts small longs to int"""
711 if py3compat.PY3:
712 if py3compat.PY3:
712 raise SkipTest("not relevant on py3")
713 raise SkipTest("not relevant on py3")
713
714
714 self.obj.value = 100L
715 self.obj.value = 100L
715 self.assertEqual(type(self.obj.value), int)
716 self.assertEqual(type(self.obj.value), int)
716
717
717
718
718 class FloatTrait(HasTraits):
719 class FloatTrait(HasTraits):
719
720
720 value = Float(99.0)
721 value = Float(99.0)
721
722
722 class TestFloat(TraitTestBase):
723 class TestFloat(TraitTestBase):
723
724
724 obj = FloatTrait()
725 obj = FloatTrait()
725
726
726 _default_value = 99.0
727 _default_value = 99.0
727 _good_values = [10, -10, 10.1, -10.1]
728 _good_values = [10, -10, 10.1, -10.1]
728 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None,
729 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None,
729 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
730 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
730 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
731 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
731 if not py3compat.PY3:
732 if not py3compat.PY3:
732 _bad_values.extend([10L, -10L])
733 _bad_values.extend([10L, -10L])
733
734
734
735
735 class ComplexTrait(HasTraits):
736 class ComplexTrait(HasTraits):
736
737
737 value = Complex(99.0-99.0j)
738 value = Complex(99.0-99.0j)
738
739
739 class TestComplex(TraitTestBase):
740 class TestComplex(TraitTestBase):
740
741
741 obj = ComplexTrait()
742 obj = ComplexTrait()
742
743
743 _default_value = 99.0-99.0j
744 _default_value = 99.0-99.0j
744 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
745 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
745 10.1j, 10.1+10.1j, 10.1-10.1j]
746 10.1j, 10.1+10.1j, 10.1-10.1j]
746 _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
747 _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
747 if not py3compat.PY3:
748 if not py3compat.PY3:
748 _bad_values.extend([10L, -10L])
749 _bad_values.extend([10L, -10L])
749
750
750
751
751 class BytesTrait(HasTraits):
752 class BytesTrait(HasTraits):
752
753
753 value = Bytes(b'string')
754 value = Bytes(b'string')
754
755
755 class TestBytes(TraitTestBase):
756 class TestBytes(TraitTestBase):
756
757
757 obj = BytesTrait()
758 obj = BytesTrait()
758
759
759 _default_value = b'string'
760 _default_value = b'string'
760 _good_values = [b'10', b'-10', b'10L',
761 _good_values = [b'10', b'-10', b'10L',
761 b'-10L', b'10.1', b'-10.1', b'string']
762 b'-10L', b'10.1', b'-10.1', b'string']
762 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
763 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
763 ['ten'],{'ten': 10},(10,), None, u'string']
764 ['ten'],{'ten': 10},(10,), None, u'string']
764
765
765
766
766 class UnicodeTrait(HasTraits):
767 class UnicodeTrait(HasTraits):
767
768
768 value = Unicode(u'unicode')
769 value = Unicode(u'unicode')
769
770
770 class TestUnicode(TraitTestBase):
771 class TestUnicode(TraitTestBase):
771
772
772 obj = UnicodeTrait()
773 obj = UnicodeTrait()
773
774
774 _default_value = u'unicode'
775 _default_value = u'unicode'
775 _good_values = ['10', '-10', '10L', '-10L', '10.1',
776 _good_values = ['10', '-10', '10L', '-10L', '10.1',
776 '-10.1', '', u'', 'string', u'string', u"€"]
777 '-10.1', '', u'', 'string', u'string', u"€"]
777 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
778 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
778 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
779 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
779
780
780
781
781 class ObjectNameTrait(HasTraits):
782 class ObjectNameTrait(HasTraits):
782 value = ObjectName("abc")
783 value = ObjectName("abc")
783
784
784 class TestObjectName(TraitTestBase):
785 class TestObjectName(TraitTestBase):
785 obj = ObjectNameTrait()
786 obj = ObjectNameTrait()
786
787
787 _default_value = "abc"
788 _default_value = "abc"
788 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
789 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
789 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
790 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
790 object(), object]
791 object(), object]
791 if sys.version_info[0] < 3:
792 if sys.version_info[0] < 3:
792 _bad_values.append(u"ΓΎ")
793 _bad_values.append(u"ΓΎ")
793 else:
794 else:
794 _good_values.append(u"ΓΎ") # ΓΎ=1 is valid in Python 3 (PEP 3131).
795 _good_values.append(u"ΓΎ") # ΓΎ=1 is valid in Python 3 (PEP 3131).
795
796
796
797
797 class DottedObjectNameTrait(HasTraits):
798 class DottedObjectNameTrait(HasTraits):
798 value = DottedObjectName("a.b")
799 value = DottedObjectName("a.b")
799
800
800 class TestDottedObjectName(TraitTestBase):
801 class TestDottedObjectName(TraitTestBase):
801 obj = DottedObjectNameTrait()
802 obj = DottedObjectNameTrait()
802
803
803 _default_value = "a.b"
804 _default_value = "a.b"
804 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
805 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
805 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."]
806 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."]
806 if sys.version_info[0] < 3:
807 if sys.version_info[0] < 3:
807 _bad_values.append(u"t.ΓΎ")
808 _bad_values.append(u"t.ΓΎ")
808 else:
809 else:
809 _good_values.append(u"t.ΓΎ")
810 _good_values.append(u"t.ΓΎ")
810
811
811
812
812 class TCPAddressTrait(HasTraits):
813 class TCPAddressTrait(HasTraits):
813
814
814 value = TCPAddress()
815 value = TCPAddress()
815
816
816 class TestTCPAddress(TraitTestBase):
817 class TestTCPAddress(TraitTestBase):
817
818
818 obj = TCPAddressTrait()
819 obj = TCPAddressTrait()
819
820
820 _default_value = ('127.0.0.1',0)
821 _default_value = ('127.0.0.1',0)
821 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
822 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
822 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
823 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
823
824
824 class ListTrait(HasTraits):
825 class ListTrait(HasTraits):
825
826
826 value = List(Int)
827 value = List(Int)
827
828
828 class TestList(TraitTestBase):
829 class TestList(TraitTestBase):
829
830
830 obj = ListTrait()
831 obj = ListTrait()
831
832
832 _default_value = []
833 _default_value = []
833 _good_values = [[], [1], range(10)]
834 _good_values = [[], [1], range(10)]
834 _bad_values = [10, [1,'a'], 'a', (1,2)]
835 _bad_values = [10, [1,'a'], 'a', (1,2)]
835
836
836 class LenListTrait(HasTraits):
837 class LenListTrait(HasTraits):
837
838
838 value = List(Int, [0], minlen=1, maxlen=2)
839 value = List(Int, [0], minlen=1, maxlen=2)
839
840
840 class TestLenList(TraitTestBase):
841 class TestLenList(TraitTestBase):
841
842
842 obj = LenListTrait()
843 obj = LenListTrait()
843
844
844 _default_value = [0]
845 _default_value = [0]
845 _good_values = [[1], range(2)]
846 _good_values = [[1], range(2)]
846 _bad_values = [10, [1,'a'], 'a', (1,2), [], range(3)]
847 _bad_values = [10, [1,'a'], 'a', (1,2), [], range(3)]
847
848
848 class TupleTrait(HasTraits):
849 class TupleTrait(HasTraits):
849
850
850 value = Tuple(Int)
851 value = Tuple(Int)
851
852
852 class TestTupleTrait(TraitTestBase):
853 class TestTupleTrait(TraitTestBase):
853
854
854 obj = TupleTrait()
855 obj = TupleTrait()
855
856
856 _default_value = None
857 _default_value = None
857 _good_values = [(1,), None,(0,)]
858 _good_values = [(1,), None,(0,)]
858 _bad_values = [10, (1,2), [1],('a'), ()]
859 _bad_values = [10, (1,2), [1],('a'), ()]
859
860
860 def test_invalid_args(self):
861 def test_invalid_args(self):
861 self.assertRaises(TypeError, Tuple, 5)
862 self.assertRaises(TypeError, Tuple, 5)
862 self.assertRaises(TypeError, Tuple, default_value='hello')
863 self.assertRaises(TypeError, Tuple, default_value='hello')
863 t = Tuple(Int, CBytes, default_value=(1,5))
864 t = Tuple(Int, CBytes, default_value=(1,5))
864
865
865 class LooseTupleTrait(HasTraits):
866 class LooseTupleTrait(HasTraits):
866
867
867 value = Tuple((1,2,3))
868 value = Tuple((1,2,3))
868
869
869 class TestLooseTupleTrait(TraitTestBase):
870 class TestLooseTupleTrait(TraitTestBase):
870
871
871 obj = LooseTupleTrait()
872 obj = LooseTupleTrait()
872
873
873 _default_value = (1,2,3)
874 _default_value = (1,2,3)
874 _good_values = [(1,), None, (0,), tuple(range(5)), tuple('hello'), ('a',5), ()]
875 _good_values = [(1,), None, (0,), tuple(range(5)), tuple('hello'), ('a',5), ()]
875 _bad_values = [10, 'hello', [1], []]
876 _bad_values = [10, 'hello', [1], []]
876
877
877 def test_invalid_args(self):
878 def test_invalid_args(self):
878 self.assertRaises(TypeError, Tuple, 5)
879 self.assertRaises(TypeError, Tuple, 5)
879 self.assertRaises(TypeError, Tuple, default_value='hello')
880 self.assertRaises(TypeError, Tuple, default_value='hello')
880 t = Tuple(Int, CBytes, default_value=(1,5))
881 t = Tuple(Int, CBytes, default_value=(1,5))
881
882
882
883
883 class MultiTupleTrait(HasTraits):
884 class MultiTupleTrait(HasTraits):
884
885
885 value = Tuple(Int, Bytes, default_value=[99,b'bottles'])
886 value = Tuple(Int, Bytes, default_value=[99,b'bottles'])
886
887
887 class TestMultiTuple(TraitTestBase):
888 class TestMultiTuple(TraitTestBase):
888
889
889 obj = MultiTupleTrait()
890 obj = MultiTupleTrait()
890
891
891 _default_value = (99,b'bottles')
892 _default_value = (99,b'bottles')
892 _good_values = [(1,b'a'), (2,b'b')]
893 _good_values = [(1,b'a'), (2,b'b')]
893 _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a'))
894 _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a'))
894
895
895 class CRegExpTrait(HasTraits):
896 class CRegExpTrait(HasTraits):
896
897
897 value = CRegExp(r'')
898 value = CRegExp(r'')
898
899
899 class TestCRegExp(TraitTestBase):
900 class TestCRegExp(TraitTestBase):
900
901
901 def coerce(self, value):
902 def coerce(self, value):
902 return re.compile(value)
903 return re.compile(value)
903
904
904 obj = CRegExpTrait()
905 obj = CRegExpTrait()
905
906
906 _default_value = re.compile(r'')
907 _default_value = re.compile(r'')
907 _good_values = [r'\d+', re.compile(r'\d+')]
908 _good_values = [r'\d+', re.compile(r'\d+')]
908 _bad_values = [r'(', None, ()]
909 _bad_values = [r'(', None, ()]
910
911 class DictTrait(HasTraits):
912 value = Dict()
913
914 def test_dict_assignment():
915 d = dict()
916 c = DictTrait()
917 c.value = d
918 d['a'] = 5
919 nt.assert_equal(d, c.value)
920 nt.assert_true(c.value is d)
@@ -1,1430 +1,1430 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A lightweight Traits like module.
3 A lightweight Traits like module.
4
4
5 This is designed to provide a lightweight, simple, pure Python version of
5 This is designed to provide a lightweight, simple, pure Python version of
6 many of the capabilities of enthought.traits. This includes:
6 many of the capabilities of enthought.traits. This includes:
7
7
8 * Validation
8 * Validation
9 * Type specification with defaults
9 * Type specification with defaults
10 * Static and dynamic notification
10 * Static and dynamic notification
11 * Basic predefined types
11 * Basic predefined types
12 * An API that is similar to enthought.traits
12 * An API that is similar to enthought.traits
13
13
14 We don't support:
14 We don't support:
15
15
16 * Delegation
16 * Delegation
17 * Automatic GUI generation
17 * Automatic GUI generation
18 * A full set of trait types. Most importantly, we don't provide container
18 * A full set of trait types. Most importantly, we don't provide container
19 traits (list, dict, tuple) that can trigger notifications if their
19 traits (list, dict, tuple) that can trigger notifications if their
20 contents change.
20 contents change.
21 * API compatibility with enthought.traits
21 * API compatibility with enthought.traits
22
22
23 There are also some important difference in our design:
23 There are also some important difference in our design:
24
24
25 * enthought.traits does not validate default values. We do.
25 * enthought.traits does not validate default values. We do.
26
26
27 We choose to create this module because we need these capabilities, but
27 We choose to create this module because we need these capabilities, but
28 we need them to be pure Python so they work in all Python implementations,
28 we need them to be pure Python so they work in all Python implementations,
29 including Jython and IronPython.
29 including Jython and IronPython.
30
30
31 Authors:
31 Authors:
32
32
33 * Brian Granger
33 * Brian Granger
34 * Enthought, Inc. Some of the code in this file comes from enthought.traits
34 * Enthought, Inc. Some of the code in this file comes from enthought.traits
35 and is licensed under the BSD license. Also, many of the ideas also come
35 and is licensed under the BSD license. Also, many of the ideas also come
36 from enthought.traits even though our implementation is very different.
36 from enthought.traits even though our implementation is very different.
37 """
37 """
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Copyright (C) 2008-2011 The IPython Development Team
40 # Copyright (C) 2008-2011 The IPython Development Team
41 #
41 #
42 # Distributed under the terms of the BSD License. The full license is in
42 # Distributed under the terms of the BSD License. The full license is in
43 # the file COPYING, distributed as part of this software.
43 # the file COPYING, distributed as part of this software.
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Imports
47 # Imports
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50
50
51 import inspect
51 import inspect
52 import re
52 import re
53 import sys
53 import sys
54 import types
54 import types
55 from types import FunctionType
55 from types import FunctionType
56 try:
56 try:
57 from types import ClassType, InstanceType
57 from types import ClassType, InstanceType
58 ClassTypes = (ClassType, type)
58 ClassTypes = (ClassType, type)
59 except:
59 except:
60 ClassTypes = (type,)
60 ClassTypes = (type,)
61
61
62 from .importstring import import_item
62 from .importstring import import_item
63 from IPython.utils import py3compat
63 from IPython.utils import py3compat
64
64
65 SequenceTypes = (list, tuple, set, frozenset)
65 SequenceTypes = (list, tuple, set, frozenset)
66
66
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68 # Basic classes
68 # Basic classes
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70
70
71
71
72 class NoDefaultSpecified ( object ): pass
72 class NoDefaultSpecified ( object ): pass
73 NoDefaultSpecified = NoDefaultSpecified()
73 NoDefaultSpecified = NoDefaultSpecified()
74
74
75
75
76 class Undefined ( object ): pass
76 class Undefined ( object ): pass
77 Undefined = Undefined()
77 Undefined = Undefined()
78
78
79 class TraitError(Exception):
79 class TraitError(Exception):
80 pass
80 pass
81
81
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83 # Utilities
83 # Utilities
84 #-----------------------------------------------------------------------------
84 #-----------------------------------------------------------------------------
85
85
86
86
87 def class_of ( object ):
87 def class_of ( object ):
88 """ Returns a string containing the class name of an object with the
88 """ Returns a string containing the class name of an object with the
89 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
89 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
90 'a PlotValue').
90 'a PlotValue').
91 """
91 """
92 if isinstance( object, basestring ):
92 if isinstance( object, basestring ):
93 return add_article( object )
93 return add_article( object )
94
94
95 return add_article( object.__class__.__name__ )
95 return add_article( object.__class__.__name__ )
96
96
97
97
98 def add_article ( name ):
98 def add_article ( name ):
99 """ Returns a string containing the correct indefinite article ('a' or 'an')
99 """ Returns a string containing the correct indefinite article ('a' or 'an')
100 prefixed to the specified string.
100 prefixed to the specified string.
101 """
101 """
102 if name[:1].lower() in 'aeiou':
102 if name[:1].lower() in 'aeiou':
103 return 'an ' + name
103 return 'an ' + name
104
104
105 return 'a ' + name
105 return 'a ' + name
106
106
107
107
108 def repr_type(obj):
108 def repr_type(obj):
109 """ Return a string representation of a value and its type for readable
109 """ Return a string representation of a value and its type for readable
110 error messages.
110 error messages.
111 """
111 """
112 the_type = type(obj)
112 the_type = type(obj)
113 if (not py3compat.PY3) and the_type is InstanceType:
113 if (not py3compat.PY3) and the_type is InstanceType:
114 # Old-style class.
114 # Old-style class.
115 the_type = obj.__class__
115 the_type = obj.__class__
116 msg = '%r %r' % (obj, the_type)
116 msg = '%r %r' % (obj, the_type)
117 return msg
117 return msg
118
118
119
119
120 def is_trait(t):
120 def is_trait(t):
121 """ Returns whether the given value is an instance or subclass of TraitType.
121 """ Returns whether the given value is an instance or subclass of TraitType.
122 """
122 """
123 return (isinstance(t, TraitType) or
123 return (isinstance(t, TraitType) or
124 (isinstance(t, type) and issubclass(t, TraitType)))
124 (isinstance(t, type) and issubclass(t, TraitType)))
125
125
126
126
127 def parse_notifier_name(name):
127 def parse_notifier_name(name):
128 """Convert the name argument to a list of names.
128 """Convert the name argument to a list of names.
129
129
130 Examples
130 Examples
131 --------
131 --------
132
132
133 >>> parse_notifier_name('a')
133 >>> parse_notifier_name('a')
134 ['a']
134 ['a']
135 >>> parse_notifier_name(['a','b'])
135 >>> parse_notifier_name(['a','b'])
136 ['a', 'b']
136 ['a', 'b']
137 >>> parse_notifier_name(None)
137 >>> parse_notifier_name(None)
138 ['anytrait']
138 ['anytrait']
139 """
139 """
140 if isinstance(name, str):
140 if isinstance(name, str):
141 return [name]
141 return [name]
142 elif name is None:
142 elif name is None:
143 return ['anytrait']
143 return ['anytrait']
144 elif isinstance(name, (list, tuple)):
144 elif isinstance(name, (list, tuple)):
145 for n in name:
145 for n in name:
146 assert isinstance(n, str), "names must be strings"
146 assert isinstance(n, str), "names must be strings"
147 return name
147 return name
148
148
149
149
150 class _SimpleTest:
150 class _SimpleTest:
151 def __init__ ( self, value ): self.value = value
151 def __init__ ( self, value ): self.value = value
152 def __call__ ( self, test ):
152 def __call__ ( self, test ):
153 return test == self.value
153 return test == self.value
154 def __repr__(self):
154 def __repr__(self):
155 return "<SimpleTest(%r)" % self.value
155 return "<SimpleTest(%r)" % self.value
156 def __str__(self):
156 def __str__(self):
157 return self.__repr__()
157 return self.__repr__()
158
158
159
159
160 def getmembers(object, predicate=None):
160 def getmembers(object, predicate=None):
161 """A safe version of inspect.getmembers that handles missing attributes.
161 """A safe version of inspect.getmembers that handles missing attributes.
162
162
163 This is useful when there are descriptor based attributes that for
163 This is useful when there are descriptor based attributes that for
164 some reason raise AttributeError even though they exist. This happens
164 some reason raise AttributeError even though they exist. This happens
165 in zope.inteface with the __provides__ attribute.
165 in zope.inteface with the __provides__ attribute.
166 """
166 """
167 results = []
167 results = []
168 for key in dir(object):
168 for key in dir(object):
169 try:
169 try:
170 value = getattr(object, key)
170 value = getattr(object, key)
171 except AttributeError:
171 except AttributeError:
172 pass
172 pass
173 else:
173 else:
174 if not predicate or predicate(value):
174 if not predicate or predicate(value):
175 results.append((key, value))
175 results.append((key, value))
176 results.sort()
176 results.sort()
177 return results
177 return results
178
178
179
179
180 #-----------------------------------------------------------------------------
180 #-----------------------------------------------------------------------------
181 # Base TraitType for all traits
181 # Base TraitType for all traits
182 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
183
183
184
184
185 class TraitType(object):
185 class TraitType(object):
186 """A base class for all trait descriptors.
186 """A base class for all trait descriptors.
187
187
188 Notes
188 Notes
189 -----
189 -----
190 Our implementation of traits is based on Python's descriptor
190 Our implementation of traits is based on Python's descriptor
191 prototol. This class is the base class for all such descriptors. The
191 prototol. This class is the base class for all such descriptors. The
192 only magic we use is a custom metaclass for the main :class:`HasTraits`
192 only magic we use is a custom metaclass for the main :class:`HasTraits`
193 class that does the following:
193 class that does the following:
194
194
195 1. Sets the :attr:`name` attribute of every :class:`TraitType`
195 1. Sets the :attr:`name` attribute of every :class:`TraitType`
196 instance in the class dict to the name of the attribute.
196 instance in the class dict to the name of the attribute.
197 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
197 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
198 instance in the class dict to the *class* that declared the trait.
198 instance in the class dict to the *class* that declared the trait.
199 This is used by the :class:`This` trait to allow subclasses to
199 This is used by the :class:`This` trait to allow subclasses to
200 accept superclasses for :class:`This` values.
200 accept superclasses for :class:`This` values.
201 """
201 """
202
202
203
203
204 metadata = {}
204 metadata = {}
205 default_value = Undefined
205 default_value = Undefined
206 info_text = 'any value'
206 info_text = 'any value'
207
207
208 def __init__(self, default_value=NoDefaultSpecified, **metadata):
208 def __init__(self, default_value=NoDefaultSpecified, **metadata):
209 """Create a TraitType.
209 """Create a TraitType.
210 """
210 """
211 if default_value is not NoDefaultSpecified:
211 if default_value is not NoDefaultSpecified:
212 self.default_value = default_value
212 self.default_value = default_value
213
213
214 if len(metadata) > 0:
214 if len(metadata) > 0:
215 if len(self.metadata) > 0:
215 if len(self.metadata) > 0:
216 self._metadata = self.metadata.copy()
216 self._metadata = self.metadata.copy()
217 self._metadata.update(metadata)
217 self._metadata.update(metadata)
218 else:
218 else:
219 self._metadata = metadata
219 self._metadata = metadata
220 else:
220 else:
221 self._metadata = self.metadata
221 self._metadata = self.metadata
222
222
223 self.init()
223 self.init()
224
224
225 def init(self):
225 def init(self):
226 pass
226 pass
227
227
228 def get_default_value(self):
228 def get_default_value(self):
229 """Create a new instance of the default value."""
229 """Create a new instance of the default value."""
230 return self.default_value
230 return self.default_value
231
231
232 def instance_init(self, obj):
232 def instance_init(self, obj):
233 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
233 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
234
234
235 Some stages of initialization must be delayed until the parent
235 Some stages of initialization must be delayed until the parent
236 :class:`HasTraits` instance has been created. This method is
236 :class:`HasTraits` instance has been created. This method is
237 called in :meth:`HasTraits.__new__` after the instance has been
237 called in :meth:`HasTraits.__new__` after the instance has been
238 created.
238 created.
239
239
240 This method trigger the creation and validation of default values
240 This method trigger the creation and validation of default values
241 and also things like the resolution of str given class names in
241 and also things like the resolution of str given class names in
242 :class:`Type` and :class`Instance`.
242 :class:`Type` and :class`Instance`.
243
243
244 Parameters
244 Parameters
245 ----------
245 ----------
246 obj : :class:`HasTraits` instance
246 obj : :class:`HasTraits` instance
247 The parent :class:`HasTraits` instance that has just been
247 The parent :class:`HasTraits` instance that has just been
248 created.
248 created.
249 """
249 """
250 self.set_default_value(obj)
250 self.set_default_value(obj)
251
251
252 def set_default_value(self, obj):
252 def set_default_value(self, obj):
253 """Set the default value on a per instance basis.
253 """Set the default value on a per instance basis.
254
254
255 This method is called by :meth:`instance_init` to create and
255 This method is called by :meth:`instance_init` to create and
256 validate the default value. The creation and validation of
256 validate the default value. The creation and validation of
257 default values must be delayed until the parent :class:`HasTraits`
257 default values must be delayed until the parent :class:`HasTraits`
258 class has been instantiated.
258 class has been instantiated.
259 """
259 """
260 # Check for a deferred initializer defined in the same class as the
260 # Check for a deferred initializer defined in the same class as the
261 # trait declaration or above.
261 # trait declaration or above.
262 mro = type(obj).mro()
262 mro = type(obj).mro()
263 meth_name = '_%s_default' % self.name
263 meth_name = '_%s_default' % self.name
264 for cls in mro[:mro.index(self.this_class)+1]:
264 for cls in mro[:mro.index(self.this_class)+1]:
265 if meth_name in cls.__dict__:
265 if meth_name in cls.__dict__:
266 break
266 break
267 else:
267 else:
268 # We didn't find one. Do static initialization.
268 # We didn't find one. Do static initialization.
269 dv = self.get_default_value()
269 dv = self.get_default_value()
270 newdv = self._validate(obj, dv)
270 newdv = self._validate(obj, dv)
271 obj._trait_values[self.name] = newdv
271 obj._trait_values[self.name] = newdv
272 return
272 return
273 # Complete the dynamic initialization.
273 # Complete the dynamic initialization.
274 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
274 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
275
275
276 def __get__(self, obj, cls=None):
276 def __get__(self, obj, cls=None):
277 """Get the value of the trait by self.name for the instance.
277 """Get the value of the trait by self.name for the instance.
278
278
279 Default values are instantiated when :meth:`HasTraits.__new__`
279 Default values are instantiated when :meth:`HasTraits.__new__`
280 is called. Thus by the time this method gets called either the
280 is called. Thus by the time this method gets called either the
281 default value or a user defined value (they called :meth:`__set__`)
281 default value or a user defined value (they called :meth:`__set__`)
282 is in the :class:`HasTraits` instance.
282 is in the :class:`HasTraits` instance.
283 """
283 """
284 if obj is None:
284 if obj is None:
285 return self
285 return self
286 else:
286 else:
287 try:
287 try:
288 value = obj._trait_values[self.name]
288 value = obj._trait_values[self.name]
289 except KeyError:
289 except KeyError:
290 # Check for a dynamic initializer.
290 # Check for a dynamic initializer.
291 if self.name in obj._trait_dyn_inits:
291 if self.name in obj._trait_dyn_inits:
292 value = obj._trait_dyn_inits[self.name](obj)
292 value = obj._trait_dyn_inits[self.name](obj)
293 # FIXME: Do we really validate here?
293 # FIXME: Do we really validate here?
294 value = self._validate(obj, value)
294 value = self._validate(obj, value)
295 obj._trait_values[self.name] = value
295 obj._trait_values[self.name] = value
296 return value
296 return value
297 else:
297 else:
298 raise TraitError('Unexpected error in TraitType: '
298 raise TraitError('Unexpected error in TraitType: '
299 'both default value and dynamic initializer are '
299 'both default value and dynamic initializer are '
300 'absent.')
300 'absent.')
301 except Exception:
301 except Exception:
302 # HasTraits should call set_default_value to populate
302 # HasTraits should call set_default_value to populate
303 # this. So this should never be reached.
303 # this. So this should never be reached.
304 raise TraitError('Unexpected error in TraitType: '
304 raise TraitError('Unexpected error in TraitType: '
305 'default value not set properly')
305 'default value not set properly')
306 else:
306 else:
307 return value
307 return value
308
308
309 def __set__(self, obj, value):
309 def __set__(self, obj, value):
310 new_value = self._validate(obj, value)
310 new_value = self._validate(obj, value)
311 old_value = self.__get__(obj)
311 old_value = self.__get__(obj)
312 obj._trait_values[self.name] = new_value
312 if old_value != new_value:
313 if old_value != new_value:
313 obj._trait_values[self.name] = new_value
314 obj._notify_trait(self.name, old_value, new_value)
314 obj._notify_trait(self.name, old_value, new_value)
315
315
316 def _validate(self, obj, value):
316 def _validate(self, obj, value):
317 if hasattr(self, 'validate'):
317 if hasattr(self, 'validate'):
318 return self.validate(obj, value)
318 return self.validate(obj, value)
319 elif hasattr(self, 'is_valid_for'):
319 elif hasattr(self, 'is_valid_for'):
320 valid = self.is_valid_for(value)
320 valid = self.is_valid_for(value)
321 if valid:
321 if valid:
322 return value
322 return value
323 else:
323 else:
324 raise TraitError('invalid value for type: %r' % value)
324 raise TraitError('invalid value for type: %r' % value)
325 elif hasattr(self, 'value_for'):
325 elif hasattr(self, 'value_for'):
326 return self.value_for(value)
326 return self.value_for(value)
327 else:
327 else:
328 return value
328 return value
329
329
330 def info(self):
330 def info(self):
331 return self.info_text
331 return self.info_text
332
332
333 def error(self, obj, value):
333 def error(self, obj, value):
334 if obj is not None:
334 if obj is not None:
335 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
335 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
336 % (self.name, class_of(obj),
336 % (self.name, class_of(obj),
337 self.info(), repr_type(value))
337 self.info(), repr_type(value))
338 else:
338 else:
339 e = "The '%s' trait must be %s, but a value of %r was specified." \
339 e = "The '%s' trait must be %s, but a value of %r was specified." \
340 % (self.name, self.info(), repr_type(value))
340 % (self.name, self.info(), repr_type(value))
341 raise TraitError(e)
341 raise TraitError(e)
342
342
343 def get_metadata(self, key):
343 def get_metadata(self, key):
344 return getattr(self, '_metadata', {}).get(key, None)
344 return getattr(self, '_metadata', {}).get(key, None)
345
345
346 def set_metadata(self, key, value):
346 def set_metadata(self, key, value):
347 getattr(self, '_metadata', {})[key] = value
347 getattr(self, '_metadata', {})[key] = value
348
348
349
349
350 #-----------------------------------------------------------------------------
350 #-----------------------------------------------------------------------------
351 # The HasTraits implementation
351 # The HasTraits implementation
352 #-----------------------------------------------------------------------------
352 #-----------------------------------------------------------------------------
353
353
354
354
355 class MetaHasTraits(type):
355 class MetaHasTraits(type):
356 """A metaclass for HasTraits.
356 """A metaclass for HasTraits.
357
357
358 This metaclass makes sure that any TraitType class attributes are
358 This metaclass makes sure that any TraitType class attributes are
359 instantiated and sets their name attribute.
359 instantiated and sets their name attribute.
360 """
360 """
361
361
362 def __new__(mcls, name, bases, classdict):
362 def __new__(mcls, name, bases, classdict):
363 """Create the HasTraits class.
363 """Create the HasTraits class.
364
364
365 This instantiates all TraitTypes in the class dict and sets their
365 This instantiates all TraitTypes in the class dict and sets their
366 :attr:`name` attribute.
366 :attr:`name` attribute.
367 """
367 """
368 # print "MetaHasTraitlets (mcls, name): ", mcls, name
368 # print "MetaHasTraitlets (mcls, name): ", mcls, name
369 # print "MetaHasTraitlets (bases): ", bases
369 # print "MetaHasTraitlets (bases): ", bases
370 # print "MetaHasTraitlets (classdict): ", classdict
370 # print "MetaHasTraitlets (classdict): ", classdict
371 for k,v in classdict.iteritems():
371 for k,v in classdict.iteritems():
372 if isinstance(v, TraitType):
372 if isinstance(v, TraitType):
373 v.name = k
373 v.name = k
374 elif inspect.isclass(v):
374 elif inspect.isclass(v):
375 if issubclass(v, TraitType):
375 if issubclass(v, TraitType):
376 vinst = v()
376 vinst = v()
377 vinst.name = k
377 vinst.name = k
378 classdict[k] = vinst
378 classdict[k] = vinst
379 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
379 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
380
380
381 def __init__(cls, name, bases, classdict):
381 def __init__(cls, name, bases, classdict):
382 """Finish initializing the HasTraits class.
382 """Finish initializing the HasTraits class.
383
383
384 This sets the :attr:`this_class` attribute of each TraitType in the
384 This sets the :attr:`this_class` attribute of each TraitType in the
385 class dict to the newly created class ``cls``.
385 class dict to the newly created class ``cls``.
386 """
386 """
387 for k, v in classdict.iteritems():
387 for k, v in classdict.iteritems():
388 if isinstance(v, TraitType):
388 if isinstance(v, TraitType):
389 v.this_class = cls
389 v.this_class = cls
390 super(MetaHasTraits, cls).__init__(name, bases, classdict)
390 super(MetaHasTraits, cls).__init__(name, bases, classdict)
391
391
392 class HasTraits(object):
392 class HasTraits(object):
393
393
394 __metaclass__ = MetaHasTraits
394 __metaclass__ = MetaHasTraits
395
395
396 def __new__(cls, **kw):
396 def __new__(cls, **kw):
397 # This is needed because in Python 2.6 object.__new__ only accepts
397 # This is needed because in Python 2.6 object.__new__ only accepts
398 # the cls argument.
398 # the cls argument.
399 new_meth = super(HasTraits, cls).__new__
399 new_meth = super(HasTraits, cls).__new__
400 if new_meth is object.__new__:
400 if new_meth is object.__new__:
401 inst = new_meth(cls)
401 inst = new_meth(cls)
402 else:
402 else:
403 inst = new_meth(cls, **kw)
403 inst = new_meth(cls, **kw)
404 inst._trait_values = {}
404 inst._trait_values = {}
405 inst._trait_notifiers = {}
405 inst._trait_notifiers = {}
406 inst._trait_dyn_inits = {}
406 inst._trait_dyn_inits = {}
407 # Here we tell all the TraitType instances to set their default
407 # Here we tell all the TraitType instances to set their default
408 # values on the instance.
408 # values on the instance.
409 for key in dir(cls):
409 for key in dir(cls):
410 # Some descriptors raise AttributeError like zope.interface's
410 # Some descriptors raise AttributeError like zope.interface's
411 # __provides__ attributes even though they exist. This causes
411 # __provides__ attributes even though they exist. This causes
412 # AttributeErrors even though they are listed in dir(cls).
412 # AttributeErrors even though they are listed in dir(cls).
413 try:
413 try:
414 value = getattr(cls, key)
414 value = getattr(cls, key)
415 except AttributeError:
415 except AttributeError:
416 pass
416 pass
417 else:
417 else:
418 if isinstance(value, TraitType):
418 if isinstance(value, TraitType):
419 value.instance_init(inst)
419 value.instance_init(inst)
420
420
421 return inst
421 return inst
422
422
423 def __init__(self, **kw):
423 def __init__(self, **kw):
424 # Allow trait values to be set using keyword arguments.
424 # Allow trait values to be set using keyword arguments.
425 # We need to use setattr for this to trigger validation and
425 # We need to use setattr for this to trigger validation and
426 # notifications.
426 # notifications.
427 for key, value in kw.iteritems():
427 for key, value in kw.iteritems():
428 setattr(self, key, value)
428 setattr(self, key, value)
429
429
430 def _notify_trait(self, name, old_value, new_value):
430 def _notify_trait(self, name, old_value, new_value):
431
431
432 # First dynamic ones
432 # First dynamic ones
433 callables = self._trait_notifiers.get(name,[])
433 callables = self._trait_notifiers.get(name,[])
434 more_callables = self._trait_notifiers.get('anytrait',[])
434 more_callables = self._trait_notifiers.get('anytrait',[])
435 callables.extend(more_callables)
435 callables.extend(more_callables)
436
436
437 # Now static ones
437 # Now static ones
438 try:
438 try:
439 cb = getattr(self, '_%s_changed' % name)
439 cb = getattr(self, '_%s_changed' % name)
440 except:
440 except:
441 pass
441 pass
442 else:
442 else:
443 callables.append(cb)
443 callables.append(cb)
444
444
445 # Call them all now
445 # Call them all now
446 for c in callables:
446 for c in callables:
447 # Traits catches and logs errors here. I allow them to raise
447 # Traits catches and logs errors here. I allow them to raise
448 if callable(c):
448 if callable(c):
449 argspec = inspect.getargspec(c)
449 argspec = inspect.getargspec(c)
450 nargs = len(argspec[0])
450 nargs = len(argspec[0])
451 # Bound methods have an additional 'self' argument
451 # Bound methods have an additional 'self' argument
452 # I don't know how to treat unbound methods, but they
452 # I don't know how to treat unbound methods, but they
453 # can't really be used for callbacks.
453 # can't really be used for callbacks.
454 if isinstance(c, types.MethodType):
454 if isinstance(c, types.MethodType):
455 offset = -1
455 offset = -1
456 else:
456 else:
457 offset = 0
457 offset = 0
458 if nargs + offset == 0:
458 if nargs + offset == 0:
459 c()
459 c()
460 elif nargs + offset == 1:
460 elif nargs + offset == 1:
461 c(name)
461 c(name)
462 elif nargs + offset == 2:
462 elif nargs + offset == 2:
463 c(name, new_value)
463 c(name, new_value)
464 elif nargs + offset == 3:
464 elif nargs + offset == 3:
465 c(name, old_value, new_value)
465 c(name, old_value, new_value)
466 else:
466 else:
467 raise TraitError('a trait changed callback '
467 raise TraitError('a trait changed callback '
468 'must have 0-3 arguments.')
468 'must have 0-3 arguments.')
469 else:
469 else:
470 raise TraitError('a trait changed callback '
470 raise TraitError('a trait changed callback '
471 'must be callable.')
471 'must be callable.')
472
472
473
473
474 def _add_notifiers(self, handler, name):
474 def _add_notifiers(self, handler, name):
475 if name not in self._trait_notifiers:
475 if name not in self._trait_notifiers:
476 nlist = []
476 nlist = []
477 self._trait_notifiers[name] = nlist
477 self._trait_notifiers[name] = nlist
478 else:
478 else:
479 nlist = self._trait_notifiers[name]
479 nlist = self._trait_notifiers[name]
480 if handler not in nlist:
480 if handler not in nlist:
481 nlist.append(handler)
481 nlist.append(handler)
482
482
483 def _remove_notifiers(self, handler, name):
483 def _remove_notifiers(self, handler, name):
484 if name in self._trait_notifiers:
484 if name in self._trait_notifiers:
485 nlist = self._trait_notifiers[name]
485 nlist = self._trait_notifiers[name]
486 try:
486 try:
487 index = nlist.index(handler)
487 index = nlist.index(handler)
488 except ValueError:
488 except ValueError:
489 pass
489 pass
490 else:
490 else:
491 del nlist[index]
491 del nlist[index]
492
492
493 def on_trait_change(self, handler, name=None, remove=False):
493 def on_trait_change(self, handler, name=None, remove=False):
494 """Setup a handler to be called when a trait changes.
494 """Setup a handler to be called when a trait changes.
495
495
496 This is used to setup dynamic notifications of trait changes.
496 This is used to setup dynamic notifications of trait changes.
497
497
498 Static handlers can be created by creating methods on a HasTraits
498 Static handlers can be created by creating methods on a HasTraits
499 subclass with the naming convention '_[traitname]_changed'. Thus,
499 subclass with the naming convention '_[traitname]_changed'. Thus,
500 to create static handler for the trait 'a', create the method
500 to create static handler for the trait 'a', create the method
501 _a_changed(self, name, old, new) (fewer arguments can be used, see
501 _a_changed(self, name, old, new) (fewer arguments can be used, see
502 below).
502 below).
503
503
504 Parameters
504 Parameters
505 ----------
505 ----------
506 handler : callable
506 handler : callable
507 A callable that is called when a trait changes. Its
507 A callable that is called when a trait changes. Its
508 signature can be handler(), handler(name), handler(name, new)
508 signature can be handler(), handler(name), handler(name, new)
509 or handler(name, old, new).
509 or handler(name, old, new).
510 name : list, str, None
510 name : list, str, None
511 If None, the handler will apply to all traits. If a list
511 If None, the handler will apply to all traits. If a list
512 of str, handler will apply to all names in the list. If a
512 of str, handler will apply to all names in the list. If a
513 str, the handler will apply just to that name.
513 str, the handler will apply just to that name.
514 remove : bool
514 remove : bool
515 If False (the default), then install the handler. If True
515 If False (the default), then install the handler. If True
516 then unintall it.
516 then unintall it.
517 """
517 """
518 if remove:
518 if remove:
519 names = parse_notifier_name(name)
519 names = parse_notifier_name(name)
520 for n in names:
520 for n in names:
521 self._remove_notifiers(handler, n)
521 self._remove_notifiers(handler, n)
522 else:
522 else:
523 names = parse_notifier_name(name)
523 names = parse_notifier_name(name)
524 for n in names:
524 for n in names:
525 self._add_notifiers(handler, n)
525 self._add_notifiers(handler, n)
526
526
527 @classmethod
527 @classmethod
528 def class_trait_names(cls, **metadata):
528 def class_trait_names(cls, **metadata):
529 """Get a list of all the names of this classes traits.
529 """Get a list of all the names of this classes traits.
530
530
531 This method is just like the :meth:`trait_names` method, but is unbound.
531 This method is just like the :meth:`trait_names` method, but is unbound.
532 """
532 """
533 return cls.class_traits(**metadata).keys()
533 return cls.class_traits(**metadata).keys()
534
534
535 @classmethod
535 @classmethod
536 def class_traits(cls, **metadata):
536 def class_traits(cls, **metadata):
537 """Get a list of all the traits of this class.
537 """Get a list of all the traits of this class.
538
538
539 This method is just like the :meth:`traits` method, but is unbound.
539 This method is just like the :meth:`traits` method, but is unbound.
540
540
541 The TraitTypes returned don't know anything about the values
541 The TraitTypes returned don't know anything about the values
542 that the various HasTrait's instances are holding.
542 that the various HasTrait's instances are holding.
543
543
544 This follows the same algorithm as traits does and does not allow
544 This follows the same algorithm as traits does and does not allow
545 for any simple way of specifying merely that a metadata name
545 for any simple way of specifying merely that a metadata name
546 exists, but has any value. This is because get_metadata returns
546 exists, but has any value. This is because get_metadata returns
547 None if a metadata key doesn't exist.
547 None if a metadata key doesn't exist.
548 """
548 """
549 traits = dict([memb for memb in getmembers(cls) if \
549 traits = dict([memb for memb in getmembers(cls) if \
550 isinstance(memb[1], TraitType)])
550 isinstance(memb[1], TraitType)])
551
551
552 if len(metadata) == 0:
552 if len(metadata) == 0:
553 return traits
553 return traits
554
554
555 for meta_name, meta_eval in metadata.items():
555 for meta_name, meta_eval in metadata.items():
556 if type(meta_eval) is not FunctionType:
556 if type(meta_eval) is not FunctionType:
557 metadata[meta_name] = _SimpleTest(meta_eval)
557 metadata[meta_name] = _SimpleTest(meta_eval)
558
558
559 result = {}
559 result = {}
560 for name, trait in traits.items():
560 for name, trait in traits.items():
561 for meta_name, meta_eval in metadata.items():
561 for meta_name, meta_eval in metadata.items():
562 if not meta_eval(trait.get_metadata(meta_name)):
562 if not meta_eval(trait.get_metadata(meta_name)):
563 break
563 break
564 else:
564 else:
565 result[name] = trait
565 result[name] = trait
566
566
567 return result
567 return result
568
568
569 def trait_names(self, **metadata):
569 def trait_names(self, **metadata):
570 """Get a list of all the names of this classes traits."""
570 """Get a list of all the names of this classes traits."""
571 return self.traits(**metadata).keys()
571 return self.traits(**metadata).keys()
572
572
573 def traits(self, **metadata):
573 def traits(self, **metadata):
574 """Get a list of all the traits of this class.
574 """Get a list of all the traits of this class.
575
575
576 The TraitTypes returned don't know anything about the values
576 The TraitTypes returned don't know anything about the values
577 that the various HasTrait's instances are holding.
577 that the various HasTrait's instances are holding.
578
578
579 This follows the same algorithm as traits does and does not allow
579 This follows the same algorithm as traits does and does not allow
580 for any simple way of specifying merely that a metadata name
580 for any simple way of specifying merely that a metadata name
581 exists, but has any value. This is because get_metadata returns
581 exists, but has any value. This is because get_metadata returns
582 None if a metadata key doesn't exist.
582 None if a metadata key doesn't exist.
583 """
583 """
584 traits = dict([memb for memb in getmembers(self.__class__) if \
584 traits = dict([memb for memb in getmembers(self.__class__) if \
585 isinstance(memb[1], TraitType)])
585 isinstance(memb[1], TraitType)])
586
586
587 if len(metadata) == 0:
587 if len(metadata) == 0:
588 return traits
588 return traits
589
589
590 for meta_name, meta_eval in metadata.items():
590 for meta_name, meta_eval in metadata.items():
591 if type(meta_eval) is not FunctionType:
591 if type(meta_eval) is not FunctionType:
592 metadata[meta_name] = _SimpleTest(meta_eval)
592 metadata[meta_name] = _SimpleTest(meta_eval)
593
593
594 result = {}
594 result = {}
595 for name, trait in traits.items():
595 for name, trait in traits.items():
596 for meta_name, meta_eval in metadata.items():
596 for meta_name, meta_eval in metadata.items():
597 if not meta_eval(trait.get_metadata(meta_name)):
597 if not meta_eval(trait.get_metadata(meta_name)):
598 break
598 break
599 else:
599 else:
600 result[name] = trait
600 result[name] = trait
601
601
602 return result
602 return result
603
603
604 def trait_metadata(self, traitname, key):
604 def trait_metadata(self, traitname, key):
605 """Get metadata values for trait by key."""
605 """Get metadata values for trait by key."""
606 try:
606 try:
607 trait = getattr(self.__class__, traitname)
607 trait = getattr(self.__class__, traitname)
608 except AttributeError:
608 except AttributeError:
609 raise TraitError("Class %s does not have a trait named %s" %
609 raise TraitError("Class %s does not have a trait named %s" %
610 (self.__class__.__name__, traitname))
610 (self.__class__.__name__, traitname))
611 else:
611 else:
612 return trait.get_metadata(key)
612 return trait.get_metadata(key)
613
613
614 #-----------------------------------------------------------------------------
614 #-----------------------------------------------------------------------------
615 # Actual TraitTypes implementations/subclasses
615 # Actual TraitTypes implementations/subclasses
616 #-----------------------------------------------------------------------------
616 #-----------------------------------------------------------------------------
617
617
618 #-----------------------------------------------------------------------------
618 #-----------------------------------------------------------------------------
619 # TraitTypes subclasses for handling classes and instances of classes
619 # TraitTypes subclasses for handling classes and instances of classes
620 #-----------------------------------------------------------------------------
620 #-----------------------------------------------------------------------------
621
621
622
622
623 class ClassBasedTraitType(TraitType):
623 class ClassBasedTraitType(TraitType):
624 """A trait with error reporting for Type, Instance and This."""
624 """A trait with error reporting for Type, Instance and This."""
625
625
626 def error(self, obj, value):
626 def error(self, obj, value):
627 kind = type(value)
627 kind = type(value)
628 if (not py3compat.PY3) and kind is InstanceType:
628 if (not py3compat.PY3) and kind is InstanceType:
629 msg = 'class %s' % value.__class__.__name__
629 msg = 'class %s' % value.__class__.__name__
630 else:
630 else:
631 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
631 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
632
632
633 if obj is not None:
633 if obj is not None:
634 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
634 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
635 % (self.name, class_of(obj),
635 % (self.name, class_of(obj),
636 self.info(), msg)
636 self.info(), msg)
637 else:
637 else:
638 e = "The '%s' trait must be %s, but a value of %r was specified." \
638 e = "The '%s' trait must be %s, but a value of %r was specified." \
639 % (self.name, self.info(), msg)
639 % (self.name, self.info(), msg)
640
640
641 raise TraitError(e)
641 raise TraitError(e)
642
642
643
643
644 class Type(ClassBasedTraitType):
644 class Type(ClassBasedTraitType):
645 """A trait whose value must be a subclass of a specified class."""
645 """A trait whose value must be a subclass of a specified class."""
646
646
647 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
647 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
648 """Construct a Type trait
648 """Construct a Type trait
649
649
650 A Type trait specifies that its values must be subclasses of
650 A Type trait specifies that its values must be subclasses of
651 a particular class.
651 a particular class.
652
652
653 If only ``default_value`` is given, it is used for the ``klass`` as
653 If only ``default_value`` is given, it is used for the ``klass`` as
654 well.
654 well.
655
655
656 Parameters
656 Parameters
657 ----------
657 ----------
658 default_value : class, str or None
658 default_value : class, str or None
659 The default value must be a subclass of klass. If an str,
659 The default value must be a subclass of klass. If an str,
660 the str must be a fully specified class name, like 'foo.bar.Bah'.
660 the str must be a fully specified class name, like 'foo.bar.Bah'.
661 The string is resolved into real class, when the parent
661 The string is resolved into real class, when the parent
662 :class:`HasTraits` class is instantiated.
662 :class:`HasTraits` class is instantiated.
663 klass : class, str, None
663 klass : class, str, None
664 Values of this trait must be a subclass of klass. The klass
664 Values of this trait must be a subclass of klass. The klass
665 may be specified in a string like: 'foo.bar.MyClass'.
665 may be specified in a string like: 'foo.bar.MyClass'.
666 The string is resolved into real class, when the parent
666 The string is resolved into real class, when the parent
667 :class:`HasTraits` class is instantiated.
667 :class:`HasTraits` class is instantiated.
668 allow_none : boolean
668 allow_none : boolean
669 Indicates whether None is allowed as an assignable value. Even if
669 Indicates whether None is allowed as an assignable value. Even if
670 ``False``, the default value may be ``None``.
670 ``False``, the default value may be ``None``.
671 """
671 """
672 if default_value is None:
672 if default_value is None:
673 if klass is None:
673 if klass is None:
674 klass = object
674 klass = object
675 elif klass is None:
675 elif klass is None:
676 klass = default_value
676 klass = default_value
677
677
678 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
678 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
679 raise TraitError("A Type trait must specify a class.")
679 raise TraitError("A Type trait must specify a class.")
680
680
681 self.klass = klass
681 self.klass = klass
682 self._allow_none = allow_none
682 self._allow_none = allow_none
683
683
684 super(Type, self).__init__(default_value, **metadata)
684 super(Type, self).__init__(default_value, **metadata)
685
685
686 def validate(self, obj, value):
686 def validate(self, obj, value):
687 """Validates that the value is a valid object instance."""
687 """Validates that the value is a valid object instance."""
688 try:
688 try:
689 if issubclass(value, self.klass):
689 if issubclass(value, self.klass):
690 return value
690 return value
691 except:
691 except:
692 if (value is None) and (self._allow_none):
692 if (value is None) and (self._allow_none):
693 return value
693 return value
694
694
695 self.error(obj, value)
695 self.error(obj, value)
696
696
697 def info(self):
697 def info(self):
698 """ Returns a description of the trait."""
698 """ Returns a description of the trait."""
699 if isinstance(self.klass, basestring):
699 if isinstance(self.klass, basestring):
700 klass = self.klass
700 klass = self.klass
701 else:
701 else:
702 klass = self.klass.__name__
702 klass = self.klass.__name__
703 result = 'a subclass of ' + klass
703 result = 'a subclass of ' + klass
704 if self._allow_none:
704 if self._allow_none:
705 return result + ' or None'
705 return result + ' or None'
706 return result
706 return result
707
707
708 def instance_init(self, obj):
708 def instance_init(self, obj):
709 self._resolve_classes()
709 self._resolve_classes()
710 super(Type, self).instance_init(obj)
710 super(Type, self).instance_init(obj)
711
711
712 def _resolve_classes(self):
712 def _resolve_classes(self):
713 if isinstance(self.klass, basestring):
713 if isinstance(self.klass, basestring):
714 self.klass = import_item(self.klass)
714 self.klass = import_item(self.klass)
715 if isinstance(self.default_value, basestring):
715 if isinstance(self.default_value, basestring):
716 self.default_value = import_item(self.default_value)
716 self.default_value = import_item(self.default_value)
717
717
718 def get_default_value(self):
718 def get_default_value(self):
719 return self.default_value
719 return self.default_value
720
720
721
721
722 class DefaultValueGenerator(object):
722 class DefaultValueGenerator(object):
723 """A class for generating new default value instances."""
723 """A class for generating new default value instances."""
724
724
725 def __init__(self, *args, **kw):
725 def __init__(self, *args, **kw):
726 self.args = args
726 self.args = args
727 self.kw = kw
727 self.kw = kw
728
728
729 def generate(self, klass):
729 def generate(self, klass):
730 return klass(*self.args, **self.kw)
730 return klass(*self.args, **self.kw)
731
731
732
732
733 class Instance(ClassBasedTraitType):
733 class Instance(ClassBasedTraitType):
734 """A trait whose value must be an instance of a specified class.
734 """A trait whose value must be an instance of a specified class.
735
735
736 The value can also be an instance of a subclass of the specified class.
736 The value can also be an instance of a subclass of the specified class.
737 """
737 """
738
738
739 def __init__(self, klass=None, args=None, kw=None,
739 def __init__(self, klass=None, args=None, kw=None,
740 allow_none=True, **metadata ):
740 allow_none=True, **metadata ):
741 """Construct an Instance trait.
741 """Construct an Instance trait.
742
742
743 This trait allows values that are instances of a particular
743 This trait allows values that are instances of a particular
744 class or its sublclasses. Our implementation is quite different
744 class or its sublclasses. Our implementation is quite different
745 from that of enthough.traits as we don't allow instances to be used
745 from that of enthough.traits as we don't allow instances to be used
746 for klass and we handle the ``args`` and ``kw`` arguments differently.
746 for klass and we handle the ``args`` and ``kw`` arguments differently.
747
747
748 Parameters
748 Parameters
749 ----------
749 ----------
750 klass : class, str
750 klass : class, str
751 The class that forms the basis for the trait. Class names
751 The class that forms the basis for the trait. Class names
752 can also be specified as strings, like 'foo.bar.Bar'.
752 can also be specified as strings, like 'foo.bar.Bar'.
753 args : tuple
753 args : tuple
754 Positional arguments for generating the default value.
754 Positional arguments for generating the default value.
755 kw : dict
755 kw : dict
756 Keyword arguments for generating the default value.
756 Keyword arguments for generating the default value.
757 allow_none : bool
757 allow_none : bool
758 Indicates whether None is allowed as a value.
758 Indicates whether None is allowed as a value.
759
759
760 Default Value
760 Default Value
761 -------------
761 -------------
762 If both ``args`` and ``kw`` are None, then the default value is None.
762 If both ``args`` and ``kw`` are None, then the default value is None.
763 If ``args`` is a tuple and ``kw`` is a dict, then the default is
763 If ``args`` is a tuple and ``kw`` is a dict, then the default is
764 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
764 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
765 not (but not both), None is replace by ``()`` or ``{}``.
765 not (but not both), None is replace by ``()`` or ``{}``.
766 """
766 """
767
767
768 self._allow_none = allow_none
768 self._allow_none = allow_none
769
769
770 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
770 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
771 raise TraitError('The klass argument must be a class'
771 raise TraitError('The klass argument must be a class'
772 ' you gave: %r' % klass)
772 ' you gave: %r' % klass)
773 self.klass = klass
773 self.klass = klass
774
774
775 # self.klass is a class, so handle default_value
775 # self.klass is a class, so handle default_value
776 if args is None and kw is None:
776 if args is None and kw is None:
777 default_value = None
777 default_value = None
778 else:
778 else:
779 if args is None:
779 if args is None:
780 # kw is not None
780 # kw is not None
781 args = ()
781 args = ()
782 elif kw is None:
782 elif kw is None:
783 # args is not None
783 # args is not None
784 kw = {}
784 kw = {}
785
785
786 if not isinstance(kw, dict):
786 if not isinstance(kw, dict):
787 raise TraitError("The 'kw' argument must be a dict or None.")
787 raise TraitError("The 'kw' argument must be a dict or None.")
788 if not isinstance(args, tuple):
788 if not isinstance(args, tuple):
789 raise TraitError("The 'args' argument must be a tuple or None.")
789 raise TraitError("The 'args' argument must be a tuple or None.")
790
790
791 default_value = DefaultValueGenerator(*args, **kw)
791 default_value = DefaultValueGenerator(*args, **kw)
792
792
793 super(Instance, self).__init__(default_value, **metadata)
793 super(Instance, self).__init__(default_value, **metadata)
794
794
795 def validate(self, obj, value):
795 def validate(self, obj, value):
796 if value is None:
796 if value is None:
797 if self._allow_none:
797 if self._allow_none:
798 return value
798 return value
799 self.error(obj, value)
799 self.error(obj, value)
800
800
801 if isinstance(value, self.klass):
801 if isinstance(value, self.klass):
802 return value
802 return value
803 else:
803 else:
804 self.error(obj, value)
804 self.error(obj, value)
805
805
806 def info(self):
806 def info(self):
807 if isinstance(self.klass, basestring):
807 if isinstance(self.klass, basestring):
808 klass = self.klass
808 klass = self.klass
809 else:
809 else:
810 klass = self.klass.__name__
810 klass = self.klass.__name__
811 result = class_of(klass)
811 result = class_of(klass)
812 if self._allow_none:
812 if self._allow_none:
813 return result + ' or None'
813 return result + ' or None'
814
814
815 return result
815 return result
816
816
817 def instance_init(self, obj):
817 def instance_init(self, obj):
818 self._resolve_classes()
818 self._resolve_classes()
819 super(Instance, self).instance_init(obj)
819 super(Instance, self).instance_init(obj)
820
820
821 def _resolve_classes(self):
821 def _resolve_classes(self):
822 if isinstance(self.klass, basestring):
822 if isinstance(self.klass, basestring):
823 self.klass = import_item(self.klass)
823 self.klass = import_item(self.klass)
824
824
825 def get_default_value(self):
825 def get_default_value(self):
826 """Instantiate a default value instance.
826 """Instantiate a default value instance.
827
827
828 This is called when the containing HasTraits classes'
828 This is called when the containing HasTraits classes'
829 :meth:`__new__` method is called to ensure that a unique instance
829 :meth:`__new__` method is called to ensure that a unique instance
830 is created for each HasTraits instance.
830 is created for each HasTraits instance.
831 """
831 """
832 dv = self.default_value
832 dv = self.default_value
833 if isinstance(dv, DefaultValueGenerator):
833 if isinstance(dv, DefaultValueGenerator):
834 return dv.generate(self.klass)
834 return dv.generate(self.klass)
835 else:
835 else:
836 return dv
836 return dv
837
837
838
838
839 class This(ClassBasedTraitType):
839 class This(ClassBasedTraitType):
840 """A trait for instances of the class containing this trait.
840 """A trait for instances of the class containing this trait.
841
841
842 Because how how and when class bodies are executed, the ``This``
842 Because how how and when class bodies are executed, the ``This``
843 trait can only have a default value of None. This, and because we
843 trait can only have a default value of None. This, and because we
844 always validate default values, ``allow_none`` is *always* true.
844 always validate default values, ``allow_none`` is *always* true.
845 """
845 """
846
846
847 info_text = 'an instance of the same type as the receiver or None'
847 info_text = 'an instance of the same type as the receiver or None'
848
848
849 def __init__(self, **metadata):
849 def __init__(self, **metadata):
850 super(This, self).__init__(None, **metadata)
850 super(This, self).__init__(None, **metadata)
851
851
852 def validate(self, obj, value):
852 def validate(self, obj, value):
853 # What if value is a superclass of obj.__class__? This is
853 # What if value is a superclass of obj.__class__? This is
854 # complicated if it was the superclass that defined the This
854 # complicated if it was the superclass that defined the This
855 # trait.
855 # trait.
856 if isinstance(value, self.this_class) or (value is None):
856 if isinstance(value, self.this_class) or (value is None):
857 return value
857 return value
858 else:
858 else:
859 self.error(obj, value)
859 self.error(obj, value)
860
860
861
861
862 #-----------------------------------------------------------------------------
862 #-----------------------------------------------------------------------------
863 # Basic TraitTypes implementations/subclasses
863 # Basic TraitTypes implementations/subclasses
864 #-----------------------------------------------------------------------------
864 #-----------------------------------------------------------------------------
865
865
866
866
867 class Any(TraitType):
867 class Any(TraitType):
868 default_value = None
868 default_value = None
869 info_text = 'any value'
869 info_text = 'any value'
870
870
871
871
872 class Int(TraitType):
872 class Int(TraitType):
873 """An int trait."""
873 """An int trait."""
874
874
875 default_value = 0
875 default_value = 0
876 info_text = 'an int'
876 info_text = 'an int'
877
877
878 def validate(self, obj, value):
878 def validate(self, obj, value):
879 if isinstance(value, int):
879 if isinstance(value, int):
880 return value
880 return value
881 self.error(obj, value)
881 self.error(obj, value)
882
882
883 class CInt(Int):
883 class CInt(Int):
884 """A casting version of the int trait."""
884 """A casting version of the int trait."""
885
885
886 def validate(self, obj, value):
886 def validate(self, obj, value):
887 try:
887 try:
888 return int(value)
888 return int(value)
889 except:
889 except:
890 self.error(obj, value)
890 self.error(obj, value)
891
891
892 if py3compat.PY3:
892 if py3compat.PY3:
893 Long, CLong = Int, CInt
893 Long, CLong = Int, CInt
894 Integer = Int
894 Integer = Int
895 else:
895 else:
896 class Long(TraitType):
896 class Long(TraitType):
897 """A long integer trait."""
897 """A long integer trait."""
898
898
899 default_value = 0L
899 default_value = 0L
900 info_text = 'a long'
900 info_text = 'a long'
901
901
902 def validate(self, obj, value):
902 def validate(self, obj, value):
903 if isinstance(value, long):
903 if isinstance(value, long):
904 return value
904 return value
905 if isinstance(value, int):
905 if isinstance(value, int):
906 return long(value)
906 return long(value)
907 self.error(obj, value)
907 self.error(obj, value)
908
908
909
909
910 class CLong(Long):
910 class CLong(Long):
911 """A casting version of the long integer trait."""
911 """A casting version of the long integer trait."""
912
912
913 def validate(self, obj, value):
913 def validate(self, obj, value):
914 try:
914 try:
915 return long(value)
915 return long(value)
916 except:
916 except:
917 self.error(obj, value)
917 self.error(obj, value)
918
918
919 class Integer(TraitType):
919 class Integer(TraitType):
920 """An integer trait.
920 """An integer trait.
921
921
922 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
922 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
923
923
924 default_value = 0
924 default_value = 0
925 info_text = 'an integer'
925 info_text = 'an integer'
926
926
927 def validate(self, obj, value):
927 def validate(self, obj, value):
928 if isinstance(value, int):
928 if isinstance(value, int):
929 return value
929 return value
930 elif isinstance(value, long):
930 elif isinstance(value, long):
931 # downcast longs that fit in int:
931 # downcast longs that fit in int:
932 # note that int(n > sys.maxint) returns a long, so
932 # note that int(n > sys.maxint) returns a long, so
933 # we don't need a condition on this cast
933 # we don't need a condition on this cast
934 return int(value)
934 return int(value)
935 self.error(obj, value)
935 self.error(obj, value)
936
936
937
937
938 class Float(TraitType):
938 class Float(TraitType):
939 """A float trait."""
939 """A float trait."""
940
940
941 default_value = 0.0
941 default_value = 0.0
942 info_text = 'a float'
942 info_text = 'a float'
943
943
944 def validate(self, obj, value):
944 def validate(self, obj, value):
945 if isinstance(value, float):
945 if isinstance(value, float):
946 return value
946 return value
947 if isinstance(value, int):
947 if isinstance(value, int):
948 return float(value)
948 return float(value)
949 self.error(obj, value)
949 self.error(obj, value)
950
950
951
951
952 class CFloat(Float):
952 class CFloat(Float):
953 """A casting version of the float trait."""
953 """A casting version of the float trait."""
954
954
955 def validate(self, obj, value):
955 def validate(self, obj, value):
956 try:
956 try:
957 return float(value)
957 return float(value)
958 except:
958 except:
959 self.error(obj, value)
959 self.error(obj, value)
960
960
961 class Complex(TraitType):
961 class Complex(TraitType):
962 """A trait for complex numbers."""
962 """A trait for complex numbers."""
963
963
964 default_value = 0.0 + 0.0j
964 default_value = 0.0 + 0.0j
965 info_text = 'a complex number'
965 info_text = 'a complex number'
966
966
967 def validate(self, obj, value):
967 def validate(self, obj, value):
968 if isinstance(value, complex):
968 if isinstance(value, complex):
969 return value
969 return value
970 if isinstance(value, (float, int)):
970 if isinstance(value, (float, int)):
971 return complex(value)
971 return complex(value)
972 self.error(obj, value)
972 self.error(obj, value)
973
973
974
974
975 class CComplex(Complex):
975 class CComplex(Complex):
976 """A casting version of the complex number trait."""
976 """A casting version of the complex number trait."""
977
977
978 def validate (self, obj, value):
978 def validate (self, obj, value):
979 try:
979 try:
980 return complex(value)
980 return complex(value)
981 except:
981 except:
982 self.error(obj, value)
982 self.error(obj, value)
983
983
984 # We should always be explicit about whether we're using bytes or unicode, both
984 # We should always be explicit about whether we're using bytes or unicode, both
985 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
985 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
986 # we don't have a Str type.
986 # we don't have a Str type.
987 class Bytes(TraitType):
987 class Bytes(TraitType):
988 """A trait for byte strings."""
988 """A trait for byte strings."""
989
989
990 default_value = b''
990 default_value = b''
991 info_text = 'a string'
991 info_text = 'a string'
992
992
993 def validate(self, obj, value):
993 def validate(self, obj, value):
994 if isinstance(value, bytes):
994 if isinstance(value, bytes):
995 return value
995 return value
996 self.error(obj, value)
996 self.error(obj, value)
997
997
998
998
999 class CBytes(Bytes):
999 class CBytes(Bytes):
1000 """A casting version of the byte string trait."""
1000 """A casting version of the byte string trait."""
1001
1001
1002 def validate(self, obj, value):
1002 def validate(self, obj, value):
1003 try:
1003 try:
1004 return bytes(value)
1004 return bytes(value)
1005 except:
1005 except:
1006 self.error(obj, value)
1006 self.error(obj, value)
1007
1007
1008
1008
1009 class Unicode(TraitType):
1009 class Unicode(TraitType):
1010 """A trait for unicode strings."""
1010 """A trait for unicode strings."""
1011
1011
1012 default_value = u''
1012 default_value = u''
1013 info_text = 'a unicode string'
1013 info_text = 'a unicode string'
1014
1014
1015 def validate(self, obj, value):
1015 def validate(self, obj, value):
1016 if isinstance(value, unicode):
1016 if isinstance(value, unicode):
1017 return value
1017 return value
1018 if isinstance(value, bytes):
1018 if isinstance(value, bytes):
1019 return unicode(value)
1019 return unicode(value)
1020 self.error(obj, value)
1020 self.error(obj, value)
1021
1021
1022
1022
1023 class CUnicode(Unicode):
1023 class CUnicode(Unicode):
1024 """A casting version of the unicode trait."""
1024 """A casting version of the unicode trait."""
1025
1025
1026 def validate(self, obj, value):
1026 def validate(self, obj, value):
1027 try:
1027 try:
1028 return unicode(value)
1028 return unicode(value)
1029 except:
1029 except:
1030 self.error(obj, value)
1030 self.error(obj, value)
1031
1031
1032
1032
1033 class ObjectName(TraitType):
1033 class ObjectName(TraitType):
1034 """A string holding a valid object name in this version of Python.
1034 """A string holding a valid object name in this version of Python.
1035
1035
1036 This does not check that the name exists in any scope."""
1036 This does not check that the name exists in any scope."""
1037 info_text = "a valid object identifier in Python"
1037 info_text = "a valid object identifier in Python"
1038
1038
1039 if py3compat.PY3:
1039 if py3compat.PY3:
1040 # Python 3:
1040 # Python 3:
1041 coerce_str = staticmethod(lambda _,s: s)
1041 coerce_str = staticmethod(lambda _,s: s)
1042
1042
1043 else:
1043 else:
1044 # Python 2:
1044 # Python 2:
1045 def coerce_str(self, obj, value):
1045 def coerce_str(self, obj, value):
1046 "In Python 2, coerce ascii-only unicode to str"
1046 "In Python 2, coerce ascii-only unicode to str"
1047 if isinstance(value, unicode):
1047 if isinstance(value, unicode):
1048 try:
1048 try:
1049 return str(value)
1049 return str(value)
1050 except UnicodeEncodeError:
1050 except UnicodeEncodeError:
1051 self.error(obj, value)
1051 self.error(obj, value)
1052 return value
1052 return value
1053
1053
1054 def validate(self, obj, value):
1054 def validate(self, obj, value):
1055 value = self.coerce_str(obj, value)
1055 value = self.coerce_str(obj, value)
1056
1056
1057 if isinstance(value, str) and py3compat.isidentifier(value):
1057 if isinstance(value, str) and py3compat.isidentifier(value):
1058 return value
1058 return value
1059 self.error(obj, value)
1059 self.error(obj, value)
1060
1060
1061 class DottedObjectName(ObjectName):
1061 class DottedObjectName(ObjectName):
1062 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1062 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1063 def validate(self, obj, value):
1063 def validate(self, obj, value):
1064 value = self.coerce_str(obj, value)
1064 value = self.coerce_str(obj, value)
1065
1065
1066 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1066 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1067 return value
1067 return value
1068 self.error(obj, value)
1068 self.error(obj, value)
1069
1069
1070
1070
1071 class Bool(TraitType):
1071 class Bool(TraitType):
1072 """A boolean (True, False) trait."""
1072 """A boolean (True, False) trait."""
1073
1073
1074 default_value = False
1074 default_value = False
1075 info_text = 'a boolean'
1075 info_text = 'a boolean'
1076
1076
1077 def validate(self, obj, value):
1077 def validate(self, obj, value):
1078 if isinstance(value, bool):
1078 if isinstance(value, bool):
1079 return value
1079 return value
1080 self.error(obj, value)
1080 self.error(obj, value)
1081
1081
1082
1082
1083 class CBool(Bool):
1083 class CBool(Bool):
1084 """A casting version of the boolean trait."""
1084 """A casting version of the boolean trait."""
1085
1085
1086 def validate(self, obj, value):
1086 def validate(self, obj, value):
1087 try:
1087 try:
1088 return bool(value)
1088 return bool(value)
1089 except:
1089 except:
1090 self.error(obj, value)
1090 self.error(obj, value)
1091
1091
1092
1092
1093 class Enum(TraitType):
1093 class Enum(TraitType):
1094 """An enum that whose value must be in a given sequence."""
1094 """An enum that whose value must be in a given sequence."""
1095
1095
1096 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1096 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1097 self.values = values
1097 self.values = values
1098 self._allow_none = allow_none
1098 self._allow_none = allow_none
1099 super(Enum, self).__init__(default_value, **metadata)
1099 super(Enum, self).__init__(default_value, **metadata)
1100
1100
1101 def validate(self, obj, value):
1101 def validate(self, obj, value):
1102 if value is None:
1102 if value is None:
1103 if self._allow_none:
1103 if self._allow_none:
1104 return value
1104 return value
1105
1105
1106 if value in self.values:
1106 if value in self.values:
1107 return value
1107 return value
1108 self.error(obj, value)
1108 self.error(obj, value)
1109
1109
1110 def info(self):
1110 def info(self):
1111 """ Returns a description of the trait."""
1111 """ Returns a description of the trait."""
1112 result = 'any of ' + repr(self.values)
1112 result = 'any of ' + repr(self.values)
1113 if self._allow_none:
1113 if self._allow_none:
1114 return result + ' or None'
1114 return result + ' or None'
1115 return result
1115 return result
1116
1116
1117 class CaselessStrEnum(Enum):
1117 class CaselessStrEnum(Enum):
1118 """An enum of strings that are caseless in validate."""
1118 """An enum of strings that are caseless in validate."""
1119
1119
1120 def validate(self, obj, value):
1120 def validate(self, obj, value):
1121 if value is None:
1121 if value is None:
1122 if self._allow_none:
1122 if self._allow_none:
1123 return value
1123 return value
1124
1124
1125 if not isinstance(value, basestring):
1125 if not isinstance(value, basestring):
1126 self.error(obj, value)
1126 self.error(obj, value)
1127
1127
1128 for v in self.values:
1128 for v in self.values:
1129 if v.lower() == value.lower():
1129 if v.lower() == value.lower():
1130 return v
1130 return v
1131 self.error(obj, value)
1131 self.error(obj, value)
1132
1132
1133 class Container(Instance):
1133 class Container(Instance):
1134 """An instance of a container (list, set, etc.)
1134 """An instance of a container (list, set, etc.)
1135
1135
1136 To be subclassed by overriding klass.
1136 To be subclassed by overriding klass.
1137 """
1137 """
1138 klass = None
1138 klass = None
1139 _valid_defaults = SequenceTypes
1139 _valid_defaults = SequenceTypes
1140 _trait = None
1140 _trait = None
1141
1141
1142 def __init__(self, trait=None, default_value=None, allow_none=True,
1142 def __init__(self, trait=None, default_value=None, allow_none=True,
1143 **metadata):
1143 **metadata):
1144 """Create a container trait type from a list, set, or tuple.
1144 """Create a container trait type from a list, set, or tuple.
1145
1145
1146 The default value is created by doing ``List(default_value)``,
1146 The default value is created by doing ``List(default_value)``,
1147 which creates a copy of the ``default_value``.
1147 which creates a copy of the ``default_value``.
1148
1148
1149 ``trait`` can be specified, which restricts the type of elements
1149 ``trait`` can be specified, which restricts the type of elements
1150 in the container to that TraitType.
1150 in the container to that TraitType.
1151
1151
1152 If only one arg is given and it is not a Trait, it is taken as
1152 If only one arg is given and it is not a Trait, it is taken as
1153 ``default_value``:
1153 ``default_value``:
1154
1154
1155 ``c = List([1,2,3])``
1155 ``c = List([1,2,3])``
1156
1156
1157 Parameters
1157 Parameters
1158 ----------
1158 ----------
1159
1159
1160 trait : TraitType [ optional ]
1160 trait : TraitType [ optional ]
1161 the type for restricting the contents of the Container. If unspecified,
1161 the type for restricting the contents of the Container. If unspecified,
1162 types are not checked.
1162 types are not checked.
1163
1163
1164 default_value : SequenceType [ optional ]
1164 default_value : SequenceType [ optional ]
1165 The default value for the Trait. Must be list/tuple/set, and
1165 The default value for the Trait. Must be list/tuple/set, and
1166 will be cast to the container type.
1166 will be cast to the container type.
1167
1167
1168 allow_none : Bool [ default True ]
1168 allow_none : Bool [ default True ]
1169 Whether to allow the value to be None
1169 Whether to allow the value to be None
1170
1170
1171 **metadata : any
1171 **metadata : any
1172 further keys for extensions to the Trait (e.g. config)
1172 further keys for extensions to the Trait (e.g. config)
1173
1173
1174 """
1174 """
1175 # allow List([values]):
1175 # allow List([values]):
1176 if default_value is None and not is_trait(trait):
1176 if default_value is None and not is_trait(trait):
1177 default_value = trait
1177 default_value = trait
1178 trait = None
1178 trait = None
1179
1179
1180 if default_value is None:
1180 if default_value is None:
1181 args = ()
1181 args = ()
1182 elif isinstance(default_value, self._valid_defaults):
1182 elif isinstance(default_value, self._valid_defaults):
1183 args = (default_value,)
1183 args = (default_value,)
1184 else:
1184 else:
1185 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1185 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1186
1186
1187 if is_trait(trait):
1187 if is_trait(trait):
1188 self._trait = trait() if isinstance(trait, type) else trait
1188 self._trait = trait() if isinstance(trait, type) else trait
1189 self._trait.name = 'element'
1189 self._trait.name = 'element'
1190 elif trait is not None:
1190 elif trait is not None:
1191 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1191 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1192
1192
1193 super(Container,self).__init__(klass=self.klass, args=args,
1193 super(Container,self).__init__(klass=self.klass, args=args,
1194 allow_none=allow_none, **metadata)
1194 allow_none=allow_none, **metadata)
1195
1195
1196 def element_error(self, obj, element, validator):
1196 def element_error(self, obj, element, validator):
1197 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1197 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1198 % (self.name, class_of(obj), validator.info(), repr_type(element))
1198 % (self.name, class_of(obj), validator.info(), repr_type(element))
1199 raise TraitError(e)
1199 raise TraitError(e)
1200
1200
1201 def validate(self, obj, value):
1201 def validate(self, obj, value):
1202 value = super(Container, self).validate(obj, value)
1202 value = super(Container, self).validate(obj, value)
1203 if value is None:
1203 if value is None:
1204 return value
1204 return value
1205
1205
1206 value = self.validate_elements(obj, value)
1206 value = self.validate_elements(obj, value)
1207
1207
1208 return value
1208 return value
1209
1209
1210 def validate_elements(self, obj, value):
1210 def validate_elements(self, obj, value):
1211 validated = []
1211 validated = []
1212 if self._trait is None or isinstance(self._trait, Any):
1212 if self._trait is None or isinstance(self._trait, Any):
1213 return value
1213 return value
1214 for v in value:
1214 for v in value:
1215 try:
1215 try:
1216 v = self._trait.validate(obj, v)
1216 v = self._trait.validate(obj, v)
1217 except TraitError:
1217 except TraitError:
1218 self.element_error(obj, v, self._trait)
1218 self.element_error(obj, v, self._trait)
1219 else:
1219 else:
1220 validated.append(v)
1220 validated.append(v)
1221 return self.klass(validated)
1221 return self.klass(validated)
1222
1222
1223
1223
1224 class List(Container):
1224 class List(Container):
1225 """An instance of a Python list."""
1225 """An instance of a Python list."""
1226 klass = list
1226 klass = list
1227
1227
1228 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint,
1228 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint,
1229 allow_none=True, **metadata):
1229 allow_none=True, **metadata):
1230 """Create a List trait type from a list, set, or tuple.
1230 """Create a List trait type from a list, set, or tuple.
1231
1231
1232 The default value is created by doing ``List(default_value)``,
1232 The default value is created by doing ``List(default_value)``,
1233 which creates a copy of the ``default_value``.
1233 which creates a copy of the ``default_value``.
1234
1234
1235 ``trait`` can be specified, which restricts the type of elements
1235 ``trait`` can be specified, which restricts the type of elements
1236 in the container to that TraitType.
1236 in the container to that TraitType.
1237
1237
1238 If only one arg is given and it is not a Trait, it is taken as
1238 If only one arg is given and it is not a Trait, it is taken as
1239 ``default_value``:
1239 ``default_value``:
1240
1240
1241 ``c = List([1,2,3])``
1241 ``c = List([1,2,3])``
1242
1242
1243 Parameters
1243 Parameters
1244 ----------
1244 ----------
1245
1245
1246 trait : TraitType [ optional ]
1246 trait : TraitType [ optional ]
1247 the type for restricting the contents of the Container. If unspecified,
1247 the type for restricting the contents of the Container. If unspecified,
1248 types are not checked.
1248 types are not checked.
1249
1249
1250 default_value : SequenceType [ optional ]
1250 default_value : SequenceType [ optional ]
1251 The default value for the Trait. Must be list/tuple/set, and
1251 The default value for the Trait. Must be list/tuple/set, and
1252 will be cast to the container type.
1252 will be cast to the container type.
1253
1253
1254 minlen : Int [ default 0 ]
1254 minlen : Int [ default 0 ]
1255 The minimum length of the input list
1255 The minimum length of the input list
1256
1256
1257 maxlen : Int [ default sys.maxint ]
1257 maxlen : Int [ default sys.maxint ]
1258 The maximum length of the input list
1258 The maximum length of the input list
1259
1259
1260 allow_none : Bool [ default True ]
1260 allow_none : Bool [ default True ]
1261 Whether to allow the value to be None
1261 Whether to allow the value to be None
1262
1262
1263 **metadata : any
1263 **metadata : any
1264 further keys for extensions to the Trait (e.g. config)
1264 further keys for extensions to the Trait (e.g. config)
1265
1265
1266 """
1266 """
1267 self._minlen = minlen
1267 self._minlen = minlen
1268 self._maxlen = maxlen
1268 self._maxlen = maxlen
1269 super(List, self).__init__(trait=trait, default_value=default_value,
1269 super(List, self).__init__(trait=trait, default_value=default_value,
1270 allow_none=allow_none, **metadata)
1270 allow_none=allow_none, **metadata)
1271
1271
1272 def length_error(self, obj, value):
1272 def length_error(self, obj, value):
1273 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1273 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1274 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1274 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1275 raise TraitError(e)
1275 raise TraitError(e)
1276
1276
1277 def validate_elements(self, obj, value):
1277 def validate_elements(self, obj, value):
1278 length = len(value)
1278 length = len(value)
1279 if length < self._minlen or length > self._maxlen:
1279 if length < self._minlen or length > self._maxlen:
1280 self.length_error(obj, value)
1280 self.length_error(obj, value)
1281
1281
1282 return super(List, self).validate_elements(obj, value)
1282 return super(List, self).validate_elements(obj, value)
1283
1283
1284
1284
1285 class Set(Container):
1285 class Set(Container):
1286 """An instance of a Python set."""
1286 """An instance of a Python set."""
1287 klass = set
1287 klass = set
1288
1288
1289 class Tuple(Container):
1289 class Tuple(Container):
1290 """An instance of a Python tuple."""
1290 """An instance of a Python tuple."""
1291 klass = tuple
1291 klass = tuple
1292
1292
1293 def __init__(self, *traits, **metadata):
1293 def __init__(self, *traits, **metadata):
1294 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1294 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1295
1295
1296 Create a tuple from a list, set, or tuple.
1296 Create a tuple from a list, set, or tuple.
1297
1297
1298 Create a fixed-type tuple with Traits:
1298 Create a fixed-type tuple with Traits:
1299
1299
1300 ``t = Tuple(Int, Str, CStr)``
1300 ``t = Tuple(Int, Str, CStr)``
1301
1301
1302 would be length 3, with Int,Str,CStr for each element.
1302 would be length 3, with Int,Str,CStr for each element.
1303
1303
1304 If only one arg is given and it is not a Trait, it is taken as
1304 If only one arg is given and it is not a Trait, it is taken as
1305 default_value:
1305 default_value:
1306
1306
1307 ``t = Tuple((1,2,3))``
1307 ``t = Tuple((1,2,3))``
1308
1308
1309 Otherwise, ``default_value`` *must* be specified by keyword.
1309 Otherwise, ``default_value`` *must* be specified by keyword.
1310
1310
1311 Parameters
1311 Parameters
1312 ----------
1312 ----------
1313
1313
1314 *traits : TraitTypes [ optional ]
1314 *traits : TraitTypes [ optional ]
1315 the tsype for restricting the contents of the Tuple. If unspecified,
1315 the tsype for restricting the contents of the Tuple. If unspecified,
1316 types are not checked. If specified, then each positional argument
1316 types are not checked. If specified, then each positional argument
1317 corresponds to an element of the tuple. Tuples defined with traits
1317 corresponds to an element of the tuple. Tuples defined with traits
1318 are of fixed length.
1318 are of fixed length.
1319
1319
1320 default_value : SequenceType [ optional ]
1320 default_value : SequenceType [ optional ]
1321 The default value for the Tuple. Must be list/tuple/set, and
1321 The default value for the Tuple. Must be list/tuple/set, and
1322 will be cast to a tuple. If `traits` are specified, the
1322 will be cast to a tuple. If `traits` are specified, the
1323 `default_value` must conform to the shape and type they specify.
1323 `default_value` must conform to the shape and type they specify.
1324
1324
1325 allow_none : Bool [ default True ]
1325 allow_none : Bool [ default True ]
1326 Whether to allow the value to be None
1326 Whether to allow the value to be None
1327
1327
1328 **metadata : any
1328 **metadata : any
1329 further keys for extensions to the Trait (e.g. config)
1329 further keys for extensions to the Trait (e.g. config)
1330
1330
1331 """
1331 """
1332 default_value = metadata.pop('default_value', None)
1332 default_value = metadata.pop('default_value', None)
1333 allow_none = metadata.pop('allow_none', True)
1333 allow_none = metadata.pop('allow_none', True)
1334
1334
1335 # allow Tuple((values,)):
1335 # allow Tuple((values,)):
1336 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1336 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1337 default_value = traits[0]
1337 default_value = traits[0]
1338 traits = ()
1338 traits = ()
1339
1339
1340 if default_value is None:
1340 if default_value is None:
1341 args = ()
1341 args = ()
1342 elif isinstance(default_value, self._valid_defaults):
1342 elif isinstance(default_value, self._valid_defaults):
1343 args = (default_value,)
1343 args = (default_value,)
1344 else:
1344 else:
1345 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1345 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1346
1346
1347 self._traits = []
1347 self._traits = []
1348 for trait in traits:
1348 for trait in traits:
1349 t = trait() if isinstance(trait, type) else trait
1349 t = trait() if isinstance(trait, type) else trait
1350 t.name = 'element'
1350 t.name = 'element'
1351 self._traits.append(t)
1351 self._traits.append(t)
1352
1352
1353 if self._traits and default_value is None:
1353 if self._traits and default_value is None:
1354 # don't allow default to be an empty container if length is specified
1354 # don't allow default to be an empty container if length is specified
1355 args = None
1355 args = None
1356 super(Container,self).__init__(klass=self.klass, args=args,
1356 super(Container,self).__init__(klass=self.klass, args=args,
1357 allow_none=allow_none, **metadata)
1357 allow_none=allow_none, **metadata)
1358
1358
1359 def validate_elements(self, obj, value):
1359 def validate_elements(self, obj, value):
1360 if not self._traits:
1360 if not self._traits:
1361 # nothing to validate
1361 # nothing to validate
1362 return value
1362 return value
1363 if len(value) != len(self._traits):
1363 if len(value) != len(self._traits):
1364 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1364 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1365 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1365 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1366 raise TraitError(e)
1366 raise TraitError(e)
1367
1367
1368 validated = []
1368 validated = []
1369 for t,v in zip(self._traits, value):
1369 for t,v in zip(self._traits, value):
1370 try:
1370 try:
1371 v = t.validate(obj, v)
1371 v = t.validate(obj, v)
1372 except TraitError:
1372 except TraitError:
1373 self.element_error(obj, v, t)
1373 self.element_error(obj, v, t)
1374 else:
1374 else:
1375 validated.append(v)
1375 validated.append(v)
1376 return tuple(validated)
1376 return tuple(validated)
1377
1377
1378
1378
1379 class Dict(Instance):
1379 class Dict(Instance):
1380 """An instance of a Python dict."""
1380 """An instance of a Python dict."""
1381
1381
1382 def __init__(self, default_value=None, allow_none=True, **metadata):
1382 def __init__(self, default_value=None, allow_none=True, **metadata):
1383 """Create a dict trait type from a dict.
1383 """Create a dict trait type from a dict.
1384
1384
1385 The default value is created by doing ``dict(default_value)``,
1385 The default value is created by doing ``dict(default_value)``,
1386 which creates a copy of the ``default_value``.
1386 which creates a copy of the ``default_value``.
1387 """
1387 """
1388 if default_value is None:
1388 if default_value is None:
1389 args = ((),)
1389 args = ((),)
1390 elif isinstance(default_value, dict):
1390 elif isinstance(default_value, dict):
1391 args = (default_value,)
1391 args = (default_value,)
1392 elif isinstance(default_value, SequenceTypes):
1392 elif isinstance(default_value, SequenceTypes):
1393 args = (default_value,)
1393 args = (default_value,)
1394 else:
1394 else:
1395 raise TypeError('default value of Dict was %s' % default_value)
1395 raise TypeError('default value of Dict was %s' % default_value)
1396
1396
1397 super(Dict,self).__init__(klass=dict, args=args,
1397 super(Dict,self).__init__(klass=dict, args=args,
1398 allow_none=allow_none, **metadata)
1398 allow_none=allow_none, **metadata)
1399
1399
1400 class TCPAddress(TraitType):
1400 class TCPAddress(TraitType):
1401 """A trait for an (ip, port) tuple.
1401 """A trait for an (ip, port) tuple.
1402
1402
1403 This allows for both IPv4 IP addresses as well as hostnames.
1403 This allows for both IPv4 IP addresses as well as hostnames.
1404 """
1404 """
1405
1405
1406 default_value = ('127.0.0.1', 0)
1406 default_value = ('127.0.0.1', 0)
1407 info_text = 'an (ip, port) tuple'
1407 info_text = 'an (ip, port) tuple'
1408
1408
1409 def validate(self, obj, value):
1409 def validate(self, obj, value):
1410 if isinstance(value, tuple):
1410 if isinstance(value, tuple):
1411 if len(value) == 2:
1411 if len(value) == 2:
1412 if isinstance(value[0], basestring) and isinstance(value[1], int):
1412 if isinstance(value[0], basestring) and isinstance(value[1], int):
1413 port = value[1]
1413 port = value[1]
1414 if port >= 0 and port <= 65535:
1414 if port >= 0 and port <= 65535:
1415 return value
1415 return value
1416 self.error(obj, value)
1416 self.error(obj, value)
1417
1417
1418 class CRegExp(TraitType):
1418 class CRegExp(TraitType):
1419 """A casting compiled regular expression trait.
1419 """A casting compiled regular expression trait.
1420
1420
1421 Accepts both strings and compiled regular expressions. The resulting
1421 Accepts both strings and compiled regular expressions. The resulting
1422 attribute will be a compiled regular expression."""
1422 attribute will be a compiled regular expression."""
1423
1423
1424 info_text = 'a regular expression'
1424 info_text = 'a regular expression'
1425
1425
1426 def validate(self, obj, value):
1426 def validate(self, obj, value):
1427 try:
1427 try:
1428 return re.compile(value)
1428 return re.compile(value)
1429 except:
1429 except:
1430 self.error(obj, value)
1430 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now