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