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