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