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