##// END OF EJS Templates
BUG: Do not store class-specific state on TraitTypes since they may be shared through subclassing.
Robert Kern -
Show More
@@ -1,729 +1,741 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Tests for IPython.utils.traitlets.
4 Tests for IPython.utils.traitlets.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
10 and is licensed under the BSD license. Also, many of the ideas also come
10 and is licensed under the BSD license. Also, many of the ideas also come
11 from enthought.traits even though our implementation is very different.
11 from enthought.traits even though our implementation is very different.
12 """
12 """
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2008-2009 The IPython Development Team
15 # Copyright (C) 2008-2009 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 from unittest import TestCase
25 from unittest import TestCase
26
26
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 HasTraits, MetaHasTraits, TraitType, Any,
28 HasTraits, MetaHasTraits, TraitType, Any,
29 Int, Long, Float, Complex, Str, Unicode, TraitError,
29 Int, Long, Float, Complex, Str, Unicode, TraitError,
30 Undefined, Type, This, Instance, TCPAddress
30 Undefined, Type, This, Instance, TCPAddress
31 )
31 )
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Helper classes for testing
35 # Helper classes for testing
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38
38
39 class HasTraitsStub(HasTraits):
39 class HasTraitsStub(HasTraits):
40
40
41 def _notify_trait(self, name, old, new):
41 def _notify_trait(self, name, old, new):
42 self._notify_name = name
42 self._notify_name = name
43 self._notify_old = old
43 self._notify_old = old
44 self._notify_new = new
44 self._notify_new = new
45
45
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Test classes
48 # Test classes
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 class TestTraitType(TestCase):
52 class TestTraitType(TestCase):
53
53
54 def test_get_undefined(self):
54 def test_get_undefined(self):
55 class A(HasTraits):
55 class A(HasTraits):
56 a = TraitType
56 a = TraitType
57 a = A()
57 a = A()
58 self.assertEquals(a.a, Undefined)
58 self.assertEquals(a.a, Undefined)
59
59
60 def test_set(self):
60 def test_set(self):
61 class A(HasTraitsStub):
61 class A(HasTraitsStub):
62 a = TraitType
62 a = TraitType
63
63
64 a = A()
64 a = A()
65 a.a = 10
65 a.a = 10
66 self.assertEquals(a.a, 10)
66 self.assertEquals(a.a, 10)
67 self.assertEquals(a._notify_name, 'a')
67 self.assertEquals(a._notify_name, 'a')
68 self.assertEquals(a._notify_old, Undefined)
68 self.assertEquals(a._notify_old, Undefined)
69 self.assertEquals(a._notify_new, 10)
69 self.assertEquals(a._notify_new, 10)
70
70
71 def test_validate(self):
71 def test_validate(self):
72 class MyTT(TraitType):
72 class MyTT(TraitType):
73 def validate(self, inst, value):
73 def validate(self, inst, value):
74 return -1
74 return -1
75 class A(HasTraitsStub):
75 class A(HasTraitsStub):
76 tt = MyTT
76 tt = MyTT
77
77
78 a = A()
78 a = A()
79 a.tt = 10
79 a.tt = 10
80 self.assertEquals(a.tt, -1)
80 self.assertEquals(a.tt, -1)
81
81
82 def test_default_validate(self):
82 def test_default_validate(self):
83 class MyIntTT(TraitType):
83 class MyIntTT(TraitType):
84 def validate(self, obj, value):
84 def validate(self, obj, value):
85 if isinstance(value, int):
85 if isinstance(value, int):
86 return value
86 return value
87 self.error(obj, value)
87 self.error(obj, value)
88 class A(HasTraits):
88 class A(HasTraits):
89 tt = MyIntTT(10)
89 tt = MyIntTT(10)
90 a = A()
90 a = A()
91 self.assertEquals(a.tt, 10)
91 self.assertEquals(a.tt, 10)
92
92
93 # Defaults are validated when the HasTraits is instantiated
93 # Defaults are validated when the HasTraits is instantiated
94 class B(HasTraits):
94 class B(HasTraits):
95 tt = MyIntTT('bad default')
95 tt = MyIntTT('bad default')
96 self.assertRaises(TraitError, B)
96 self.assertRaises(TraitError, B)
97
97
98 def test_is_valid_for(self):
98 def test_is_valid_for(self):
99 class MyTT(TraitType):
99 class MyTT(TraitType):
100 def is_valid_for(self, value):
100 def is_valid_for(self, value):
101 return True
101 return True
102 class A(HasTraits):
102 class A(HasTraits):
103 tt = MyTT
103 tt = MyTT
104
104
105 a = A()
105 a = A()
106 a.tt = 10
106 a.tt = 10
107 self.assertEquals(a.tt, 10)
107 self.assertEquals(a.tt, 10)
108
108
109 def test_value_for(self):
109 def test_value_for(self):
110 class MyTT(TraitType):
110 class MyTT(TraitType):
111 def value_for(self, value):
111 def value_for(self, value):
112 return 20
112 return 20
113 class A(HasTraits):
113 class A(HasTraits):
114 tt = MyTT
114 tt = MyTT
115
115
116 a = A()
116 a = A()
117 a.tt = 10
117 a.tt = 10
118 self.assertEquals(a.tt, 20)
118 self.assertEquals(a.tt, 20)
119
119
120 def test_info(self):
120 def test_info(self):
121 class A(HasTraits):
121 class A(HasTraits):
122 tt = TraitType
122 tt = TraitType
123 a = A()
123 a = A()
124 self.assertEquals(A.tt.info(), 'any value')
124 self.assertEquals(A.tt.info(), 'any value')
125
125
126 def test_error(self):
126 def test_error(self):
127 class A(HasTraits):
127 class A(HasTraits):
128 tt = TraitType
128 tt = TraitType
129 a = A()
129 a = A()
130 self.assertRaises(TraitError, A.tt.error, a, 10)
130 self.assertRaises(TraitError, A.tt.error, a, 10)
131
131
132 def test_dynamic_initializer(self):
132 def test_dynamic_initializer(self):
133 class A(HasTraits):
133 class A(HasTraits):
134 x = Int(10)
134 x = Int(10)
135 def _x_default(self):
135 def _x_default(self):
136 return 11
136 return 11
137 class B(A):
137 class B(A):
138 x = Int(20)
138 x = Int(20)
139 class C(A):
139 class C(A):
140 def _x_default(self):
140 def _x_default(self):
141 return 21
141 return 21
142
142
143 a = A()
143 a = A()
144 self.assertEquals(a._trait_values, {})
144 self.assertEquals(a._trait_values, {})
145 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
145 self.assertEquals(a.x, 11)
146 self.assertEquals(a.x, 11)
146 self.assertEquals(a._trait_values, {'x': 11})
147 self.assertEquals(a._trait_values, {'x': 11})
147 b = B()
148 b = B()
148 self.assertEquals(b._trait_values, {'x': 20})
149 self.assertEquals(b._trait_values, {'x': 20})
150 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
149 self.assertEquals(b.x, 20)
151 self.assertEquals(b.x, 20)
150 c = C()
152 c = C()
151 self.assertEquals(c._trait_values, {})
153 self.assertEquals(c._trait_values, {})
154 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
152 self.assertEquals(c.x, 21)
155 self.assertEquals(c.x, 21)
153 self.assertEquals(c._trait_values, {'x': 21})
156 self.assertEquals(c._trait_values, {'x': 21})
157 # Ensure that the base class remains unmolested when the _default
158 # initializer gets overridden in a subclass.
159 a = A()
160 c = C()
161 self.assertEquals(a._trait_values, {})
162 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
163 self.assertEquals(a.x, 11)
164 self.assertEquals(a._trait_values, {'x': 11})
165
154
166
155
167
156 class TestHasTraitsMeta(TestCase):
168 class TestHasTraitsMeta(TestCase):
157
169
158 def test_metaclass(self):
170 def test_metaclass(self):
159 self.assertEquals(type(HasTraits), MetaHasTraits)
171 self.assertEquals(type(HasTraits), MetaHasTraits)
160
172
161 class A(HasTraits):
173 class A(HasTraits):
162 a = Int
174 a = Int
163
175
164 a = A()
176 a = A()
165 self.assertEquals(type(a.__class__), MetaHasTraits)
177 self.assertEquals(type(a.__class__), MetaHasTraits)
166 self.assertEquals(a.a,0)
178 self.assertEquals(a.a,0)
167 a.a = 10
179 a.a = 10
168 self.assertEquals(a.a,10)
180 self.assertEquals(a.a,10)
169
181
170 class B(HasTraits):
182 class B(HasTraits):
171 b = Int()
183 b = Int()
172
184
173 b = B()
185 b = B()
174 self.assertEquals(b.b,0)
186 self.assertEquals(b.b,0)
175 b.b = 10
187 b.b = 10
176 self.assertEquals(b.b,10)
188 self.assertEquals(b.b,10)
177
189
178 class C(HasTraits):
190 class C(HasTraits):
179 c = Int(30)
191 c = Int(30)
180
192
181 c = C()
193 c = C()
182 self.assertEquals(c.c,30)
194 self.assertEquals(c.c,30)
183 c.c = 10
195 c.c = 10
184 self.assertEquals(c.c,10)
196 self.assertEquals(c.c,10)
185
197
186 def test_this_class(self):
198 def test_this_class(self):
187 class A(HasTraits):
199 class A(HasTraits):
188 t = This()
200 t = This()
189 tt = This()
201 tt = This()
190 class B(A):
202 class B(A):
191 tt = This()
203 tt = This()
192 ttt = This()
204 ttt = This()
193 self.assertEquals(A.t.this_class, A)
205 self.assertEquals(A.t.this_class, A)
194 self.assertEquals(B.t.this_class, A)
206 self.assertEquals(B.t.this_class, A)
195 self.assertEquals(B.tt.this_class, B)
207 self.assertEquals(B.tt.this_class, B)
196 self.assertEquals(B.ttt.this_class, B)
208 self.assertEquals(B.ttt.this_class, B)
197
209
198 class TestHasTraitsNotify(TestCase):
210 class TestHasTraitsNotify(TestCase):
199
211
200 def setUp(self):
212 def setUp(self):
201 self._notify1 = []
213 self._notify1 = []
202 self._notify2 = []
214 self._notify2 = []
203
215
204 def notify1(self, name, old, new):
216 def notify1(self, name, old, new):
205 self._notify1.append((name, old, new))
217 self._notify1.append((name, old, new))
206
218
207 def notify2(self, name, old, new):
219 def notify2(self, name, old, new):
208 self._notify2.append((name, old, new))
220 self._notify2.append((name, old, new))
209
221
210 def test_notify_all(self):
222 def test_notify_all(self):
211
223
212 class A(HasTraits):
224 class A(HasTraits):
213 a = Int
225 a = Int
214 b = Float
226 b = Float
215
227
216 a = A()
228 a = A()
217 a.on_trait_change(self.notify1)
229 a.on_trait_change(self.notify1)
218 a.a = 0
230 a.a = 0
219 self.assertEquals(len(self._notify1),0)
231 self.assertEquals(len(self._notify1),0)
220 a.b = 0.0
232 a.b = 0.0
221 self.assertEquals(len(self._notify1),0)
233 self.assertEquals(len(self._notify1),0)
222 a.a = 10
234 a.a = 10
223 self.assert_(('a',0,10) in self._notify1)
235 self.assert_(('a',0,10) in self._notify1)
224 a.b = 10.0
236 a.b = 10.0
225 self.assert_(('b',0.0,10.0) in self._notify1)
237 self.assert_(('b',0.0,10.0) in self._notify1)
226 self.assertRaises(TraitError,setattr,a,'a','bad string')
238 self.assertRaises(TraitError,setattr,a,'a','bad string')
227 self.assertRaises(TraitError,setattr,a,'b','bad string')
239 self.assertRaises(TraitError,setattr,a,'b','bad string')
228 self._notify1 = []
240 self._notify1 = []
229 a.on_trait_change(self.notify1,remove=True)
241 a.on_trait_change(self.notify1,remove=True)
230 a.a = 20
242 a.a = 20
231 a.b = 20.0
243 a.b = 20.0
232 self.assertEquals(len(self._notify1),0)
244 self.assertEquals(len(self._notify1),0)
233
245
234 def test_notify_one(self):
246 def test_notify_one(self):
235
247
236 class A(HasTraits):
248 class A(HasTraits):
237 a = Int
249 a = Int
238 b = Float
250 b = Float
239
251
240 a = A()
252 a = A()
241 a.on_trait_change(self.notify1, 'a')
253 a.on_trait_change(self.notify1, 'a')
242 a.a = 0
254 a.a = 0
243 self.assertEquals(len(self._notify1),0)
255 self.assertEquals(len(self._notify1),0)
244 a.a = 10
256 a.a = 10
245 self.assert_(('a',0,10) in self._notify1)
257 self.assert_(('a',0,10) in self._notify1)
246 self.assertRaises(TraitError,setattr,a,'a','bad string')
258 self.assertRaises(TraitError,setattr,a,'a','bad string')
247
259
248 def test_subclass(self):
260 def test_subclass(self):
249
261
250 class A(HasTraits):
262 class A(HasTraits):
251 a = Int
263 a = Int
252
264
253 class B(A):
265 class B(A):
254 b = Float
266 b = Float
255
267
256 b = B()
268 b = B()
257 self.assertEquals(b.a,0)
269 self.assertEquals(b.a,0)
258 self.assertEquals(b.b,0.0)
270 self.assertEquals(b.b,0.0)
259 b.a = 100
271 b.a = 100
260 b.b = 100.0
272 b.b = 100.0
261 self.assertEquals(b.a,100)
273 self.assertEquals(b.a,100)
262 self.assertEquals(b.b,100.0)
274 self.assertEquals(b.b,100.0)
263
275
264 def test_notify_subclass(self):
276 def test_notify_subclass(self):
265
277
266 class A(HasTraits):
278 class A(HasTraits):
267 a = Int
279 a = Int
268
280
269 class B(A):
281 class B(A):
270 b = Float
282 b = Float
271
283
272 b = B()
284 b = B()
273 b.on_trait_change(self.notify1, 'a')
285 b.on_trait_change(self.notify1, 'a')
274 b.on_trait_change(self.notify2, 'b')
286 b.on_trait_change(self.notify2, 'b')
275 b.a = 0
287 b.a = 0
276 b.b = 0.0
288 b.b = 0.0
277 self.assertEquals(len(self._notify1),0)
289 self.assertEquals(len(self._notify1),0)
278 self.assertEquals(len(self._notify2),0)
290 self.assertEquals(len(self._notify2),0)
279 b.a = 10
291 b.a = 10
280 b.b = 10.0
292 b.b = 10.0
281 self.assert_(('a',0,10) in self._notify1)
293 self.assert_(('a',0,10) in self._notify1)
282 self.assert_(('b',0.0,10.0) in self._notify2)
294 self.assert_(('b',0.0,10.0) in self._notify2)
283
295
284 def test_static_notify(self):
296 def test_static_notify(self):
285
297
286 class A(HasTraits):
298 class A(HasTraits):
287 a = Int
299 a = Int
288 _notify1 = []
300 _notify1 = []
289 def _a_changed(self, name, old, new):
301 def _a_changed(self, name, old, new):
290 self._notify1.append((name, old, new))
302 self._notify1.append((name, old, new))
291
303
292 a = A()
304 a = A()
293 a.a = 0
305 a.a = 0
294 # This is broken!!!
306 # This is broken!!!
295 self.assertEquals(len(a._notify1),0)
307 self.assertEquals(len(a._notify1),0)
296 a.a = 10
308 a.a = 10
297 self.assert_(('a',0,10) in a._notify1)
309 self.assert_(('a',0,10) in a._notify1)
298
310
299 class B(A):
311 class B(A):
300 b = Float
312 b = Float
301 _notify2 = []
313 _notify2 = []
302 def _b_changed(self, name, old, new):
314 def _b_changed(self, name, old, new):
303 self._notify2.append((name, old, new))
315 self._notify2.append((name, old, new))
304
316
305 b = B()
317 b = B()
306 b.a = 10
318 b.a = 10
307 b.b = 10.0
319 b.b = 10.0
308 self.assert_(('a',0,10) in b._notify1)
320 self.assert_(('a',0,10) in b._notify1)
309 self.assert_(('b',0.0,10.0) in b._notify2)
321 self.assert_(('b',0.0,10.0) in b._notify2)
310
322
311 def test_notify_args(self):
323 def test_notify_args(self):
312
324
313 def callback0():
325 def callback0():
314 self.cb = ()
326 self.cb = ()
315 def callback1(name):
327 def callback1(name):
316 self.cb = (name,)
328 self.cb = (name,)
317 def callback2(name, new):
329 def callback2(name, new):
318 self.cb = (name, new)
330 self.cb = (name, new)
319 def callback3(name, old, new):
331 def callback3(name, old, new):
320 self.cb = (name, old, new)
332 self.cb = (name, old, new)
321
333
322 class A(HasTraits):
334 class A(HasTraits):
323 a = Int
335 a = Int
324
336
325 a = A()
337 a = A()
326 a.on_trait_change(callback0, 'a')
338 a.on_trait_change(callback0, 'a')
327 a.a = 10
339 a.a = 10
328 self.assertEquals(self.cb,())
340 self.assertEquals(self.cb,())
329 a.on_trait_change(callback0, 'a', remove=True)
341 a.on_trait_change(callback0, 'a', remove=True)
330
342
331 a.on_trait_change(callback1, 'a')
343 a.on_trait_change(callback1, 'a')
332 a.a = 100
344 a.a = 100
333 self.assertEquals(self.cb,('a',))
345 self.assertEquals(self.cb,('a',))
334 a.on_trait_change(callback1, 'a', remove=True)
346 a.on_trait_change(callback1, 'a', remove=True)
335
347
336 a.on_trait_change(callback2, 'a')
348 a.on_trait_change(callback2, 'a')
337 a.a = 1000
349 a.a = 1000
338 self.assertEquals(self.cb,('a',1000))
350 self.assertEquals(self.cb,('a',1000))
339 a.on_trait_change(callback2, 'a', remove=True)
351 a.on_trait_change(callback2, 'a', remove=True)
340
352
341 a.on_trait_change(callback3, 'a')
353 a.on_trait_change(callback3, 'a')
342 a.a = 10000
354 a.a = 10000
343 self.assertEquals(self.cb,('a',1000,10000))
355 self.assertEquals(self.cb,('a',1000,10000))
344 a.on_trait_change(callback3, 'a', remove=True)
356 a.on_trait_change(callback3, 'a', remove=True)
345
357
346 self.assertEquals(len(a._trait_notifiers['a']),0)
358 self.assertEquals(len(a._trait_notifiers['a']),0)
347
359
348
360
349 class TestHasTraits(TestCase):
361 class TestHasTraits(TestCase):
350
362
351 def test_trait_names(self):
363 def test_trait_names(self):
352 class A(HasTraits):
364 class A(HasTraits):
353 i = Int
365 i = Int
354 f = Float
366 f = Float
355 a = A()
367 a = A()
356 self.assertEquals(a.trait_names(),['i','f'])
368 self.assertEquals(a.trait_names(),['i','f'])
357
369
358 def test_trait_metadata(self):
370 def test_trait_metadata(self):
359 class A(HasTraits):
371 class A(HasTraits):
360 i = Int(config_key='MY_VALUE')
372 i = Int(config_key='MY_VALUE')
361 a = A()
373 a = A()
362 self.assertEquals(a.trait_metadata('i','config_key'), 'MY_VALUE')
374 self.assertEquals(a.trait_metadata('i','config_key'), 'MY_VALUE')
363
375
364 def test_traits(self):
376 def test_traits(self):
365 class A(HasTraits):
377 class A(HasTraits):
366 i = Int
378 i = Int
367 f = Float
379 f = Float
368 a = A()
380 a = A()
369 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
381 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
370
382
371 def test_traits_metadata(self):
383 def test_traits_metadata(self):
372 class A(HasTraits):
384 class A(HasTraits):
373 i = Int(config_key='VALUE1', other_thing='VALUE2')
385 i = Int(config_key='VALUE1', other_thing='VALUE2')
374 f = Float(config_key='VALUE3', other_thing='VALUE2')
386 f = Float(config_key='VALUE3', other_thing='VALUE2')
375 j = Int(0)
387 j = Int(0)
376 a = A()
388 a = A()
377 self.assertEquals(a.traits(), dict(i=A.i, f=A.f, j=A.j))
389 self.assertEquals(a.traits(), dict(i=A.i, f=A.f, j=A.j))
378 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
390 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
379 self.assertEquals(traits, dict(i=A.i))
391 self.assertEquals(traits, dict(i=A.i))
380
392
381 # This passes, but it shouldn't because I am replicating a bug in
393 # This passes, but it shouldn't because I am replicating a bug in
382 # traits.
394 # traits.
383 traits = a.traits(config_key=lambda v: True)
395 traits = a.traits(config_key=lambda v: True)
384 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
396 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
385
397
386 def test_init(self):
398 def test_init(self):
387 class A(HasTraits):
399 class A(HasTraits):
388 i = Int()
400 i = Int()
389 x = Float()
401 x = Float()
390 a = A(i=1, x=10.0)
402 a = A(i=1, x=10.0)
391 self.assertEquals(a.i, 1)
403 self.assertEquals(a.i, 1)
392 self.assertEquals(a.x, 10.0)
404 self.assertEquals(a.x, 10.0)
393
405
394 #-----------------------------------------------------------------------------
406 #-----------------------------------------------------------------------------
395 # Tests for specific trait types
407 # Tests for specific trait types
396 #-----------------------------------------------------------------------------
408 #-----------------------------------------------------------------------------
397
409
398
410
399 class TestType(TestCase):
411 class TestType(TestCase):
400
412
401 def test_default(self):
413 def test_default(self):
402
414
403 class B(object): pass
415 class B(object): pass
404 class A(HasTraits):
416 class A(HasTraits):
405 klass = Type
417 klass = Type
406
418
407 a = A()
419 a = A()
408 self.assertEquals(a.klass, None)
420 self.assertEquals(a.klass, None)
409
421
410 a.klass = B
422 a.klass = B
411 self.assertEquals(a.klass, B)
423 self.assertEquals(a.klass, B)
412 self.assertRaises(TraitError, setattr, a, 'klass', 10)
424 self.assertRaises(TraitError, setattr, a, 'klass', 10)
413
425
414 def test_value(self):
426 def test_value(self):
415
427
416 class B(object): pass
428 class B(object): pass
417 class C(object): pass
429 class C(object): pass
418 class A(HasTraits):
430 class A(HasTraits):
419 klass = Type(B)
431 klass = Type(B)
420
432
421 a = A()
433 a = A()
422 self.assertEquals(a.klass, B)
434 self.assertEquals(a.klass, B)
423 self.assertRaises(TraitError, setattr, a, 'klass', C)
435 self.assertRaises(TraitError, setattr, a, 'klass', C)
424 self.assertRaises(TraitError, setattr, a, 'klass', object)
436 self.assertRaises(TraitError, setattr, a, 'klass', object)
425 a.klass = B
437 a.klass = B
426
438
427 def test_allow_none(self):
439 def test_allow_none(self):
428
440
429 class B(object): pass
441 class B(object): pass
430 class C(B): pass
442 class C(B): pass
431 class A(HasTraits):
443 class A(HasTraits):
432 klass = Type(B, allow_none=False)
444 klass = Type(B, allow_none=False)
433
445
434 a = A()
446 a = A()
435 self.assertEquals(a.klass, B)
447 self.assertEquals(a.klass, B)
436 self.assertRaises(TraitError, setattr, a, 'klass', None)
448 self.assertRaises(TraitError, setattr, a, 'klass', None)
437 a.klass = C
449 a.klass = C
438 self.assertEquals(a.klass, C)
450 self.assertEquals(a.klass, C)
439
451
440 def test_validate_klass(self):
452 def test_validate_klass(self):
441
453
442 class A(HasTraits):
454 class A(HasTraits):
443 klass = Type('no strings allowed')
455 klass = Type('no strings allowed')
444
456
445 self.assertRaises(ImportError, A)
457 self.assertRaises(ImportError, A)
446
458
447 class A(HasTraits):
459 class A(HasTraits):
448 klass = Type('rub.adub.Duck')
460 klass = Type('rub.adub.Duck')
449
461
450 self.assertRaises(ImportError, A)
462 self.assertRaises(ImportError, A)
451
463
452 def test_validate_default(self):
464 def test_validate_default(self):
453
465
454 class B(object): pass
466 class B(object): pass
455 class A(HasTraits):
467 class A(HasTraits):
456 klass = Type('bad default', B)
468 klass = Type('bad default', B)
457
469
458 self.assertRaises(ImportError, A)
470 self.assertRaises(ImportError, A)
459
471
460 class C(HasTraits):
472 class C(HasTraits):
461 klass = Type(None, B, allow_none=False)
473 klass = Type(None, B, allow_none=False)
462
474
463 self.assertRaises(TraitError, C)
475 self.assertRaises(TraitError, C)
464
476
465 def test_str_klass(self):
477 def test_str_klass(self):
466
478
467 class A(HasTraits):
479 class A(HasTraits):
468 klass = Type('IPython.utils.ipstruct.Struct')
480 klass = Type('IPython.utils.ipstruct.Struct')
469
481
470 from IPython.utils.ipstruct import Struct
482 from IPython.utils.ipstruct import Struct
471 a = A()
483 a = A()
472 a.klass = Struct
484 a.klass = Struct
473 self.assertEquals(a.klass, Struct)
485 self.assertEquals(a.klass, Struct)
474
486
475 self.assertRaises(TraitError, setattr, a, 'klass', 10)
487 self.assertRaises(TraitError, setattr, a, 'klass', 10)
476
488
477 class TestInstance(TestCase):
489 class TestInstance(TestCase):
478
490
479 def test_basic(self):
491 def test_basic(self):
480 class Foo(object): pass
492 class Foo(object): pass
481 class Bar(Foo): pass
493 class Bar(Foo): pass
482 class Bah(object): pass
494 class Bah(object): pass
483
495
484 class A(HasTraits):
496 class A(HasTraits):
485 inst = Instance(Foo)
497 inst = Instance(Foo)
486
498
487 a = A()
499 a = A()
488 self.assert_(a.inst is None)
500 self.assert_(a.inst is None)
489 a.inst = Foo()
501 a.inst = Foo()
490 self.assert_(isinstance(a.inst, Foo))
502 self.assert_(isinstance(a.inst, Foo))
491 a.inst = Bar()
503 a.inst = Bar()
492 self.assert_(isinstance(a.inst, Foo))
504 self.assert_(isinstance(a.inst, Foo))
493 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
505 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
494 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
506 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
495 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
507 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
496
508
497 def test_unique_default_value(self):
509 def test_unique_default_value(self):
498 class Foo(object): pass
510 class Foo(object): pass
499 class A(HasTraits):
511 class A(HasTraits):
500 inst = Instance(Foo,(),{})
512 inst = Instance(Foo,(),{})
501
513
502 a = A()
514 a = A()
503 b = A()
515 b = A()
504 self.assert_(a.inst is not b.inst)
516 self.assert_(a.inst is not b.inst)
505
517
506 def test_args_kw(self):
518 def test_args_kw(self):
507 class Foo(object):
519 class Foo(object):
508 def __init__(self, c): self.c = c
520 def __init__(self, c): self.c = c
509 class Bar(object): pass
521 class Bar(object): pass
510 class Bah(object):
522 class Bah(object):
511 def __init__(self, c, d):
523 def __init__(self, c, d):
512 self.c = c; self.d = d
524 self.c = c; self.d = d
513
525
514 class A(HasTraits):
526 class A(HasTraits):
515 inst = Instance(Foo, (10,))
527 inst = Instance(Foo, (10,))
516 a = A()
528 a = A()
517 self.assertEquals(a.inst.c, 10)
529 self.assertEquals(a.inst.c, 10)
518
530
519 class B(HasTraits):
531 class B(HasTraits):
520 inst = Instance(Bah, args=(10,), kw=dict(d=20))
532 inst = Instance(Bah, args=(10,), kw=dict(d=20))
521 b = B()
533 b = B()
522 self.assertEquals(b.inst.c, 10)
534 self.assertEquals(b.inst.c, 10)
523 self.assertEquals(b.inst.d, 20)
535 self.assertEquals(b.inst.d, 20)
524
536
525 class C(HasTraits):
537 class C(HasTraits):
526 inst = Instance(Foo)
538 inst = Instance(Foo)
527 c = C()
539 c = C()
528 self.assert_(c.inst is None)
540 self.assert_(c.inst is None)
529
541
530 def test_bad_default(self):
542 def test_bad_default(self):
531 class Foo(object): pass
543 class Foo(object): pass
532
544
533 class A(HasTraits):
545 class A(HasTraits):
534 inst = Instance(Foo, allow_none=False)
546 inst = Instance(Foo, allow_none=False)
535
547
536 self.assertRaises(TraitError, A)
548 self.assertRaises(TraitError, A)
537
549
538 def test_instance(self):
550 def test_instance(self):
539 class Foo(object): pass
551 class Foo(object): pass
540
552
541 def inner():
553 def inner():
542 class A(HasTraits):
554 class A(HasTraits):
543 inst = Instance(Foo())
555 inst = Instance(Foo())
544
556
545 self.assertRaises(TraitError, inner)
557 self.assertRaises(TraitError, inner)
546
558
547
559
548 class TestThis(TestCase):
560 class TestThis(TestCase):
549
561
550 def test_this_class(self):
562 def test_this_class(self):
551 class Foo(HasTraits):
563 class Foo(HasTraits):
552 this = This
564 this = This
553
565
554 f = Foo()
566 f = Foo()
555 self.assertEquals(f.this, None)
567 self.assertEquals(f.this, None)
556 g = Foo()
568 g = Foo()
557 f.this = g
569 f.this = g
558 self.assertEquals(f.this, g)
570 self.assertEquals(f.this, g)
559 self.assertRaises(TraitError, setattr, f, 'this', 10)
571 self.assertRaises(TraitError, setattr, f, 'this', 10)
560
572
561 def test_this_inst(self):
573 def test_this_inst(self):
562 class Foo(HasTraits):
574 class Foo(HasTraits):
563 this = This()
575 this = This()
564
576
565 f = Foo()
577 f = Foo()
566 f.this = Foo()
578 f.this = Foo()
567 self.assert_(isinstance(f.this, Foo))
579 self.assert_(isinstance(f.this, Foo))
568
580
569 def test_subclass(self):
581 def test_subclass(self):
570 class Foo(HasTraits):
582 class Foo(HasTraits):
571 t = This()
583 t = This()
572 class Bar(Foo):
584 class Bar(Foo):
573 pass
585 pass
574 f = Foo()
586 f = Foo()
575 b = Bar()
587 b = Bar()
576 f.t = b
588 f.t = b
577 b.t = f
589 b.t = f
578 self.assertEquals(f.t, b)
590 self.assertEquals(f.t, b)
579 self.assertEquals(b.t, f)
591 self.assertEquals(b.t, f)
580
592
581 def test_subclass_override(self):
593 def test_subclass_override(self):
582 class Foo(HasTraits):
594 class Foo(HasTraits):
583 t = This()
595 t = This()
584 class Bar(Foo):
596 class Bar(Foo):
585 t = This()
597 t = This()
586 f = Foo()
598 f = Foo()
587 b = Bar()
599 b = Bar()
588 f.t = b
600 f.t = b
589 self.assertEquals(f.t, b)
601 self.assertEquals(f.t, b)
590 self.assertRaises(TraitError, setattr, b, 't', f)
602 self.assertRaises(TraitError, setattr, b, 't', f)
591
603
592 class TraitTestBase(TestCase):
604 class TraitTestBase(TestCase):
593 """A best testing class for basic trait types."""
605 """A best testing class for basic trait types."""
594
606
595 def assign(self, value):
607 def assign(self, value):
596 self.obj.value = value
608 self.obj.value = value
597
609
598 def coerce(self, value):
610 def coerce(self, value):
599 return value
611 return value
600
612
601 def test_good_values(self):
613 def test_good_values(self):
602 if hasattr(self, '_good_values'):
614 if hasattr(self, '_good_values'):
603 for value in self._good_values:
615 for value in self._good_values:
604 self.assign(value)
616 self.assign(value)
605 self.assertEquals(self.obj.value, self.coerce(value))
617 self.assertEquals(self.obj.value, self.coerce(value))
606
618
607 def test_bad_values(self):
619 def test_bad_values(self):
608 if hasattr(self, '_bad_values'):
620 if hasattr(self, '_bad_values'):
609 for value in self._bad_values:
621 for value in self._bad_values:
610 self.assertRaises(TraitError, self.assign, value)
622 self.assertRaises(TraitError, self.assign, value)
611
623
612 def test_default_value(self):
624 def test_default_value(self):
613 if hasattr(self, '_default_value'):
625 if hasattr(self, '_default_value'):
614 self.assertEquals(self._default_value, self.obj.value)
626 self.assertEquals(self._default_value, self.obj.value)
615
627
616
628
617 class AnyTrait(HasTraits):
629 class AnyTrait(HasTraits):
618
630
619 value = Any
631 value = Any
620
632
621 class AnyTraitTest(TraitTestBase):
633 class AnyTraitTest(TraitTestBase):
622
634
623 obj = AnyTrait()
635 obj = AnyTrait()
624
636
625 _default_value = None
637 _default_value = None
626 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
638 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
627 _bad_values = []
639 _bad_values = []
628
640
629
641
630 class IntTrait(HasTraits):
642 class IntTrait(HasTraits):
631
643
632 value = Int(99)
644 value = Int(99)
633
645
634 class TestInt(TraitTestBase):
646 class TestInt(TraitTestBase):
635
647
636 obj = IntTrait()
648 obj = IntTrait()
637 _default_value = 99
649 _default_value = 99
638 _good_values = [10, -10]
650 _good_values = [10, -10]
639 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
651 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
640 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
652 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
641 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
653 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
642
654
643
655
644 class LongTrait(HasTraits):
656 class LongTrait(HasTraits):
645
657
646 value = Long(99L)
658 value = Long(99L)
647
659
648 class TestLong(TraitTestBase):
660 class TestLong(TraitTestBase):
649
661
650 obj = LongTrait()
662 obj = LongTrait()
651
663
652 _default_value = 99L
664 _default_value = 99L
653 _good_values = [10, -10, 10L, -10L]
665 _good_values = [10, -10, 10L, -10L]
654 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
666 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
655 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
667 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
656 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
668 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
657 u'-10.1']
669 u'-10.1']
658
670
659
671
660 class FloatTrait(HasTraits):
672 class FloatTrait(HasTraits):
661
673
662 value = Float(99.0)
674 value = Float(99.0)
663
675
664 class TestFloat(TraitTestBase):
676 class TestFloat(TraitTestBase):
665
677
666 obj = FloatTrait()
678 obj = FloatTrait()
667
679
668 _default_value = 99.0
680 _default_value = 99.0
669 _good_values = [10, -10, 10.1, -10.1]
681 _good_values = [10, -10, 10.1, -10.1]
670 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
682 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
671 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
683 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
672 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
684 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
673
685
674
686
675 class ComplexTrait(HasTraits):
687 class ComplexTrait(HasTraits):
676
688
677 value = Complex(99.0-99.0j)
689 value = Complex(99.0-99.0j)
678
690
679 class TestComplex(TraitTestBase):
691 class TestComplex(TraitTestBase):
680
692
681 obj = ComplexTrait()
693 obj = ComplexTrait()
682
694
683 _default_value = 99.0-99.0j
695 _default_value = 99.0-99.0j
684 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
696 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
685 10.1j, 10.1+10.1j, 10.1-10.1j]
697 10.1j, 10.1+10.1j, 10.1-10.1j]
686 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
698 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
687
699
688
700
689 class StringTrait(HasTraits):
701 class StringTrait(HasTraits):
690
702
691 value = Str('string')
703 value = Str('string')
692
704
693 class TestString(TraitTestBase):
705 class TestString(TraitTestBase):
694
706
695 obj = StringTrait()
707 obj = StringTrait()
696
708
697 _default_value = 'string'
709 _default_value = 'string'
698 _good_values = ['10', '-10', '10L',
710 _good_values = ['10', '-10', '10L',
699 '-10L', '10.1', '-10.1', 'string']
711 '-10L', '10.1', '-10.1', 'string']
700 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
712 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
701 ['ten'],{'ten': 10},(10,), None, u'string']
713 ['ten'],{'ten': 10},(10,), None, u'string']
702
714
703
715
704 class UnicodeTrait(HasTraits):
716 class UnicodeTrait(HasTraits):
705
717
706 value = Unicode(u'unicode')
718 value = Unicode(u'unicode')
707
719
708 class TestUnicode(TraitTestBase):
720 class TestUnicode(TraitTestBase):
709
721
710 obj = UnicodeTrait()
722 obj = UnicodeTrait()
711
723
712 _default_value = u'unicode'
724 _default_value = u'unicode'
713 _good_values = ['10', '-10', '10L', '-10L', '10.1',
725 _good_values = ['10', '-10', '10L', '-10L', '10.1',
714 '-10.1', '', u'', 'string', u'string', ]
726 '-10.1', '', u'', 'string', u'string', ]
715 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
727 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
716 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
728 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
717
729
718
730
719 class TCPAddressTrait(HasTraits):
731 class TCPAddressTrait(HasTraits):
720
732
721 value = TCPAddress()
733 value = TCPAddress()
722
734
723 class TestTCPAddress(TraitTestBase):
735 class TestTCPAddress(TraitTestBase):
724
736
725 obj = TCPAddressTrait()
737 obj = TCPAddressTrait()
726
738
727 _default_value = ('127.0.0.1',0)
739 _default_value = ('127.0.0.1',0)
728 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
740 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
729 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
741 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
@@ -1,1079 +1,1075 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight Traits like module.
4 A lightweight Traits like module.
5
5
6 This is designed to provide a lightweight, simple, pure Python version of
6 This is designed to provide a lightweight, simple, pure Python version of
7 many of the capabilities of enthought.traits. This includes:
7 many of the capabilities of enthought.traits. This includes:
8
8
9 * Validation
9 * Validation
10 * Type specification with defaults
10 * Type specification with defaults
11 * Static and dynamic notification
11 * Static and dynamic notification
12 * Basic predefined types
12 * Basic predefined types
13 * An API that is similar to enthought.traits
13 * An API that is similar to enthought.traits
14
14
15 We don't support:
15 We don't support:
16
16
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types. Most importantly, we don't provide container
19 * A full set of trait types. Most importantly, we don't provide container
20 traits (list, dict, tuple) that can trigger notifications if their
20 traits (list, dict, tuple) that can trigger notifications if their
21 contents change.
21 contents change.
22 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
23
23
24 There are also some important difference in our design:
24 There are also some important difference in our design:
25
25
26 * enthought.traits does not validate default values. We do.
26 * enthought.traits does not validate default values. We do.
27
27
28 We choose to create this module because we need these capabilities, but
28 We choose to create this module because we need these capabilities, but
29 we need them to be pure Python so they work in all Python implementations,
29 we need them to be pure Python so they work in all Python implementations,
30 including Jython and IronPython.
30 including Jython and IronPython.
31
31
32 Authors:
32 Authors:
33
33
34 * Brian Granger
34 * Brian Granger
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
36 and is licensed under the BSD license. Also, many of the ideas also come
36 and is licensed under the BSD license. Also, many of the ideas also come
37 from enthought.traits even though our implementation is very different.
37 from enthought.traits even though our implementation is very different.
38 """
38 """
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Copyright (C) 2008-2009 The IPython Development Team
41 # Copyright (C) 2008-2009 The IPython Development Team
42 #
42 #
43 # Distributed under the terms of the BSD License. The full license is in
43 # Distributed under the terms of the BSD License. The full license is in
44 # the file COPYING, distributed as part of this software.
44 # the file COPYING, distributed as part of this software.
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Imports
48 # Imports
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 import inspect
52 import inspect
53 import sys
53 import sys
54 import types
54 import types
55 from types import (
55 from types import (
56 InstanceType, ClassType, FunctionType,
56 InstanceType, ClassType, FunctionType,
57 ListType, TupleType
57 ListType, TupleType
58 )
58 )
59 from .importstring import import_item
59 from .importstring import import_item
60
60
61 ClassTypes = (ClassType, type)
61 ClassTypes = (ClassType, type)
62
62
63 SequenceTypes = (ListType, TupleType)
63 SequenceTypes = (ListType, TupleType)
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Basic classes
66 # Basic classes
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69
69
70 class NoDefaultSpecified ( object ): pass
70 class NoDefaultSpecified ( object ): pass
71 NoDefaultSpecified = NoDefaultSpecified()
71 NoDefaultSpecified = NoDefaultSpecified()
72
72
73
73
74 class Undefined ( object ): pass
74 class Undefined ( object ): pass
75 Undefined = Undefined()
75 Undefined = Undefined()
76
76
77 class TraitError(Exception):
77 class TraitError(Exception):
78 pass
78 pass
79
79
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81 # Utilities
81 # Utilities
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83
83
84
84
85 def class_of ( object ):
85 def class_of ( object ):
86 """ Returns a string containing the class name of an object with the
86 """ Returns a string containing the class name of an object with the
87 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
87 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
88 'a PlotValue').
88 'a PlotValue').
89 """
89 """
90 if isinstance( object, basestring ):
90 if isinstance( object, basestring ):
91 return add_article( object )
91 return add_article( object )
92
92
93 return add_article( object.__class__.__name__ )
93 return add_article( object.__class__.__name__ )
94
94
95
95
96 def add_article ( name ):
96 def add_article ( name ):
97 """ Returns a string containing the correct indefinite article ('a' or 'an')
97 """ Returns a string containing the correct indefinite article ('a' or 'an')
98 prefixed to the specified string.
98 prefixed to the specified string.
99 """
99 """
100 if name[:1].lower() in 'aeiou':
100 if name[:1].lower() in 'aeiou':
101 return 'an ' + name
101 return 'an ' + name
102
102
103 return 'a ' + name
103 return 'a ' + name
104
104
105
105
106 def repr_type(obj):
106 def repr_type(obj):
107 """ Return a string representation of a value and its type for readable
107 """ Return a string representation of a value and its type for readable
108 error messages.
108 error messages.
109 """
109 """
110 the_type = type(obj)
110 the_type = type(obj)
111 if the_type is InstanceType:
111 if the_type is InstanceType:
112 # Old-style class.
112 # Old-style class.
113 the_type = obj.__class__
113 the_type = obj.__class__
114 msg = '%r %r' % (obj, the_type)
114 msg = '%r %r' % (obj, the_type)
115 return msg
115 return msg
116
116
117
117
118 def parse_notifier_name(name):
118 def parse_notifier_name(name):
119 """Convert the name argument to a list of names.
119 """Convert the name argument to a list of names.
120
120
121 Examples
121 Examples
122 --------
122 --------
123
123
124 >>> parse_notifier_name('a')
124 >>> parse_notifier_name('a')
125 ['a']
125 ['a']
126 >>> parse_notifier_name(['a','b'])
126 >>> parse_notifier_name(['a','b'])
127 ['a', 'b']
127 ['a', 'b']
128 >>> parse_notifier_name(None)
128 >>> parse_notifier_name(None)
129 ['anytrait']
129 ['anytrait']
130 """
130 """
131 if isinstance(name, str):
131 if isinstance(name, str):
132 return [name]
132 return [name]
133 elif name is None:
133 elif name is None:
134 return ['anytrait']
134 return ['anytrait']
135 elif isinstance(name, (list, tuple)):
135 elif isinstance(name, (list, tuple)):
136 for n in name:
136 for n in name:
137 assert isinstance(n, str), "names must be strings"
137 assert isinstance(n, str), "names must be strings"
138 return name
138 return name
139
139
140
140
141 class _SimpleTest:
141 class _SimpleTest:
142 def __init__ ( self, value ): self.value = value
142 def __init__ ( self, value ): self.value = value
143 def __call__ ( self, test ):
143 def __call__ ( self, test ):
144 return test == self.value
144 return test == self.value
145 def __repr__(self):
145 def __repr__(self):
146 return "<SimpleTest(%r)" % self.value
146 return "<SimpleTest(%r)" % self.value
147 def __str__(self):
147 def __str__(self):
148 return self.__repr__()
148 return self.__repr__()
149
149
150
150
151 def getmembers(object, predicate=None):
151 def getmembers(object, predicate=None):
152 """A safe version of inspect.getmembers that handles missing attributes.
152 """A safe version of inspect.getmembers that handles missing attributes.
153
153
154 This is useful when there are descriptor based attributes that for
154 This is useful when there are descriptor based attributes that for
155 some reason raise AttributeError even though they exist. This happens
155 some reason raise AttributeError even though they exist. This happens
156 in zope.inteface with the __provides__ attribute.
156 in zope.inteface with the __provides__ attribute.
157 """
157 """
158 results = []
158 results = []
159 for key in dir(object):
159 for key in dir(object):
160 try:
160 try:
161 value = getattr(object, key)
161 value = getattr(object, key)
162 except AttributeError:
162 except AttributeError:
163 pass
163 pass
164 else:
164 else:
165 if not predicate or predicate(value):
165 if not predicate or predicate(value):
166 results.append((key, value))
166 results.append((key, value))
167 results.sort()
167 results.sort()
168 return results
168 return results
169
169
170
170
171 #-----------------------------------------------------------------------------
171 #-----------------------------------------------------------------------------
172 # Base TraitType for all traits
172 # Base TraitType for all traits
173 #-----------------------------------------------------------------------------
173 #-----------------------------------------------------------------------------
174
174
175
175
176 class TraitType(object):
176 class TraitType(object):
177 """A base class for all trait descriptors.
177 """A base class for all trait descriptors.
178
178
179 Notes
179 Notes
180 -----
180 -----
181 Our implementation of traits is based on Python's descriptor
181 Our implementation of traits is based on Python's descriptor
182 prototol. This class is the base class for all such descriptors. The
182 prototol. This class is the base class for all such descriptors. The
183 only magic we use is a custom metaclass for the main :class:`HasTraits`
183 only magic we use is a custom metaclass for the main :class:`HasTraits`
184 class that does the following:
184 class that does the following:
185
185
186 1. Sets the :attr:`name` attribute of every :class:`TraitType`
186 1. Sets the :attr:`name` attribute of every :class:`TraitType`
187 instance in the class dict to the name of the attribute.
187 instance in the class dict to the name of the attribute.
188 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
188 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
189 instance in the class dict to the *class* that declared the trait.
189 instance in the class dict to the *class* that declared the trait.
190 This is used by the :class:`This` trait to allow subclasses to
190 This is used by the :class:`This` trait to allow subclasses to
191 accept superclasses for :class:`This` values.
191 accept superclasses for :class:`This` values.
192 """
192 """
193
193
194
194
195 metadata = {}
195 metadata = {}
196 default_value = Undefined
196 default_value = Undefined
197 info_text = 'any value'
197 info_text = 'any value'
198
198
199 def __init__(self, default_value=NoDefaultSpecified, **metadata):
199 def __init__(self, default_value=NoDefaultSpecified, **metadata):
200 """Create a TraitType.
200 """Create a TraitType.
201 """
201 """
202 if default_value is not NoDefaultSpecified:
202 if default_value is not NoDefaultSpecified:
203 self.default_value = default_value
203 self.default_value = default_value
204
204
205 if len(metadata) > 0:
205 if len(metadata) > 0:
206 if len(self.metadata) > 0:
206 if len(self.metadata) > 0:
207 self._metadata = self.metadata.copy()
207 self._metadata = self.metadata.copy()
208 self._metadata.update(metadata)
208 self._metadata.update(metadata)
209 else:
209 else:
210 self._metadata = metadata
210 self._metadata = metadata
211 else:
211 else:
212 self._metadata = self.metadata
212 self._metadata = self.metadata
213
213
214 self.init()
214 self.init()
215
215
216 def init(self):
216 def init(self):
217 pass
217 pass
218
218
219 def get_default_value(self):
219 def get_default_value(self):
220 """Create a new instance of the default value."""
220 """Create a new instance of the default value."""
221 return self.default_value
221 return self.default_value
222
222
223 def instance_init(self, obj):
223 def instance_init(self, obj):
224 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
224 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
225
225
226 Some stages of initialization must be delayed until the parent
226 Some stages of initialization must be delayed until the parent
227 :class:`HasTraits` instance has been created. This method is
227 :class:`HasTraits` instance has been created. This method is
228 called in :meth:`HasTraits.__new__` after the instance has been
228 called in :meth:`HasTraits.__new__` after the instance has been
229 created.
229 created.
230
230
231 This method trigger the creation and validation of default values
231 This method trigger the creation and validation of default values
232 and also things like the resolution of str given class names in
232 and also things like the resolution of str given class names in
233 :class:`Type` and :class`Instance`.
233 :class:`Type` and :class`Instance`.
234
234
235 Parameters
235 Parameters
236 ----------
236 ----------
237 obj : :class:`HasTraits` instance
237 obj : :class:`HasTraits` instance
238 The parent :class:`HasTraits` instance that has just been
238 The parent :class:`HasTraits` instance that has just been
239 created.
239 created.
240 """
240 """
241 self.set_default_value(obj)
241 self.set_default_value(obj)
242
242
243 def set_default_value(self, obj):
243 def set_default_value(self, obj):
244 """Set the default value on a per instance basis.
244 """Set the default value on a per instance basis.
245
245
246 This method is called by :meth:`instance_init` to create and
246 This method is called by :meth:`instance_init` to create and
247 validate the default value. The creation and validation of
247 validate the default value. The creation and validation of
248 default values must be delayed until the parent :class:`HasTraits`
248 default values must be delayed until the parent :class:`HasTraits`
249 class has been instantiated.
249 class has been instantiated.
250 """
250 """
251 # Check for a deferred initializer defined in the same class as the
251 # Check for a deferred initializer defined in the same class as the
252 # trait declaration or above.
252 # trait declaration or above.
253 mro = type(obj).mro()
253 mro = type(obj).mro()
254 meth_name = '_%s_default' % self.name
254 meth_name = '_%s_default' % self.name
255 for cls in mro[:mro.index(self.this_class)+1]:
255 for cls in mro[:mro.index(self.this_class)+1]:
256 if meth_name in cls.__dict__:
256 if meth_name in cls.__dict__:
257 break
257 break
258 else:
258 else:
259 # We didn't find one. Do static initialization.
259 # We didn't find one. Do static initialization.
260 dv = self.get_default_value()
260 dv = self.get_default_value()
261 newdv = self._validate(obj, dv)
261 newdv = self._validate(obj, dv)
262 obj._trait_values[self.name] = newdv
262 obj._trait_values[self.name] = newdv
263 return
263 return
264 # Complete the dynamic initialization.
264 # Complete the dynamic initialization.
265 self.dynamic_initializer = cls.__dict__[meth_name]
265 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
266
266
267 def __get__(self, obj, cls=None):
267 def __get__(self, obj, cls=None):
268 """Get the value of the trait by self.name for the instance.
268 """Get the value of the trait by self.name for the instance.
269
269
270 Default values are instantiated when :meth:`HasTraits.__new__`
270 Default values are instantiated when :meth:`HasTraits.__new__`
271 is called. Thus by the time this method gets called either the
271 is called. Thus by the time this method gets called either the
272 default value or a user defined value (they called :meth:`__set__`)
272 default value or a user defined value (they called :meth:`__set__`)
273 is in the :class:`HasTraits` instance.
273 is in the :class:`HasTraits` instance.
274 """
274 """
275 if obj is None:
275 if obj is None:
276 return self
276 return self
277 else:
277 else:
278 try:
278 try:
279 value = obj._trait_values[self.name]
279 value = obj._trait_values[self.name]
280 except KeyError:
280 except KeyError:
281 # Check for a dynamic initializer.
281 # Check for a dynamic initializer.
282 if hasattr(self, 'dynamic_initializer'):
282 if self.name in obj._trait_dyn_inits:
283 value = self.dynamic_initializer(obj)
283 value = obj._trait_dyn_inits[self.name](obj)
284 # FIXME: Do we really validate here?
284 # FIXME: Do we really validate here?
285 value = self._validate(obj, value)
285 value = self._validate(obj, value)
286 obj._trait_values[self.name] = value
286 obj._trait_values[self.name] = value
287 return value
287 return value
288 else:
288 else:
289 raise TraitError('Unexpected error in TraitType: '
289 raise TraitError('Unexpected error in TraitType: '
290 'both default value and dynamic initializer are '
290 'both default value and dynamic initializer are '
291 'absent.')
291 'absent.')
292 except Exception:
292 except Exception:
293 # HasTraits should call set_default_value to populate
293 # HasTraits should call set_default_value to populate
294 # this. So this should never be reached.
294 # this. So this should never be reached.
295 raise TraitError('Unexpected error in TraitType: '
295 raise TraitError('Unexpected error in TraitType: '
296 'default value not set properly')
296 'default value not set properly')
297 else:
297 else:
298 return value
298 return value
299
299
300 def __set__(self, obj, value):
300 def __set__(self, obj, value):
301 new_value = self._validate(obj, value)
301 new_value = self._validate(obj, value)
302 old_value = self.__get__(obj)
302 old_value = self.__get__(obj)
303 if old_value != new_value:
303 if old_value != new_value:
304 obj._trait_values[self.name] = new_value
304 obj._trait_values[self.name] = new_value
305 obj._notify_trait(self.name, old_value, new_value)
305 obj._notify_trait(self.name, old_value, new_value)
306
306
307 def _validate(self, obj, value):
307 def _validate(self, obj, value):
308 if hasattr(self, 'validate'):
308 if hasattr(self, 'validate'):
309 return self.validate(obj, value)
309 return self.validate(obj, value)
310 elif hasattr(self, 'is_valid_for'):
310 elif hasattr(self, 'is_valid_for'):
311 valid = self.is_valid_for(value)
311 valid = self.is_valid_for(value)
312 if valid:
312 if valid:
313 return value
313 return value
314 else:
314 else:
315 raise TraitError('invalid value for type: %r' % value)
315 raise TraitError('invalid value for type: %r' % value)
316 elif hasattr(self, 'value_for'):
316 elif hasattr(self, 'value_for'):
317 return self.value_for(value)
317 return self.value_for(value)
318 else:
318 else:
319 return value
319 return value
320
320
321 def set_dynamic_initializer(self, method):
322 """ Set the dynamic initializer method, if any.
323 """
324 self.dynamic_initializer = method
325
326 def info(self):
321 def info(self):
327 return self.info_text
322 return self.info_text
328
323
329 def error(self, obj, value):
324 def error(self, obj, value):
330 if obj is not None:
325 if obj is not None:
331 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
326 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
332 % (self.name, class_of(obj),
327 % (self.name, class_of(obj),
333 self.info(), repr_type(value))
328 self.info(), repr_type(value))
334 else:
329 else:
335 e = "The '%s' trait must be %s, but a value of %r was specified." \
330 e = "The '%s' trait must be %s, but a value of %r was specified." \
336 % (self.name, self.info(), repr_type(value))
331 % (self.name, self.info(), repr_type(value))
337 raise TraitError(e)
332 raise TraitError(e)
338
333
339 def get_metadata(self, key):
334 def get_metadata(self, key):
340 return getattr(self, '_metadata', {}).get(key, None)
335 return getattr(self, '_metadata', {}).get(key, None)
341
336
342 def set_metadata(self, key, value):
337 def set_metadata(self, key, value):
343 getattr(self, '_metadata', {})[key] = value
338 getattr(self, '_metadata', {})[key] = value
344
339
345
340
346 #-----------------------------------------------------------------------------
341 #-----------------------------------------------------------------------------
347 # The HasTraits implementation
342 # The HasTraits implementation
348 #-----------------------------------------------------------------------------
343 #-----------------------------------------------------------------------------
349
344
350
345
351 class MetaHasTraits(type):
346 class MetaHasTraits(type):
352 """A metaclass for HasTraits.
347 """A metaclass for HasTraits.
353
348
354 This metaclass makes sure that any TraitType class attributes are
349 This metaclass makes sure that any TraitType class attributes are
355 instantiated and sets their name attribute.
350 instantiated and sets their name attribute.
356 """
351 """
357
352
358 def __new__(mcls, name, bases, classdict):
353 def __new__(mcls, name, bases, classdict):
359 """Create the HasTraits class.
354 """Create the HasTraits class.
360
355
361 This instantiates all TraitTypes in the class dict and sets their
356 This instantiates all TraitTypes in the class dict and sets their
362 :attr:`name` attribute.
357 :attr:`name` attribute.
363 """
358 """
364 # print "MetaHasTraitlets (mcls, name): ", mcls, name
359 # print "MetaHasTraitlets (mcls, name): ", mcls, name
365 # print "MetaHasTraitlets (bases): ", bases
360 # print "MetaHasTraitlets (bases): ", bases
366 # print "MetaHasTraitlets (classdict): ", classdict
361 # print "MetaHasTraitlets (classdict): ", classdict
367 for k,v in classdict.iteritems():
362 for k,v in classdict.iteritems():
368 if isinstance(v, TraitType):
363 if isinstance(v, TraitType):
369 v.name = k
364 v.name = k
370 elif inspect.isclass(v):
365 elif inspect.isclass(v):
371 if issubclass(v, TraitType):
366 if issubclass(v, TraitType):
372 vinst = v()
367 vinst = v()
373 vinst.name = k
368 vinst.name = k
374 classdict[k] = vinst
369 classdict[k] = vinst
375 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
370 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
376
371
377 def __init__(cls, name, bases, classdict):
372 def __init__(cls, name, bases, classdict):
378 """Finish initializing the HasTraits class.
373 """Finish initializing the HasTraits class.
379
374
380 This sets the :attr:`this_class` attribute of each TraitType in the
375 This sets the :attr:`this_class` attribute of each TraitType in the
381 class dict to the newly created class ``cls``.
376 class dict to the newly created class ``cls``.
382 """
377 """
383 for k, v in classdict.iteritems():
378 for k, v in classdict.iteritems():
384 if isinstance(v, TraitType):
379 if isinstance(v, TraitType):
385 v.this_class = cls
380 v.this_class = cls
386 super(MetaHasTraits, cls).__init__(name, bases, classdict)
381 super(MetaHasTraits, cls).__init__(name, bases, classdict)
387
382
388 class HasTraits(object):
383 class HasTraits(object):
389
384
390 __metaclass__ = MetaHasTraits
385 __metaclass__ = MetaHasTraits
391
386
392 def __new__(cls, **kw):
387 def __new__(cls, **kw):
393 # This is needed because in Python 2.6 object.__new__ only accepts
388 # This is needed because in Python 2.6 object.__new__ only accepts
394 # the cls argument.
389 # the cls argument.
395 new_meth = super(HasTraits, cls).__new__
390 new_meth = super(HasTraits, cls).__new__
396 if new_meth is object.__new__:
391 if new_meth is object.__new__:
397 inst = new_meth(cls)
392 inst = new_meth(cls)
398 else:
393 else:
399 inst = new_meth(cls, **kw)
394 inst = new_meth(cls, **kw)
400 inst._trait_values = {}
395 inst._trait_values = {}
401 inst._trait_notifiers = {}
396 inst._trait_notifiers = {}
397 inst._trait_dyn_inits = {}
402 # Here we tell all the TraitType instances to set their default
398 # Here we tell all the TraitType instances to set their default
403 # values on the instance.
399 # values on the instance.
404 for key in dir(cls):
400 for key in dir(cls):
405 # Some descriptors raise AttributeError like zope.interface's
401 # Some descriptors raise AttributeError like zope.interface's
406 # __provides__ attributes even though they exist. This causes
402 # __provides__ attributes even though they exist. This causes
407 # AttributeErrors even though they are listed in dir(cls).
403 # AttributeErrors even though they are listed in dir(cls).
408 try:
404 try:
409 value = getattr(cls, key)
405 value = getattr(cls, key)
410 except AttributeError:
406 except AttributeError:
411 pass
407 pass
412 else:
408 else:
413 if isinstance(value, TraitType):
409 if isinstance(value, TraitType):
414 value.instance_init(inst)
410 value.instance_init(inst)
415
411
416 return inst
412 return inst
417
413
418 def __init__(self, **kw):
414 def __init__(self, **kw):
419 # Allow trait values to be set using keyword arguments.
415 # Allow trait values to be set using keyword arguments.
420 # We need to use setattr for this to trigger validation and
416 # We need to use setattr for this to trigger validation and
421 # notifications.
417 # notifications.
422 for key, value in kw.iteritems():
418 for key, value in kw.iteritems():
423 setattr(self, key, value)
419 setattr(self, key, value)
424
420
425 def _notify_trait(self, name, old_value, new_value):
421 def _notify_trait(self, name, old_value, new_value):
426
422
427 # First dynamic ones
423 # First dynamic ones
428 callables = self._trait_notifiers.get(name,[])
424 callables = self._trait_notifiers.get(name,[])
429 more_callables = self._trait_notifiers.get('anytrait',[])
425 more_callables = self._trait_notifiers.get('anytrait',[])
430 callables.extend(more_callables)
426 callables.extend(more_callables)
431
427
432 # Now static ones
428 # Now static ones
433 try:
429 try:
434 cb = getattr(self, '_%s_changed' % name)
430 cb = getattr(self, '_%s_changed' % name)
435 except:
431 except:
436 pass
432 pass
437 else:
433 else:
438 callables.append(cb)
434 callables.append(cb)
439
435
440 # Call them all now
436 # Call them all now
441 for c in callables:
437 for c in callables:
442 # Traits catches and logs errors here. I allow them to raise
438 # Traits catches and logs errors here. I allow them to raise
443 if callable(c):
439 if callable(c):
444 argspec = inspect.getargspec(c)
440 argspec = inspect.getargspec(c)
445 nargs = len(argspec[0])
441 nargs = len(argspec[0])
446 # Bound methods have an additional 'self' argument
442 # Bound methods have an additional 'self' argument
447 # I don't know how to treat unbound methods, but they
443 # I don't know how to treat unbound methods, but they
448 # can't really be used for callbacks.
444 # can't really be used for callbacks.
449 if isinstance(c, types.MethodType):
445 if isinstance(c, types.MethodType):
450 offset = -1
446 offset = -1
451 else:
447 else:
452 offset = 0
448 offset = 0
453 if nargs + offset == 0:
449 if nargs + offset == 0:
454 c()
450 c()
455 elif nargs + offset == 1:
451 elif nargs + offset == 1:
456 c(name)
452 c(name)
457 elif nargs + offset == 2:
453 elif nargs + offset == 2:
458 c(name, new_value)
454 c(name, new_value)
459 elif nargs + offset == 3:
455 elif nargs + offset == 3:
460 c(name, old_value, new_value)
456 c(name, old_value, new_value)
461 else:
457 else:
462 raise TraitError('a trait changed callback '
458 raise TraitError('a trait changed callback '
463 'must have 0-3 arguments.')
459 'must have 0-3 arguments.')
464 else:
460 else:
465 raise TraitError('a trait changed callback '
461 raise TraitError('a trait changed callback '
466 'must be callable.')
462 'must be callable.')
467
463
468
464
469 def _add_notifiers(self, handler, name):
465 def _add_notifiers(self, handler, name):
470 if not self._trait_notifiers.has_key(name):
466 if not self._trait_notifiers.has_key(name):
471 nlist = []
467 nlist = []
472 self._trait_notifiers[name] = nlist
468 self._trait_notifiers[name] = nlist
473 else:
469 else:
474 nlist = self._trait_notifiers[name]
470 nlist = self._trait_notifiers[name]
475 if handler not in nlist:
471 if handler not in nlist:
476 nlist.append(handler)
472 nlist.append(handler)
477
473
478 def _remove_notifiers(self, handler, name):
474 def _remove_notifiers(self, handler, name):
479 if self._trait_notifiers.has_key(name):
475 if self._trait_notifiers.has_key(name):
480 nlist = self._trait_notifiers[name]
476 nlist = self._trait_notifiers[name]
481 try:
477 try:
482 index = nlist.index(handler)
478 index = nlist.index(handler)
483 except ValueError:
479 except ValueError:
484 pass
480 pass
485 else:
481 else:
486 del nlist[index]
482 del nlist[index]
487
483
488 def on_trait_change(self, handler, name=None, remove=False):
484 def on_trait_change(self, handler, name=None, remove=False):
489 """Setup a handler to be called when a trait changes.
485 """Setup a handler to be called when a trait changes.
490
486
491 This is used to setup dynamic notifications of trait changes.
487 This is used to setup dynamic notifications of trait changes.
492
488
493 Static handlers can be created by creating methods on a HasTraits
489 Static handlers can be created by creating methods on a HasTraits
494 subclass with the naming convention '_[traitname]_changed'. Thus,
490 subclass with the naming convention '_[traitname]_changed'. Thus,
495 to create static handler for the trait 'a', create the method
491 to create static handler for the trait 'a', create the method
496 _a_changed(self, name, old, new) (fewer arguments can be used, see
492 _a_changed(self, name, old, new) (fewer arguments can be used, see
497 below).
493 below).
498
494
499 Parameters
495 Parameters
500 ----------
496 ----------
501 handler : callable
497 handler : callable
502 A callable that is called when a trait changes. Its
498 A callable that is called when a trait changes. Its
503 signature can be handler(), handler(name), handler(name, new)
499 signature can be handler(), handler(name), handler(name, new)
504 or handler(name, old, new).
500 or handler(name, old, new).
505 name : list, str, None
501 name : list, str, None
506 If None, the handler will apply to all traits. If a list
502 If None, the handler will apply to all traits. If a list
507 of str, handler will apply to all names in the list. If a
503 of str, handler will apply to all names in the list. If a
508 str, the handler will apply just to that name.
504 str, the handler will apply just to that name.
509 remove : bool
505 remove : bool
510 If False (the default), then install the handler. If True
506 If False (the default), then install the handler. If True
511 then unintall it.
507 then unintall it.
512 """
508 """
513 if remove:
509 if remove:
514 names = parse_notifier_name(name)
510 names = parse_notifier_name(name)
515 for n in names:
511 for n in names:
516 self._remove_notifiers(handler, n)
512 self._remove_notifiers(handler, n)
517 else:
513 else:
518 names = parse_notifier_name(name)
514 names = parse_notifier_name(name)
519 for n in names:
515 for n in names:
520 self._add_notifiers(handler, n)
516 self._add_notifiers(handler, n)
521
517
522 def trait_names(self, **metadata):
518 def trait_names(self, **metadata):
523 """Get a list of all the names of this classes traits."""
519 """Get a list of all the names of this classes traits."""
524 return self.traits(**metadata).keys()
520 return self.traits(**metadata).keys()
525
521
526 def traits(self, **metadata):
522 def traits(self, **metadata):
527 """Get a list of all the traits of this class.
523 """Get a list of all the traits of this class.
528
524
529 The TraitTypes returned don't know anything about the values
525 The TraitTypes returned don't know anything about the values
530 that the various HasTrait's instances are holding.
526 that the various HasTrait's instances are holding.
531
527
532 This follows the same algorithm as traits does and does not allow
528 This follows the same algorithm as traits does and does not allow
533 for any simple way of specifying merely that a metadata name
529 for any simple way of specifying merely that a metadata name
534 exists, but has any value. This is because get_metadata returns
530 exists, but has any value. This is because get_metadata returns
535 None if a metadata key doesn't exist.
531 None if a metadata key doesn't exist.
536 """
532 """
537 traits = dict([memb for memb in getmembers(self.__class__) if \
533 traits = dict([memb for memb in getmembers(self.__class__) if \
538 isinstance(memb[1], TraitType)])
534 isinstance(memb[1], TraitType)])
539
535
540 if len(metadata) == 0:
536 if len(metadata) == 0:
541 return traits
537 return traits
542
538
543 for meta_name, meta_eval in metadata.items():
539 for meta_name, meta_eval in metadata.items():
544 if type(meta_eval) is not FunctionType:
540 if type(meta_eval) is not FunctionType:
545 metadata[meta_name] = _SimpleTest(meta_eval)
541 metadata[meta_name] = _SimpleTest(meta_eval)
546
542
547 result = {}
543 result = {}
548 for name, trait in traits.items():
544 for name, trait in traits.items():
549 for meta_name, meta_eval in metadata.items():
545 for meta_name, meta_eval in metadata.items():
550 if not meta_eval(trait.get_metadata(meta_name)):
546 if not meta_eval(trait.get_metadata(meta_name)):
551 break
547 break
552 else:
548 else:
553 result[name] = trait
549 result[name] = trait
554
550
555 return result
551 return result
556
552
557 def trait_metadata(self, traitname, key):
553 def trait_metadata(self, traitname, key):
558 """Get metadata values for trait by key."""
554 """Get metadata values for trait by key."""
559 try:
555 try:
560 trait = getattr(self.__class__, traitname)
556 trait = getattr(self.__class__, traitname)
561 except AttributeError:
557 except AttributeError:
562 raise TraitError("Class %s does not have a trait named %s" %
558 raise TraitError("Class %s does not have a trait named %s" %
563 (self.__class__.__name__, traitname))
559 (self.__class__.__name__, traitname))
564 else:
560 else:
565 return trait.get_metadata(key)
561 return trait.get_metadata(key)
566
562
567 #-----------------------------------------------------------------------------
563 #-----------------------------------------------------------------------------
568 # Actual TraitTypes implementations/subclasses
564 # Actual TraitTypes implementations/subclasses
569 #-----------------------------------------------------------------------------
565 #-----------------------------------------------------------------------------
570
566
571 #-----------------------------------------------------------------------------
567 #-----------------------------------------------------------------------------
572 # TraitTypes subclasses for handling classes and instances of classes
568 # TraitTypes subclasses for handling classes and instances of classes
573 #-----------------------------------------------------------------------------
569 #-----------------------------------------------------------------------------
574
570
575
571
576 class ClassBasedTraitType(TraitType):
572 class ClassBasedTraitType(TraitType):
577 """A trait with error reporting for Type, Instance and This."""
573 """A trait with error reporting for Type, Instance and This."""
578
574
579 def error(self, obj, value):
575 def error(self, obj, value):
580 kind = type(value)
576 kind = type(value)
581 if kind is InstanceType:
577 if kind is InstanceType:
582 msg = 'class %s' % value.__class__.__name__
578 msg = 'class %s' % value.__class__.__name__
583 else:
579 else:
584 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
580 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
585
581
586 super(ClassBasedTraitType, self).error(obj, msg)
582 super(ClassBasedTraitType, self).error(obj, msg)
587
583
588
584
589 class Type(ClassBasedTraitType):
585 class Type(ClassBasedTraitType):
590 """A trait whose value must be a subclass of a specified class."""
586 """A trait whose value must be a subclass of a specified class."""
591
587
592 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
588 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
593 """Construct a Type trait
589 """Construct a Type trait
594
590
595 A Type trait specifies that its values must be subclasses of
591 A Type trait specifies that its values must be subclasses of
596 a particular class.
592 a particular class.
597
593
598 If only ``default_value`` is given, it is used for the ``klass`` as
594 If only ``default_value`` is given, it is used for the ``klass`` as
599 well.
595 well.
600
596
601 Parameters
597 Parameters
602 ----------
598 ----------
603 default_value : class, str or None
599 default_value : class, str or None
604 The default value must be a subclass of klass. If an str,
600 The default value must be a subclass of klass. If an str,
605 the str must be a fully specified class name, like 'foo.bar.Bah'.
601 the str must be a fully specified class name, like 'foo.bar.Bah'.
606 The string is resolved into real class, when the parent
602 The string is resolved into real class, when the parent
607 :class:`HasTraits` class is instantiated.
603 :class:`HasTraits` class is instantiated.
608 klass : class, str, None
604 klass : class, str, None
609 Values of this trait must be a subclass of klass. The klass
605 Values of this trait must be a subclass of klass. The klass
610 may be specified in a string like: 'foo.bar.MyClass'.
606 may be specified in a string like: 'foo.bar.MyClass'.
611 The string is resolved into real class, when the parent
607 The string is resolved into real class, when the parent
612 :class:`HasTraits` class is instantiated.
608 :class:`HasTraits` class is instantiated.
613 allow_none : boolean
609 allow_none : boolean
614 Indicates whether None is allowed as an assignable value. Even if
610 Indicates whether None is allowed as an assignable value. Even if
615 ``False``, the default value may be ``None``.
611 ``False``, the default value may be ``None``.
616 """
612 """
617 if default_value is None:
613 if default_value is None:
618 if klass is None:
614 if klass is None:
619 klass = object
615 klass = object
620 elif klass is None:
616 elif klass is None:
621 klass = default_value
617 klass = default_value
622
618
623 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
619 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
624 raise TraitError("A Type trait must specify a class.")
620 raise TraitError("A Type trait must specify a class.")
625
621
626 self.klass = klass
622 self.klass = klass
627 self._allow_none = allow_none
623 self._allow_none = allow_none
628
624
629 super(Type, self).__init__(default_value, **metadata)
625 super(Type, self).__init__(default_value, **metadata)
630
626
631 def validate(self, obj, value):
627 def validate(self, obj, value):
632 """Validates that the value is a valid object instance."""
628 """Validates that the value is a valid object instance."""
633 try:
629 try:
634 if issubclass(value, self.klass):
630 if issubclass(value, self.klass):
635 return value
631 return value
636 except:
632 except:
637 if (value is None) and (self._allow_none):
633 if (value is None) and (self._allow_none):
638 return value
634 return value
639
635
640 self.error(obj, value)
636 self.error(obj, value)
641
637
642 def info(self):
638 def info(self):
643 """ Returns a description of the trait."""
639 """ Returns a description of the trait."""
644 if isinstance(self.klass, basestring):
640 if isinstance(self.klass, basestring):
645 klass = self.klass
641 klass = self.klass
646 else:
642 else:
647 klass = self.klass.__name__
643 klass = self.klass.__name__
648 result = 'a subclass of ' + klass
644 result = 'a subclass of ' + klass
649 if self._allow_none:
645 if self._allow_none:
650 return result + ' or None'
646 return result + ' or None'
651 return result
647 return result
652
648
653 def instance_init(self, obj):
649 def instance_init(self, obj):
654 self._resolve_classes()
650 self._resolve_classes()
655 super(Type, self).instance_init(obj)
651 super(Type, self).instance_init(obj)
656
652
657 def _resolve_classes(self):
653 def _resolve_classes(self):
658 if isinstance(self.klass, basestring):
654 if isinstance(self.klass, basestring):
659 self.klass = import_item(self.klass)
655 self.klass = import_item(self.klass)
660 if isinstance(self.default_value, basestring):
656 if isinstance(self.default_value, basestring):
661 self.default_value = import_item(self.default_value)
657 self.default_value = import_item(self.default_value)
662
658
663 def get_default_value(self):
659 def get_default_value(self):
664 return self.default_value
660 return self.default_value
665
661
666
662
667 class DefaultValueGenerator(object):
663 class DefaultValueGenerator(object):
668 """A class for generating new default value instances."""
664 """A class for generating new default value instances."""
669
665
670 def __init__(self, *args, **kw):
666 def __init__(self, *args, **kw):
671 self.args = args
667 self.args = args
672 self.kw = kw
668 self.kw = kw
673
669
674 def generate(self, klass):
670 def generate(self, klass):
675 return klass(*self.args, **self.kw)
671 return klass(*self.args, **self.kw)
676
672
677
673
678 class Instance(ClassBasedTraitType):
674 class Instance(ClassBasedTraitType):
679 """A trait whose value must be an instance of a specified class.
675 """A trait whose value must be an instance of a specified class.
680
676
681 The value can also be an instance of a subclass of the specified class.
677 The value can also be an instance of a subclass of the specified class.
682 """
678 """
683
679
684 def __init__(self, klass=None, args=None, kw=None,
680 def __init__(self, klass=None, args=None, kw=None,
685 allow_none=True, **metadata ):
681 allow_none=True, **metadata ):
686 """Construct an Instance trait.
682 """Construct an Instance trait.
687
683
688 This trait allows values that are instances of a particular
684 This trait allows values that are instances of a particular
689 class or its sublclasses. Our implementation is quite different
685 class or its sublclasses. Our implementation is quite different
690 from that of enthough.traits as we don't allow instances to be used
686 from that of enthough.traits as we don't allow instances to be used
691 for klass and we handle the ``args`` and ``kw`` arguments differently.
687 for klass and we handle the ``args`` and ``kw`` arguments differently.
692
688
693 Parameters
689 Parameters
694 ----------
690 ----------
695 klass : class, str
691 klass : class, str
696 The class that forms the basis for the trait. Class names
692 The class that forms the basis for the trait. Class names
697 can also be specified as strings, like 'foo.bar.Bar'.
693 can also be specified as strings, like 'foo.bar.Bar'.
698 args : tuple
694 args : tuple
699 Positional arguments for generating the default value.
695 Positional arguments for generating the default value.
700 kw : dict
696 kw : dict
701 Keyword arguments for generating the default value.
697 Keyword arguments for generating the default value.
702 allow_none : bool
698 allow_none : bool
703 Indicates whether None is allowed as a value.
699 Indicates whether None is allowed as a value.
704
700
705 Default Value
701 Default Value
706 -------------
702 -------------
707 If both ``args`` and ``kw`` are None, then the default value is None.
703 If both ``args`` and ``kw`` are None, then the default value is None.
708 If ``args`` is a tuple and ``kw`` is a dict, then the default is
704 If ``args`` is a tuple and ``kw`` is a dict, then the default is
709 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
705 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
710 not (but not both), None is replace by ``()`` or ``{}``.
706 not (but not both), None is replace by ``()`` or ``{}``.
711 """
707 """
712
708
713 self._allow_none = allow_none
709 self._allow_none = allow_none
714
710
715 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
711 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
716 raise TraitError('The klass argument must be a class'
712 raise TraitError('The klass argument must be a class'
717 ' you gave: %r' % klass)
713 ' you gave: %r' % klass)
718 self.klass = klass
714 self.klass = klass
719
715
720 # self.klass is a class, so handle default_value
716 # self.klass is a class, so handle default_value
721 if args is None and kw is None:
717 if args is None and kw is None:
722 default_value = None
718 default_value = None
723 else:
719 else:
724 if args is None:
720 if args is None:
725 # kw is not None
721 # kw is not None
726 args = ()
722 args = ()
727 elif kw is None:
723 elif kw is None:
728 # args is not None
724 # args is not None
729 kw = {}
725 kw = {}
730
726
731 if not isinstance(kw, dict):
727 if not isinstance(kw, dict):
732 raise TraitError("The 'kw' argument must be a dict or None.")
728 raise TraitError("The 'kw' argument must be a dict or None.")
733 if not isinstance(args, tuple):
729 if not isinstance(args, tuple):
734 raise TraitError("The 'args' argument must be a tuple or None.")
730 raise TraitError("The 'args' argument must be a tuple or None.")
735
731
736 default_value = DefaultValueGenerator(*args, **kw)
732 default_value = DefaultValueGenerator(*args, **kw)
737
733
738 super(Instance, self).__init__(default_value, **metadata)
734 super(Instance, self).__init__(default_value, **metadata)
739
735
740 def validate(self, obj, value):
736 def validate(self, obj, value):
741 if value is None:
737 if value is None:
742 if self._allow_none:
738 if self._allow_none:
743 return value
739 return value
744 self.error(obj, value)
740 self.error(obj, value)
745
741
746 if isinstance(value, self.klass):
742 if isinstance(value, self.klass):
747 return value
743 return value
748 else:
744 else:
749 self.error(obj, value)
745 self.error(obj, value)
750
746
751 def info(self):
747 def info(self):
752 if isinstance(self.klass, basestring):
748 if isinstance(self.klass, basestring):
753 klass = self.klass
749 klass = self.klass
754 else:
750 else:
755 klass = self.klass.__name__
751 klass = self.klass.__name__
756 result = class_of(klass)
752 result = class_of(klass)
757 if self._allow_none:
753 if self._allow_none:
758 return result + ' or None'
754 return result + ' or None'
759
755
760 return result
756 return result
761
757
762 def instance_init(self, obj):
758 def instance_init(self, obj):
763 self._resolve_classes()
759 self._resolve_classes()
764 super(Instance, self).instance_init(obj)
760 super(Instance, self).instance_init(obj)
765
761
766 def _resolve_classes(self):
762 def _resolve_classes(self):
767 if isinstance(self.klass, basestring):
763 if isinstance(self.klass, basestring):
768 self.klass = import_item(self.klass)
764 self.klass = import_item(self.klass)
769
765
770 def get_default_value(self):
766 def get_default_value(self):
771 """Instantiate a default value instance.
767 """Instantiate a default value instance.
772
768
773 This is called when the containing HasTraits classes'
769 This is called when the containing HasTraits classes'
774 :meth:`__new__` method is called to ensure that a unique instance
770 :meth:`__new__` method is called to ensure that a unique instance
775 is created for each HasTraits instance.
771 is created for each HasTraits instance.
776 """
772 """
777 dv = self.default_value
773 dv = self.default_value
778 if isinstance(dv, DefaultValueGenerator):
774 if isinstance(dv, DefaultValueGenerator):
779 return dv.generate(self.klass)
775 return dv.generate(self.klass)
780 else:
776 else:
781 return dv
777 return dv
782
778
783
779
784 class This(ClassBasedTraitType):
780 class This(ClassBasedTraitType):
785 """A trait for instances of the class containing this trait.
781 """A trait for instances of the class containing this trait.
786
782
787 Because how how and when class bodies are executed, the ``This``
783 Because how how and when class bodies are executed, the ``This``
788 trait can only have a default value of None. This, and because we
784 trait can only have a default value of None. This, and because we
789 always validate default values, ``allow_none`` is *always* true.
785 always validate default values, ``allow_none`` is *always* true.
790 """
786 """
791
787
792 info_text = 'an instance of the same type as the receiver or None'
788 info_text = 'an instance of the same type as the receiver or None'
793
789
794 def __init__(self, **metadata):
790 def __init__(self, **metadata):
795 super(This, self).__init__(None, **metadata)
791 super(This, self).__init__(None, **metadata)
796
792
797 def validate(self, obj, value):
793 def validate(self, obj, value):
798 # What if value is a superclass of obj.__class__? This is
794 # What if value is a superclass of obj.__class__? This is
799 # complicated if it was the superclass that defined the This
795 # complicated if it was the superclass that defined the This
800 # trait.
796 # trait.
801 if isinstance(value, self.this_class) or (value is None):
797 if isinstance(value, self.this_class) or (value is None):
802 return value
798 return value
803 else:
799 else:
804 self.error(obj, value)
800 self.error(obj, value)
805
801
806
802
807 #-----------------------------------------------------------------------------
803 #-----------------------------------------------------------------------------
808 # Basic TraitTypes implementations/subclasses
804 # Basic TraitTypes implementations/subclasses
809 #-----------------------------------------------------------------------------
805 #-----------------------------------------------------------------------------
810
806
811
807
812 class Any(TraitType):
808 class Any(TraitType):
813 default_value = None
809 default_value = None
814 info_text = 'any value'
810 info_text = 'any value'
815
811
816
812
817 class Int(TraitType):
813 class Int(TraitType):
818 """A integer trait."""
814 """A integer trait."""
819
815
820 default_value = 0
816 default_value = 0
821 info_text = 'an integer'
817 info_text = 'an integer'
822
818
823 def validate(self, obj, value):
819 def validate(self, obj, value):
824 if isinstance(value, int):
820 if isinstance(value, int):
825 return value
821 return value
826 self.error(obj, value)
822 self.error(obj, value)
827
823
828 class CInt(Int):
824 class CInt(Int):
829 """A casting version of the int trait."""
825 """A casting version of the int trait."""
830
826
831 def validate(self, obj, value):
827 def validate(self, obj, value):
832 try:
828 try:
833 return int(value)
829 return int(value)
834 except:
830 except:
835 self.error(obj, value)
831 self.error(obj, value)
836
832
837
833
838 class Long(TraitType):
834 class Long(TraitType):
839 """A long integer trait."""
835 """A long integer trait."""
840
836
841 default_value = 0L
837 default_value = 0L
842 info_text = 'a long'
838 info_text = 'a long'
843
839
844 def validate(self, obj, value):
840 def validate(self, obj, value):
845 if isinstance(value, long):
841 if isinstance(value, long):
846 return value
842 return value
847 if isinstance(value, int):
843 if isinstance(value, int):
848 return long(value)
844 return long(value)
849 self.error(obj, value)
845 self.error(obj, value)
850
846
851
847
852 class CLong(Long):
848 class CLong(Long):
853 """A casting version of the long integer trait."""
849 """A casting version of the long integer trait."""
854
850
855 def validate(self, obj, value):
851 def validate(self, obj, value):
856 try:
852 try:
857 return long(value)
853 return long(value)
858 except:
854 except:
859 self.error(obj, value)
855 self.error(obj, value)
860
856
861
857
862 class Float(TraitType):
858 class Float(TraitType):
863 """A float trait."""
859 """A float trait."""
864
860
865 default_value = 0.0
861 default_value = 0.0
866 info_text = 'a float'
862 info_text = 'a float'
867
863
868 def validate(self, obj, value):
864 def validate(self, obj, value):
869 if isinstance(value, float):
865 if isinstance(value, float):
870 return value
866 return value
871 if isinstance(value, int):
867 if isinstance(value, int):
872 return float(value)
868 return float(value)
873 self.error(obj, value)
869 self.error(obj, value)
874
870
875
871
876 class CFloat(Float):
872 class CFloat(Float):
877 """A casting version of the float trait."""
873 """A casting version of the float trait."""
878
874
879 def validate(self, obj, value):
875 def validate(self, obj, value):
880 try:
876 try:
881 return float(value)
877 return float(value)
882 except:
878 except:
883 self.error(obj, value)
879 self.error(obj, value)
884
880
885 class Complex(TraitType):
881 class Complex(TraitType):
886 """A trait for complex numbers."""
882 """A trait for complex numbers."""
887
883
888 default_value = 0.0 + 0.0j
884 default_value = 0.0 + 0.0j
889 info_text = 'a complex number'
885 info_text = 'a complex number'
890
886
891 def validate(self, obj, value):
887 def validate(self, obj, value):
892 if isinstance(value, complex):
888 if isinstance(value, complex):
893 return value
889 return value
894 if isinstance(value, (float, int)):
890 if isinstance(value, (float, int)):
895 return complex(value)
891 return complex(value)
896 self.error(obj, value)
892 self.error(obj, value)
897
893
898
894
899 class CComplex(Complex):
895 class CComplex(Complex):
900 """A casting version of the complex number trait."""
896 """A casting version of the complex number trait."""
901
897
902 def validate (self, obj, value):
898 def validate (self, obj, value):
903 try:
899 try:
904 return complex(value)
900 return complex(value)
905 except:
901 except:
906 self.error(obj, value)
902 self.error(obj, value)
907
903
908
904
909 class Str(TraitType):
905 class Str(TraitType):
910 """A trait for strings."""
906 """A trait for strings."""
911
907
912 default_value = ''
908 default_value = ''
913 info_text = 'a string'
909 info_text = 'a string'
914
910
915 def validate(self, obj, value):
911 def validate(self, obj, value):
916 if isinstance(value, str):
912 if isinstance(value, str):
917 return value
913 return value
918 self.error(obj, value)
914 self.error(obj, value)
919
915
920
916
921 class CStr(Str):
917 class CStr(Str):
922 """A casting version of the string trait."""
918 """A casting version of the string trait."""
923
919
924 def validate(self, obj, value):
920 def validate(self, obj, value):
925 try:
921 try:
926 return str(value)
922 return str(value)
927 except:
923 except:
928 try:
924 try:
929 return unicode(value)
925 return unicode(value)
930 except:
926 except:
931 self.error(obj, value)
927 self.error(obj, value)
932
928
933
929
934 class Unicode(TraitType):
930 class Unicode(TraitType):
935 """A trait for unicode strings."""
931 """A trait for unicode strings."""
936
932
937 default_value = u''
933 default_value = u''
938 info_text = 'a unicode string'
934 info_text = 'a unicode string'
939
935
940 def validate(self, obj, value):
936 def validate(self, obj, value):
941 if isinstance(value, unicode):
937 if isinstance(value, unicode):
942 return value
938 return value
943 if isinstance(value, str):
939 if isinstance(value, str):
944 return unicode(value)
940 return unicode(value)
945 self.error(obj, value)
941 self.error(obj, value)
946
942
947
943
948 class CUnicode(Unicode):
944 class CUnicode(Unicode):
949 """A casting version of the unicode trait."""
945 """A casting version of the unicode trait."""
950
946
951 def validate(self, obj, value):
947 def validate(self, obj, value):
952 try:
948 try:
953 return unicode(value)
949 return unicode(value)
954 except:
950 except:
955 self.error(obj, value)
951 self.error(obj, value)
956
952
957
953
958 class Bool(TraitType):
954 class Bool(TraitType):
959 """A boolean (True, False) trait."""
955 """A boolean (True, False) trait."""
960
956
961 default_value = False
957 default_value = False
962 info_text = 'a boolean'
958 info_text = 'a boolean'
963
959
964 def validate(self, obj, value):
960 def validate(self, obj, value):
965 if isinstance(value, bool):
961 if isinstance(value, bool):
966 return value
962 return value
967 self.error(obj, value)
963 self.error(obj, value)
968
964
969
965
970 class CBool(Bool):
966 class CBool(Bool):
971 """A casting version of the boolean trait."""
967 """A casting version of the boolean trait."""
972
968
973 def validate(self, obj, value):
969 def validate(self, obj, value):
974 try:
970 try:
975 return bool(value)
971 return bool(value)
976 except:
972 except:
977 self.error(obj, value)
973 self.error(obj, value)
978
974
979
975
980 class Enum(TraitType):
976 class Enum(TraitType):
981 """An enum that whose value must be in a given sequence."""
977 """An enum that whose value must be in a given sequence."""
982
978
983 def __init__(self, values, default_value=None, allow_none=True, **metadata):
979 def __init__(self, values, default_value=None, allow_none=True, **metadata):
984 self.values = values
980 self.values = values
985 self._allow_none = allow_none
981 self._allow_none = allow_none
986 super(Enum, self).__init__(default_value, **metadata)
982 super(Enum, self).__init__(default_value, **metadata)
987
983
988 def validate(self, obj, value):
984 def validate(self, obj, value):
989 if value is None:
985 if value is None:
990 if self._allow_none:
986 if self._allow_none:
991 return value
987 return value
992
988
993 if value in self.values:
989 if value in self.values:
994 return value
990 return value
995 self.error(obj, value)
991 self.error(obj, value)
996
992
997 def info(self):
993 def info(self):
998 """ Returns a description of the trait."""
994 """ Returns a description of the trait."""
999 result = 'any of ' + repr(self.values)
995 result = 'any of ' + repr(self.values)
1000 if self._allow_none:
996 if self._allow_none:
1001 return result + ' or None'
997 return result + ' or None'
1002 return result
998 return result
1003
999
1004 class CaselessStrEnum(Enum):
1000 class CaselessStrEnum(Enum):
1005 """An enum of strings that are caseless in validate."""
1001 """An enum of strings that are caseless in validate."""
1006
1002
1007 def validate(self, obj, value):
1003 def validate(self, obj, value):
1008 if value is None:
1004 if value is None:
1009 if self._allow_none:
1005 if self._allow_none:
1010 return value
1006 return value
1011
1007
1012 if not isinstance(value, str):
1008 if not isinstance(value, str):
1013 self.error(obj, value)
1009 self.error(obj, value)
1014
1010
1015 for v in self.values:
1011 for v in self.values:
1016 if v.lower() == value.lower():
1012 if v.lower() == value.lower():
1017 return v
1013 return v
1018 self.error(obj, value)
1014 self.error(obj, value)
1019
1015
1020
1016
1021 class List(Instance):
1017 class List(Instance):
1022 """An instance of a Python list."""
1018 """An instance of a Python list."""
1023
1019
1024 def __init__(self, default_value=None, allow_none=True, **metadata):
1020 def __init__(self, default_value=None, allow_none=True, **metadata):
1025 """Create a list trait type from a list or tuple.
1021 """Create a list trait type from a list or tuple.
1026
1022
1027 The default value is created by doing ``list(default_value)``,
1023 The default value is created by doing ``list(default_value)``,
1028 which creates a copy of the ``default_value``.
1024 which creates a copy of the ``default_value``.
1029 """
1025 """
1030 if default_value is None:
1026 if default_value is None:
1031 args = ((),)
1027 args = ((),)
1032 elif isinstance(default_value, SequenceTypes):
1028 elif isinstance(default_value, SequenceTypes):
1033 args = (default_value,)
1029 args = (default_value,)
1034 else:
1030 else:
1035 raise TypeError('default value of List was %s' % default_value)
1031 raise TypeError('default value of List was %s' % default_value)
1036
1032
1037 super(List,self).__init__(klass=list, args=args,
1033 super(List,self).__init__(klass=list, args=args,
1038 allow_none=allow_none, **metadata)
1034 allow_none=allow_none, **metadata)
1039
1035
1040
1036
1041 class Dict(Instance):
1037 class Dict(Instance):
1042 """An instance of a Python dict."""
1038 """An instance of a Python dict."""
1043
1039
1044 def __init__(self, default_value=None, allow_none=True, **metadata):
1040 def __init__(self, default_value=None, allow_none=True, **metadata):
1045 """Create a dict trait type from a dict.
1041 """Create a dict trait type from a dict.
1046
1042
1047 The default value is created by doing ``dict(default_value)``,
1043 The default value is created by doing ``dict(default_value)``,
1048 which creates a copy of the ``default_value``.
1044 which creates a copy of the ``default_value``.
1049 """
1045 """
1050 if default_value is None:
1046 if default_value is None:
1051 args = ((),)
1047 args = ((),)
1052 elif isinstance(default_value, dict):
1048 elif isinstance(default_value, dict):
1053 args = (default_value,)
1049 args = (default_value,)
1054 elif isinstance(default_value, SequenceTypes):
1050 elif isinstance(default_value, SequenceTypes):
1055 args = (default_value,)
1051 args = (default_value,)
1056 else:
1052 else:
1057 raise TypeError('default value of Dict was %s' % default_value)
1053 raise TypeError('default value of Dict was %s' % default_value)
1058
1054
1059 super(Dict,self).__init__(klass=dict, args=args,
1055 super(Dict,self).__init__(klass=dict, args=args,
1060 allow_none=allow_none, **metadata)
1056 allow_none=allow_none, **metadata)
1061
1057
1062
1058
1063 class TCPAddress(TraitType):
1059 class TCPAddress(TraitType):
1064 """A trait for an (ip, port) tuple.
1060 """A trait for an (ip, port) tuple.
1065
1061
1066 This allows for both IPv4 IP addresses as well as hostnames.
1062 This allows for both IPv4 IP addresses as well as hostnames.
1067 """
1063 """
1068
1064
1069 default_value = ('127.0.0.1', 0)
1065 default_value = ('127.0.0.1', 0)
1070 info_text = 'an (ip, port) tuple'
1066 info_text = 'an (ip, port) tuple'
1071
1067
1072 def validate(self, obj, value):
1068 def validate(self, obj, value):
1073 if isinstance(value, tuple):
1069 if isinstance(value, tuple):
1074 if len(value) == 2:
1070 if len(value) == 2:
1075 if isinstance(value[0], basestring) and isinstance(value[1], int):
1071 if isinstance(value[0], basestring) and isinstance(value[1], int):
1076 port = value[1]
1072 port = value[1]
1077 if port >= 0 and port <= 65535:
1073 if port >= 0 and port <= 65535:
1078 return value
1074 return value
1079 self.error(obj, value)
1075 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now