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