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