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