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