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