##// END OF EJS Templates
A few more small traitlets features.
Brian Granger -
Show More
@@ -1,558 +1,567 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Tests for IPython.utils.traitlets.
4 Tests for IPython.utils.traitlets.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
10 and is licensed under the BSD license. Also, many of the ideas also come
10 and is licensed under the BSD license. Also, many of the ideas also come
11 from enthought.traits even though our implementation is very different.
11 from enthought.traits even though our implementation is very different.
12 """
12 """
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2008-2009 The IPython Development Team
15 # Copyright (C) 2008-2009 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 import sys
25 import sys
26 import os
26 import os
27
27
28
28
29 from unittest import TestCase
29 from unittest import TestCase
30
30
31 from IPython.utils.traitlets import (
31 from IPython.utils.traitlets import (
32 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
32 HasTraitlets, MetaHasTraitlets, TraitletType, Any,
33 Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError,
33 Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError,
34 Undefined, Type, Instance
34 Undefined, Type, Instance
35 )
35 )
36
36
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Helper classes for testing
39 # Helper classes for testing
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42
42
43 class HasTraitletsStub(object):
43 class HasTraitletsStub(object):
44
44
45 def __init__(self):
45 def __init__(self):
46 self._traitlet_values = {}
46 self._traitlet_values = {}
47 self._traitlet_notifiers = {}
47 self._traitlet_notifiers = {}
48
48
49 def _notify(self, name, old, new):
49 def _notify_traitlet(self, name, old, new):
50 self._notify_name = name
50 self._notify_name = name
51 self._notify_old = old
51 self._notify_old = old
52 self._notify_new = new
52 self._notify_new = new
53
53
54
54
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56 # Test classes
56 # Test classes
57 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
58
58
59
59
60 class TestTraitletType(TestCase):
60 class TestTraitletType(TestCase):
61
61
62 name = 'a'
62 name = 'a'
63
63
64 def setUp(self):
64 def setUp(self):
65 self.tt = TraitletType()
65 self.tt = TraitletType()
66 self.tt.name = self.name
66 self.tt.name = self.name
67 self.hast = HasTraitletsStub()
67 self.hast = HasTraitletsStub()
68
68
69 def test_get_undefined(self):
69 def test_get_undefined(self):
70 value = self.tt.__get__(self.hast)
70 value = self.tt.__get__(self.hast)
71 self.assertEquals(value, Undefined)
71 self.assertEquals(value, Undefined)
72
72
73 def test_set(self):
73 def test_set(self):
74 self.tt.__set__(self.hast, 10)
74 self.tt.__set__(self.hast, 10)
75 self.assertEquals(self.hast._traitlet_values[self.name],10)
75 self.assertEquals(self.hast._traitlet_values[self.name],10)
76 self.assertEquals(self.hast._notify_name,self.name)
76 self.assertEquals(self.hast._notify_name,self.name)
77 self.assertEquals(self.hast._notify_old,Undefined)
77 self.assertEquals(self.hast._notify_old,Undefined)
78 self.assertEquals(self.hast._notify_new,10)
78 self.assertEquals(self.hast._notify_new,10)
79
79
80 def test_validate(self):
80 def test_validate(self):
81 class MyTT(TraitletType):
81 class MyTT(TraitletType):
82 def validate(self, inst, value):
82 def validate(self, inst, value):
83 return -1
83 return -1
84 tt = MyTT()
84 tt = MyTT()
85 tt.name = self.name
85 tt.name = self.name
86 tt.__set__(self.hast, 10)
86 tt.__set__(self.hast, 10)
87 self.assertEquals(tt.__get__(self.hast),-1)
87 self.assertEquals(tt.__get__(self.hast),-1)
88
88
89 def test_default_validate(self):
89 def test_default_validate(self):
90 class MyIntTT(TraitletType):
90 class MyIntTT(TraitletType):
91 def validate(self, obj, value):
91 def validate(self, obj, value):
92 if isinstance(value, int):
92 if isinstance(value, int):
93 return value
93 return value
94 self.error(obj, value)
94 self.error(obj, value)
95 tt = MyIntTT(10)
95 tt = MyIntTT(10)
96 tt.name = 'a'
96 tt.name = 'a'
97 self.assertEquals(tt.__get__(self.hast), 10)
97 self.assertEquals(tt.__get__(self.hast), 10)
98 tt = MyIntTT('bad default')
98 tt = MyIntTT('bad default')
99 tt.name = 'b' # different name from 'a' as we want an unset dv
99 tt.name = 'b' # different name from 'a' as we want an unset dv
100 self.assertRaises(TraitletError, tt.__get__, self.hast)
100 self.assertRaises(TraitletError, tt.__get__, self.hast)
101
101
102
102
103 def test_is_valid_for(self):
103 def test_is_valid_for(self):
104 class MyTT(TraitletType):
104 class MyTT(TraitletType):
105 def is_valid_for(self, value):
105 def is_valid_for(self, value):
106 return True
106 return True
107 tt = MyTT()
107 tt = MyTT()
108 tt.name = self.name
108 tt.name = self.name
109 tt.__set__(self.hast, 10)
109 tt.__set__(self.hast, 10)
110 self.assertEquals(tt.__get__(self.hast), 10)
110 self.assertEquals(tt.__get__(self.hast), 10)
111
111
112 def test_value_for(self):
112 def test_value_for(self):
113 class MyTT(TraitletType):
113 class MyTT(TraitletType):
114 def value_for(self, value):
114 def value_for(self, value):
115 return 20
115 return 20
116 tt = MyTT()
116 tt = MyTT()
117 tt.name = self.name
117 tt.name = self.name
118 tt.__set__(self.hast, 10)
118 tt.__set__(self.hast, 10)
119 self.assertEquals(tt.__get__(self.hast), 20)
119 self.assertEquals(tt.__get__(self.hast), 20)
120
120
121 def test_info(self):
121 def test_info(self):
122 self.assertEquals(self.tt.info(), 'any value')
122 self.assertEquals(self.tt.info(), 'any value')
123
123
124 def test_error(self):
124 def test_error(self):
125 self.assertRaises(TraitletError, self.tt.error, self.hast, 10)
125 self.assertRaises(TraitletError, self.tt.error, self.hast, 10)
126
126
127
127
128 class TestHasTraitletsMeta(TestCase):
128 class TestHasTraitletsMeta(TestCase):
129
129
130 def test_metaclass(self):
130 def test_metaclass(self):
131 self.assertEquals(type(HasTraitlets), MetaHasTraitlets)
131 self.assertEquals(type(HasTraitlets), MetaHasTraitlets)
132
132
133 class A(HasTraitlets):
133 class A(HasTraitlets):
134 a = Int
134 a = Int
135
135
136 a = A()
136 a = A()
137 self.assertEquals(type(a.__class__), MetaHasTraitlets)
137 self.assertEquals(type(a.__class__), MetaHasTraitlets)
138 self.assertEquals(a.a,0)
138 self.assertEquals(a.a,0)
139 a.a = 10
139 a.a = 10
140 self.assertEquals(a.a,10)
140 self.assertEquals(a.a,10)
141
141
142 class B(HasTraitlets):
142 class B(HasTraitlets):
143 b = Int()
143 b = Int()
144
144
145 b = B()
145 b = B()
146 self.assertEquals(b.b,0)
146 self.assertEquals(b.b,0)
147 b.b = 10
147 b.b = 10
148 self.assertEquals(b.b,10)
148 self.assertEquals(b.b,10)
149
149
150 class C(HasTraitlets):
150 class C(HasTraitlets):
151 c = Int(30)
151 c = Int(30)
152
152
153 c = C()
153 c = C()
154 self.assertEquals(c.c,30)
154 self.assertEquals(c.c,30)
155 c.c = 10
155 c.c = 10
156 self.assertEquals(c.c,10)
156 self.assertEquals(c.c,10)
157
157
158
158
159 class TestHasTraitletsNotify(TestCase):
159 class TestHasTraitletsNotify(TestCase):
160
160
161 def setUp(self):
161 def setUp(self):
162 self._notify1 = []
162 self._notify1 = []
163 self._notify2 = []
163 self._notify2 = []
164
164
165 def notify1(self, name, old, new):
165 def notify1(self, name, old, new):
166 self._notify1.append((name, old, new))
166 self._notify1.append((name, old, new))
167
167
168 def notify2(self, name, old, new):
168 def notify2(self, name, old, new):
169 self._notify2.append((name, old, new))
169 self._notify2.append((name, old, new))
170
170
171 def test_notify_all(self):
171 def test_notify_all(self):
172
172
173 class A(HasTraitlets):
173 class A(HasTraitlets):
174 a = Int
174 a = Int
175 b = Float
175 b = Float
176
176
177 a = A()
177 a = A()
178 a.on_traitlet_change(self.notify1)
178 a.on_traitlet_change(self.notify1)
179 a.a = 0
179 a.a = 0
180 self.assertEquals(len(self._notify1),0)
180 self.assertEquals(len(self._notify1),0)
181 a.b = 0.0
181 a.b = 0.0
182 self.assertEquals(len(self._notify1),0)
182 self.assertEquals(len(self._notify1),0)
183 a.a = 10
183 a.a = 10
184 self.assert_(('a',0,10) in self._notify1)
184 self.assert_(('a',0,10) in self._notify1)
185 a.b = 10.0
185 a.b = 10.0
186 self.assert_(('b',0.0,10.0) in self._notify1)
186 self.assert_(('b',0.0,10.0) in self._notify1)
187 self.assertRaises(TraitletError,setattr,a,'a','bad string')
187 self.assertRaises(TraitletError,setattr,a,'a','bad string')
188 self.assertRaises(TraitletError,setattr,a,'b','bad string')
188 self.assertRaises(TraitletError,setattr,a,'b','bad string')
189 self._notify1 = []
189 self._notify1 = []
190 a.on_traitlet_change(self.notify1,remove=True)
190 a.on_traitlet_change(self.notify1,remove=True)
191 a.a = 20
191 a.a = 20
192 a.b = 20.0
192 a.b = 20.0
193 self.assertEquals(len(self._notify1),0)
193 self.assertEquals(len(self._notify1),0)
194
194
195 def test_notify_one(self):
195 def test_notify_one(self):
196
196
197 class A(HasTraitlets):
197 class A(HasTraitlets):
198 a = Int
198 a = Int
199 b = Float
199 b = Float
200
200
201 a = A()
201 a = A()
202 a.on_traitlet_change(self.notify1, 'a')
202 a.on_traitlet_change(self.notify1, 'a')
203 a.a = 0
203 a.a = 0
204 self.assertEquals(len(self._notify1),0)
204 self.assertEquals(len(self._notify1),0)
205 a.a = 10
205 a.a = 10
206 self.assert_(('a',0,10) in self._notify1)
206 self.assert_(('a',0,10) in self._notify1)
207 self.assertRaises(TraitletError,setattr,a,'a','bad string')
207 self.assertRaises(TraitletError,setattr,a,'a','bad string')
208
208
209 def test_subclass(self):
209 def test_subclass(self):
210
210
211 class A(HasTraitlets):
211 class A(HasTraitlets):
212 a = Int
212 a = Int
213
213
214 class B(A):
214 class B(A):
215 b = Float
215 b = Float
216
216
217 b = B()
217 b = B()
218 self.assertEquals(b.a,0)
218 self.assertEquals(b.a,0)
219 self.assertEquals(b.b,0.0)
219 self.assertEquals(b.b,0.0)
220 b.a = 100
220 b.a = 100
221 b.b = 100.0
221 b.b = 100.0
222 self.assertEquals(b.a,100)
222 self.assertEquals(b.a,100)
223 self.assertEquals(b.b,100.0)
223 self.assertEquals(b.b,100.0)
224
224
225 def test_notify_subclass(self):
225 def test_notify_subclass(self):
226
226
227 class A(HasTraitlets):
227 class A(HasTraitlets):
228 a = Int
228 a = Int
229
229
230 class B(A):
230 class B(A):
231 b = Float
231 b = Float
232
232
233 b = B()
233 b = B()
234 b.on_traitlet_change(self.notify1, 'a')
234 b.on_traitlet_change(self.notify1, 'a')
235 b.on_traitlet_change(self.notify2, 'b')
235 b.on_traitlet_change(self.notify2, 'b')
236 b.a = 0
236 b.a = 0
237 b.b = 0.0
237 b.b = 0.0
238 self.assertEquals(len(self._notify1),0)
238 self.assertEquals(len(self._notify1),0)
239 self.assertEquals(len(self._notify2),0)
239 self.assertEquals(len(self._notify2),0)
240 b.a = 10
240 b.a = 10
241 b.b = 10.0
241 b.b = 10.0
242 self.assert_(('a',0,10) in self._notify1)
242 self.assert_(('a',0,10) in self._notify1)
243 self.assert_(('b',0.0,10.0) in self._notify2)
243 self.assert_(('b',0.0,10.0) in self._notify2)
244
244
245 def test_static_notify(self):
245 def test_static_notify(self):
246
246
247 class A(HasTraitlets):
247 class A(HasTraitlets):
248 a = Int
248 a = Int
249 _notify1 = []
249 _notify1 = []
250 def _a_changed(self, name, old, new):
250 def _a_changed(self, name, old, new):
251 self._notify1.append((name, old, new))
251 self._notify1.append((name, old, new))
252
252
253 a = A()
253 a = A()
254 a.a = 0
254 a.a = 0
255 self.assertEquals(len(a._notify1),0)
255 self.assertEquals(len(a._notify1),0)
256 a.a = 10
256 a.a = 10
257 self.assert_(('a',0,10) in a._notify1)
257 self.assert_(('a',0,10) in a._notify1)
258
258
259 class B(A):
259 class B(A):
260 b = Float
260 b = Float
261 _notify2 = []
261 _notify2 = []
262 def _b_changed(self, name, old, new):
262 def _b_changed(self, name, old, new):
263 self._notify2.append((name, old, new))
263 self._notify2.append((name, old, new))
264
264
265 b = B()
265 b = B()
266 b.a = 10
266 b.a = 10
267 b.b = 10.0
267 b.b = 10.0
268 self.assert_(('a',0,10) in b._notify1)
268 self.assert_(('a',0,10) in b._notify1)
269 self.assert_(('b',0.0,10.0) in b._notify2)
269 self.assert_(('b',0.0,10.0) in b._notify2)
270
270
271 def test_notify_args(self):
271 def test_notify_args(self):
272
272
273 def callback0():
273 def callback0():
274 self.cb = ()
274 self.cb = ()
275 def callback1(name):
275 def callback1(name):
276 self.cb = (name,)
276 self.cb = (name,)
277 def callback2(name, new):
277 def callback2(name, new):
278 self.cb = (name, new)
278 self.cb = (name, new)
279 def callback3(name, old, new):
279 def callback3(name, old, new):
280 self.cb = (name, old, new)
280 self.cb = (name, old, new)
281
281
282 class A(HasTraitlets):
282 class A(HasTraitlets):
283 a = Int
283 a = Int
284
284
285 a = A()
285 a = A()
286 a.on_traitlet_change(callback0, 'a')
286 a.on_traitlet_change(callback0, 'a')
287 a.a = 10
287 a.a = 10
288 self.assertEquals(self.cb,())
288 self.assertEquals(self.cb,())
289 a.on_traitlet_change(callback0, 'a', remove=True)
289 a.on_traitlet_change(callback0, 'a', remove=True)
290
290
291 a.on_traitlet_change(callback1, 'a')
291 a.on_traitlet_change(callback1, 'a')
292 a.a = 100
292 a.a = 100
293 self.assertEquals(self.cb,('a',))
293 self.assertEquals(self.cb,('a',))
294 a.on_traitlet_change(callback1, 'a', remove=True)
294 a.on_traitlet_change(callback1, 'a', remove=True)
295
295
296 a.on_traitlet_change(callback2, 'a')
296 a.on_traitlet_change(callback2, 'a')
297 a.a = 1000
297 a.a = 1000
298 self.assertEquals(self.cb,('a',1000))
298 self.assertEquals(self.cb,('a',1000))
299 a.on_traitlet_change(callback2, 'a', remove=True)
299 a.on_traitlet_change(callback2, 'a', remove=True)
300
300
301 a.on_traitlet_change(callback3, 'a')
301 a.on_traitlet_change(callback3, 'a')
302 a.a = 10000
302 a.a = 10000
303 self.assertEquals(self.cb,('a',1000,10000))
303 self.assertEquals(self.cb,('a',1000,10000))
304 a.on_traitlet_change(callback3, 'a', remove=True)
304 a.on_traitlet_change(callback3, 'a', remove=True)
305
305
306 self.assertEquals(len(a._traitlet_notifiers['a']),0)
306 self.assertEquals(len(a._traitlet_notifiers['a']),0)
307
307
308
308
309 class TestAddTraitlet(TestCase):
309 class TestAddTraitlet(TestCase):
310
310
311 def test_add_float(self):
311 def test_add_float(self):
312
312
313 class A(HasTraitlets):
313 class A(HasTraitlets):
314 a = Int
314 a = Int
315
315
316 a = A()
316 a = A()
317 a.a = 10
317 a.a = 10
318 a._add_class_traitlet('b',Float)
318 a._add_class_traitlet('b',Float)
319 self.assertEquals(a.b,0.0)
319 self.assertEquals(a.b,0.0)
320 a.b = 10.0
320 a.b = 10.0
321 self.assertEquals(a.b,10.0)
321 self.assertEquals(a.b,10.0)
322 self.assertRaises(TraitletError, setattr, a, 'b', 'bad value')
322 self.assertRaises(TraitletError, setattr, a, 'b', 'bad value')
323
323
324 class TestTraitletKeys(TestCase):
325
326 def test_keys(self):
327 class A(HasTraitlets):
328 a = Int
329 b = Float
330 a = A()
331 self.assertEquals(a.traitlet_keys(),['a','b'])
332
324
333
325 #-----------------------------------------------------------------------------
334 #-----------------------------------------------------------------------------
326 # Tests for specific traitlet types
335 # Tests for specific traitlet types
327 #-----------------------------------------------------------------------------
336 #-----------------------------------------------------------------------------
328
337
329
338
330 class TestType(TestCase):
339 class TestType(TestCase):
331
340
332 def test_default(self):
341 def test_default(self):
333
342
334 class B(object): pass
343 class B(object): pass
335 class A(HasTraitlets):
344 class A(HasTraitlets):
336 klass = Type
345 klass = Type
337
346
338 a = A()
347 a = A()
339 self.assertEquals(a.klass, None)
348 self.assertEquals(a.klass, None)
340 a.klass = B
349 a.klass = B
341 self.assertEquals(a.klass, B)
350 self.assertEquals(a.klass, B)
342 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
351 self.assertRaises(TraitletError, setattr, a, 'klass', 10)
343
352
344 def test_value(self):
353 def test_value(self):
345
354
346 class B(object): pass
355 class B(object): pass
347 class C(object): pass
356 class C(object): pass
348 class A(HasTraitlets):
357 class A(HasTraitlets):
349 klass = Type(B)
358 klass = Type(B)
350
359
351 a = A()
360 a = A()
352 self.assertEquals(a.klass, B)
361 self.assertEquals(a.klass, B)
353 self.assertRaises(TraitletError, setattr, a, 'klass', C)
362 self.assertRaises(TraitletError, setattr, a, 'klass', C)
354 self.assertRaises(TraitletError, setattr, a, 'klass', object)
363 self.assertRaises(TraitletError, setattr, a, 'klass', object)
355 a.klass = B
364 a.klass = B
356
365
357 def test_allow_none(self):
366 def test_allow_none(self):
358
367
359 class B(object): pass
368 class B(object): pass
360 class C(B): pass
369 class C(B): pass
361 class A(HasTraitlets):
370 class A(HasTraitlets):
362 klass = Type(B, allow_none=False)
371 klass = Type(B, allow_none=False)
363
372
364 a = A()
373 a = A()
365 self.assertEquals(a.klass, B)
374 self.assertEquals(a.klass, B)
366 self.assertRaises(TraitletError, setattr, a, 'klass', None)
375 self.assertRaises(TraitletError, setattr, a, 'klass', None)
367 a.klass = C
376 a.klass = C
368 self.assertEquals(a.klass, C)
377 self.assertEquals(a.klass, C)
369
378
370
379
371 class TestInstance(TestCase):
380 class TestInstance(TestCase):
372
381
373 def test_basic(self):
382 def test_basic(self):
374 class Foo(object): pass
383 class Foo(object): pass
375 class Bar(Foo): pass
384 class Bar(Foo): pass
376 class Bah(object): pass
385 class Bah(object): pass
377
386
378 class A(HasTraitlets):
387 class A(HasTraitlets):
379 inst = Instance(Foo)
388 inst = Instance(Foo)
380
389
381 a = A()
390 a = A()
382 self.assert_(isinstance(a.inst, Foo))
391 self.assert_(isinstance(a.inst, Foo))
383 a.inst = Foo()
392 a.inst = Foo()
384 self.assert_(isinstance(a.inst, Foo))
393 self.assert_(isinstance(a.inst, Foo))
385 a.inst = Bar()
394 a.inst = Bar()
386 self.assert_(isinstance(a.inst, Foo))
395 self.assert_(isinstance(a.inst, Foo))
387 self.assertRaises(TraitletError, setattr, a, 'inst', Foo)
396 self.assertRaises(TraitletError, setattr, a, 'inst', Foo)
388 self.assertRaises(TraitletError, setattr, a, 'inst', Bar)
397 self.assertRaises(TraitletError, setattr, a, 'inst', Bar)
389 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
398 self.assertRaises(TraitletError, setattr, a, 'inst', Bah())
390
399
391 def test_unique_default_value(self):
400 def test_unique_default_value(self):
392 class Foo(object): pass
401 class Foo(object): pass
393 class A(HasTraitlets):
402 class A(HasTraitlets):
394 inst = Instance(Foo)
403 inst = Instance(Foo)
395
404
396 a = A()
405 a = A()
397 b = A()
406 b = A()
398 self.assert_(a.inst is not b.inst)
407 self.assert_(a.inst is not b.inst)
399
408
400 def test_args_kw(self):
409 def test_args_kw(self):
401 class Foo(object):
410 class Foo(object):
402 def __init__(self, c): self.c = c
411 def __init__(self, c): self.c = c
403
412
404 class A(HasTraitlets):
413 class A(HasTraitlets):
405 inst = Instance(Foo, args=(10,))
414 inst = Instance(Foo, args=(10,))
406
415
407 a = A()
416 a = A()
408 self.assertEquals(a.inst.c, 10)
417 self.assertEquals(a.inst.c, 10)
409
418
410 class Bar(object):
419 class Bar(object):
411 def __init__(self, c, d):
420 def __init__(self, c, d):
412 self.c = c; self.d = d
421 self.c = c; self.d = d
413
422
414 class B(HasTraitlets):
423 class B(HasTraitlets):
415 inst = Instance(Bar, args=(10,),kw=dict(d=20))
424 inst = Instance(Bar, args=(10,),kw=dict(d=20))
416 b = B()
425 b = B()
417 self.assertEquals(b.inst.c, 10)
426 self.assertEquals(b.inst.c, 10)
418 self.assertEquals(b.inst.d, 20)
427 self.assertEquals(b.inst.d, 20)
419
428
420 def test_instance(self):
429 def test_instance(self):
421 # Does passing an instance yield a default value of None?
430 # Does passing an instance yield a default value of None?
422 class Foo(object): pass
431 class Foo(object): pass
423
432
424 class A(HasTraitlets):
433 class A(HasTraitlets):
425 inst = Instance(Foo())
434 inst = Instance(Foo())
426 a = A()
435 a = A()
427 self.assertEquals(a.inst, None)
436 self.assertEquals(a.inst, None)
428
437
429 class B(HasTraitlets):
438 class B(HasTraitlets):
430 inst = Instance(Foo(), allow_none=False)
439 inst = Instance(Foo(), allow_none=False)
431 b = B()
440 b = B()
432 self.assertRaises(TraitletError, getattr, b, 'inst')
441 self.assertRaises(TraitletError, getattr, b, 'inst')
433
442
434 class TraitletTestBase(TestCase):
443 class TraitletTestBase(TestCase):
435 """A best testing class for basic traitlet types."""
444 """A best testing class for basic traitlet types."""
436
445
437 def assign(self, value):
446 def assign(self, value):
438 self.obj.value = value
447 self.obj.value = value
439
448
440 def coerce(self, value):
449 def coerce(self, value):
441 return value
450 return value
442
451
443 def test_good_values(self):
452 def test_good_values(self):
444 if hasattr(self, '_good_values'):
453 if hasattr(self, '_good_values'):
445 for value in self._good_values:
454 for value in self._good_values:
446 self.assign(value)
455 self.assign(value)
447 self.assertEquals(self.obj.value, self.coerce(value))
456 self.assertEquals(self.obj.value, self.coerce(value))
448
457
449 def test_bad_values(self):
458 def test_bad_values(self):
450 if hasattr(self, '_bad_values'):
459 if hasattr(self, '_bad_values'):
451 for value in self._bad_values:
460 for value in self._bad_values:
452 self.assertRaises(TraitletError, self.assign, value)
461 self.assertRaises(TraitletError, self.assign, value)
453
462
454 def test_default_value(self):
463 def test_default_value(self):
455 if hasattr(self, '_default_value'):
464 if hasattr(self, '_default_value'):
456 self.assertEquals(self._default_value, self.obj.value)
465 self.assertEquals(self._default_value, self.obj.value)
457
466
458
467
459 class AnyTraitlet(HasTraitlets):
468 class AnyTraitlet(HasTraitlets):
460
469
461 value = Any
470 value = Any
462
471
463 class AnyTraitTest(TraitletTestBase):
472 class AnyTraitTest(TraitletTestBase):
464
473
465 obj = AnyTraitlet()
474 obj = AnyTraitlet()
466
475
467 _default_value = None
476 _default_value = None
468 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
477 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
469 _bad_values = []
478 _bad_values = []
470
479
471
480
472 class IntTraitlet(HasTraitlets):
481 class IntTraitlet(HasTraitlets):
473
482
474 value = Int(99)
483 value = Int(99)
475
484
476 class TestInt(TraitletTestBase):
485 class TestInt(TraitletTestBase):
477
486
478 obj = IntTraitlet()
487 obj = IntTraitlet()
479 _default_value = 99
488 _default_value = 99
480 _good_values = [10, -10]
489 _good_values = [10, -10]
481 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
490 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
482 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
491 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
483 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
492 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
484
493
485
494
486 class LongTraitlet(HasTraitlets):
495 class LongTraitlet(HasTraitlets):
487
496
488 value = Long(99L)
497 value = Long(99L)
489
498
490 class TestLong(TraitletTestBase):
499 class TestLong(TraitletTestBase):
491
500
492 obj = LongTraitlet()
501 obj = LongTraitlet()
493
502
494 _default_value = 99L
503 _default_value = 99L
495 _good_values = [10, -10, 10L, -10L]
504 _good_values = [10, -10, 10L, -10L]
496 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
505 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
497 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
506 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
498 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
507 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
499 u'-10.1']
508 u'-10.1']
500
509
501
510
502 class FloatTraitlet(HasTraitlets):
511 class FloatTraitlet(HasTraitlets):
503
512
504 value = Float(99.0)
513 value = Float(99.0)
505
514
506 class TestFloat(TraitletTestBase):
515 class TestFloat(TraitletTestBase):
507
516
508 obj = FloatTraitlet()
517 obj = FloatTraitlet()
509
518
510 _default_value = 99.0
519 _default_value = 99.0
511 _good_values = [10, -10, 10.1, -10.1]
520 _good_values = [10, -10, 10.1, -10.1]
512 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
521 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
513 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
522 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
514 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
523 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
515
524
516
525
517 class ComplexTraitlet(HasTraitlets):
526 class ComplexTraitlet(HasTraitlets):
518
527
519 value = Complex(99.0-99.0j)
528 value = Complex(99.0-99.0j)
520
529
521 class TestComplex(TraitletTestBase):
530 class TestComplex(TraitletTestBase):
522
531
523 obj = ComplexTraitlet()
532 obj = ComplexTraitlet()
524
533
525 _default_value = 99.0-99.0j
534 _default_value = 99.0-99.0j
526 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
535 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
527 10.1j, 10.1+10.1j, 10.1-10.1j]
536 10.1j, 10.1+10.1j, 10.1-10.1j]
528 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
537 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
529
538
530
539
531 class StringTraitlet(HasTraitlets):
540 class StringTraitlet(HasTraitlets):
532
541
533 value = Str('string')
542 value = Str('string')
534
543
535 class TestString(TraitletTestBase):
544 class TestString(TraitletTestBase):
536
545
537 obj = StringTraitlet()
546 obj = StringTraitlet()
538
547
539 _default_value = 'string'
548 _default_value = 'string'
540 _good_values = ['10', '-10', '10L',
549 _good_values = ['10', '-10', '10L',
541 '-10L', '10.1', '-10.1', 'string']
550 '-10L', '10.1', '-10.1', 'string']
542 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
551 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
543 ['ten'],{'ten': 10},(10,), None, u'string']
552 ['ten'],{'ten': 10},(10,), None, u'string']
544
553
545
554
546 class UnicodeTraitlet(HasTraitlets):
555 class UnicodeTraitlet(HasTraitlets):
547
556
548 value = Unicode(u'unicode')
557 value = Unicode(u'unicode')
549
558
550 class TestUnicode(TraitletTestBase):
559 class TestUnicode(TraitletTestBase):
551
560
552 obj = UnicodeTraitlet()
561 obj = UnicodeTraitlet()
553
562
554 _default_value = u'unicode'
563 _default_value = u'unicode'
555 _good_values = ['10', '-10', '10L', '-10L', '10.1',
564 _good_values = ['10', '-10', '10L', '-10L', '10.1',
556 '-10.1', '', u'', 'string', u'string', ]
565 '-10.1', '', u'', 'string', u'string', ]
557 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
566 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
558 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
567 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
@@ -1,828 +1,838 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight Traits like module.
4 A lightweight Traits like module.
5
5
6 This is designed to provide a lightweight, simple, pure Python version of
6 This is designed to provide a lightweight, simple, pure Python version of
7 many of the capabilities of enthought.traits. This includes:
7 many of the capabilities of enthought.traits. This includes:
8
8
9 * Validation
9 * Validation
10 * Type specification with defaults
10 * Type specification with defaults
11 * Static and dynamic notification
11 * Static and dynamic notification
12 * Basic predefined types
12 * Basic predefined types
13 * An API that is similar to enthought.traits
13 * An API that is similar to enthought.traits
14
14
15 We don't support:
15 We don't support:
16
16
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types
19 * A full set of trait types. Most importantly, we don't provide container
20 traitlets (list, dict, tuple) that can trigger notifications if their
21 contents change.
20 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
21
23
24 There are also some important difference in our design:
25
26 * enthought.traits does not validate default values. We do.
27
22 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
23 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,
24 including Jython and IronPython.
30 including Jython and IronPython.
25
31
26 Authors:
32 Authors:
27
33
28 * Brian Granger
34 * Brian Granger
29 * 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
30 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
31 from enthought.traits even though our implementation is very different.
37 from enthought.traits even though our implementation is very different.
32 """
38 """
33
39
34 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
35 # Copyright (C) 2008-2009 The IPython Development Team
41 # Copyright (C) 2008-2009 The IPython Development Team
36 #
42 #
37 # 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
38 # the file COPYING, distributed as part of this software.
44 # the file COPYING, distributed as part of this software.
39 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
40
46
41 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
42 # Imports
48 # Imports
43 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
44
50
45
51
46 import inspect
52 import inspect
47 import sys
53 import sys
48 import types
54 import types
49 from types import InstanceType, ClassType
55 from types import InstanceType, ClassType
50
56
51 ClassTypes = (ClassType, type)
57 ClassTypes = (ClassType, type)
52
58
53 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
54 # Basic classes
60 # Basic classes
55 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
56
62
57
63
58 class NoDefaultSpecified ( object ): pass
64 class NoDefaultSpecified ( object ): pass
59 NoDefaultSpecified = NoDefaultSpecified()
65 NoDefaultSpecified = NoDefaultSpecified()
60
66
61
67
62 class Undefined ( object ): pass
68 class Undefined ( object ): pass
63 Undefined = Undefined()
69 Undefined = Undefined()
64
70
65
71
66 class TraitletError(Exception):
72 class TraitletError(Exception):
67 pass
73 pass
68
74
69
75
70 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
71 # Utilities
77 # Utilities
72 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
73
79
74
80
75 def class_of ( object ):
81 def class_of ( object ):
76 """ Returns a string containing the class name of an object with the
82 """ Returns a string containing the class name of an object with the
77 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
83 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
78 'a PlotValue').
84 'a PlotValue').
79 """
85 """
80 if isinstance( object, basestring ):
86 if isinstance( object, basestring ):
81 return add_article( object )
87 return add_article( object )
82
88
83 return add_article( object.__class__.__name__ )
89 return add_article( object.__class__.__name__ )
84
90
85
91
86 def add_article ( name ):
92 def add_article ( name ):
87 """ Returns a string containing the correct indefinite article ('a' or 'an')
93 """ Returns a string containing the correct indefinite article ('a' or 'an')
88 prefixed to the specified string.
94 prefixed to the specified string.
89 """
95 """
90 if name[:1].lower() in 'aeiou':
96 if name[:1].lower() in 'aeiou':
91 return 'an ' + name
97 return 'an ' + name
92
98
93 return 'a ' + name
99 return 'a ' + name
94
100
95
101
96 def repr_type(obj):
102 def repr_type(obj):
97 """ Return a string representation of a value and its type for readable
103 """ Return a string representation of a value and its type for readable
98 error messages.
104 error messages.
99 """
105 """
100 the_type = type(obj)
106 the_type = type(obj)
101 if the_type is InstanceType:
107 if the_type is InstanceType:
102 # Old-style class.
108 # Old-style class.
103 the_type = obj.__class__
109 the_type = obj.__class__
104 msg = '%r %r' % (obj, the_type)
110 msg = '%r %r' % (obj, the_type)
105 return msg
111 return msg
106
112
107
113
108 def parse_notifier_name(name):
114 def parse_notifier_name(name):
109 """Convert the name argument to a list of names.
115 """Convert the name argument to a list of names.
110
116
111 Examples
117 Examples
112 --------
118 --------
113
119
114 >>> parse_notifier_name('a')
120 >>> parse_notifier_name('a')
115 ['a']
121 ['a']
116 >>> parse_notifier_name(['a','b'])
122 >>> parse_notifier_name(['a','b'])
117 ['a', 'b']
123 ['a', 'b']
118 >>> parse_notifier_name(None)
124 >>> parse_notifier_name(None)
119 ['anytraitlet']
125 ['anytraitlet']
120 """
126 """
121 if isinstance(name, str):
127 if isinstance(name, str):
122 return [name]
128 return [name]
123 elif name is None:
129 elif name is None:
124 return ['anytraitlet']
130 return ['anytraitlet']
125 elif isinstance(name, (list, tuple)):
131 elif isinstance(name, (list, tuple)):
126 for n in name:
132 for n in name:
127 assert isinstance(n, str), "names must be strings"
133 assert isinstance(n, str), "names must be strings"
128 return name
134 return name
129
135
130
136
131 def get_module_name ( level = 2 ):
137 def get_module_name ( level = 2 ):
132 """ Returns the name of the module that the caller's caller is located in.
138 """ Returns the name of the module that the caller's caller is located in.
133 """
139 """
134 return sys._getframe( level ).f_globals.get( '__name__', '__main__' )
140 return sys._getframe( level ).f_globals.get( '__name__', '__main__' )
135
141
136
142
137 #-----------------------------------------------------------------------------
143 #-----------------------------------------------------------------------------
138 # Base TraitletType for all traitlets
144 # Base TraitletType for all traitlets
139 #-----------------------------------------------------------------------------
145 #-----------------------------------------------------------------------------
140
146
141
147
142 class TraitletType(object):
148 class TraitletType(object):
143
149
144 metadata = {}
150 metadata = {}
145 default_value = Undefined
151 default_value = Undefined
146 info_text = 'any value'
152 info_text = 'any value'
147
153
148 def __init__(self, default_value=NoDefaultSpecified, **metadata):
154 def __init__(self, default_value=NoDefaultSpecified, **metadata):
149 """Create a TraitletType.
155 """Create a TraitletType.
150 """
156 """
151 if default_value is not NoDefaultSpecified:
157 if default_value is not NoDefaultSpecified:
152 self.default_value = default_value
158 self.default_value = default_value
153 self.metadata.update(metadata)
159 self.metadata.update(metadata)
154 self.init()
160 self.init()
155
161
156 def init(self):
162 def init(self):
157 pass
163 pass
158
164
159 def get_default_value(self):
165 def get_default_value(self):
160 """Create a new instance of the default value."""
166 """Create a new instance of the default value."""
161 dv = self.default_value
167 dv = self.default_value
162 return dv
168 return dv
163
169
164 def __get__(self, obj, cls=None):
170 def __get__(self, obj, cls=None):
165 """Get the value of the traitlet by self.name for the instance.
171 """Get the value of the traitlet by self.name for the instance.
166
172
167 The creation of default values is deferred until this is called the
173 The creation of default values is deferred until this is called the
168 first time. This is done so instances of the parent HasTraitlets
174 first time. This is done so instances of the parent HasTraitlets
169 will have their own default value instances.
175 will have their own default value instances.
170 """
176 """
171 if obj is None:
177 if obj is None:
172 return self
178 return self
173 else:
179 else:
174 if not obj._traitlet_values.has_key(self.name):
180 if not obj._traitlet_values.has_key(self.name):
175 dv = self.get_default_value()
181 dv = self.get_default_value()
176 self.__set__(obj, dv, first=True)
182 self.__set__(obj, dv, first=True)
177 return dv
183 return dv
178 else:
184 else:
179 return obj._traitlet_values[self.name]
185 return obj._traitlet_values[self.name]
180
186
181 def __set__(self, obj, value, first=False):
187 def __set__(self, obj, value, first=False):
182 new_value = self._validate(obj, value)
188 new_value = self._validate(obj, value)
183 if not first:
189 if not first:
184 old_value = self.__get__(obj)
190 old_value = self.__get__(obj)
185 if old_value != new_value:
191 if old_value != new_value:
186 obj._traitlet_values[self.name] = new_value
192 obj._traitlet_values[self.name] = new_value
187 obj._notify(self.name, old_value, new_value)
193 obj._notify_traitlet(self.name, old_value, new_value)
188 else:
194 else:
189 obj._traitlet_values[self.name] = new_value
195 obj._traitlet_values[self.name] = new_value
190
196
191 def _validate(self, obj, value):
197 def _validate(self, obj, value):
192 if hasattr(self, 'validate'):
198 if hasattr(self, 'validate'):
193 return self.validate(obj, value)
199 return self.validate(obj, value)
194 elif hasattr(self, 'is_valid_for'):
200 elif hasattr(self, 'is_valid_for'):
195 valid = self.is_valid_for(value)
201 valid = self.is_valid_for(value)
196 if valid:
202 if valid:
197 return value
203 return value
198 else:
204 else:
199 raise TraitletError('invalid value for type: %r' % value)
205 raise TraitletError('invalid value for type: %r' % value)
200 elif hasattr(self, 'value_for'):
206 elif hasattr(self, 'value_for'):
201 return self.value_for(value)
207 return self.value_for(value)
202 else:
208 else:
203 return value
209 return value
204
210
205 def info(self):
211 def info(self):
206 return self.info_text
212 return self.info_text
207
213
208 def error(self, obj, value):
214 def error(self, obj, value):
209 if obj is not None:
215 if obj is not None:
210 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
216 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
211 % (self.name, class_of(obj),
217 % (self.name, class_of(obj),
212 self.info(), repr_type(value))
218 self.info(), repr_type(value))
213 else:
219 else:
214 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
220 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
215 % (self.name, self.info(), repr_type(value))
221 % (self.name, self.info(), repr_type(value))
216 raise TraitletError(e)
222 raise TraitletError(e)
217
223
218
224
219 #-----------------------------------------------------------------------------
225 #-----------------------------------------------------------------------------
220 # The HasTraitlets implementation
226 # The HasTraitlets implementation
221 #-----------------------------------------------------------------------------
227 #-----------------------------------------------------------------------------
222
228
223
229
224 class MetaHasTraitlets(type):
230 class MetaHasTraitlets(type):
225 """A metaclass for HasTraitlets.
231 """A metaclass for HasTraitlets.
226
232
227 This metaclass makes sure that any TraitletType class attributes are
233 This metaclass makes sure that any TraitletType class attributes are
228 instantiated and sets their name attribute.
234 instantiated and sets their name attribute.
229 """
235 """
230
236
231 def __new__(mcls, name, bases, classdict):
237 def __new__(mcls, name, bases, classdict):
232 for k,v in classdict.iteritems():
238 for k,v in classdict.iteritems():
233 if isinstance(v, TraitletType):
239 if isinstance(v, TraitletType):
234 v.name = k
240 v.name = k
235 elif inspect.isclass(v):
241 elif inspect.isclass(v):
236 if issubclass(v, TraitletType):
242 if issubclass(v, TraitletType):
237 vinst = v()
243 vinst = v()
238 vinst.name = k
244 vinst.name = k
239 classdict[k] = vinst
245 classdict[k] = vinst
240 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
246 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
241
247
242
248
243 class HasTraitlets(object):
249 class HasTraitlets(object):
244
250
245 __metaclass__ = MetaHasTraitlets
251 __metaclass__ = MetaHasTraitlets
246
252
247 def __init__(self):
253 def __init__(self):
248 self._traitlet_values = {}
254 self._traitlet_values = {}
249 self._traitlet_notifiers = {}
255 self._traitlet_notifiers = {}
250
256
251 def _notify(self, name, old_value, new_value):
257 def _notify_traitlet(self, name, old_value, new_value):
252
258
253 # First dynamic ones
259 # First dynamic ones
254 callables = self._traitlet_notifiers.get(name,[])
260 callables = self._traitlet_notifiers.get(name,[])
255 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
261 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
256 callables.extend(more_callables)
262 callables.extend(more_callables)
257
263
258 # Now static ones
264 # Now static ones
259 try:
265 try:
260 cb = getattr(self, '_%s_changed' % name)
266 cb = getattr(self, '_%s_changed' % name)
261 except:
267 except:
262 pass
268 pass
263 else:
269 else:
264 callables.append(cb)
270 callables.append(cb)
265
271
266 # Call them all now
272 # Call them all now
267 for c in callables:
273 for c in callables:
268 # Traits catches and logs errors here. I allow them to raise
274 # Traits catches and logs errors here. I allow them to raise
269 if callable(c):
275 if callable(c):
270 argspec = inspect.getargspec(c)
276 argspec = inspect.getargspec(c)
271 nargs = len(argspec[0])
277 nargs = len(argspec[0])
272 # Bound methods have an additional 'self' argument
278 # Bound methods have an additional 'self' argument
273 # I don't know how to treat unbound methods, but they
279 # I don't know how to treat unbound methods, but they
274 # can't really be used for callbacks.
280 # can't really be used for callbacks.
275 if isinstance(c, types.MethodType):
281 if isinstance(c, types.MethodType):
276 offset = -1
282 offset = -1
277 else:
283 else:
278 offset = 0
284 offset = 0
279 if nargs + offset == 0:
285 if nargs + offset == 0:
280 c()
286 c()
281 elif nargs + offset == 1:
287 elif nargs + offset == 1:
282 c(name)
288 c(name)
283 elif nargs + offset == 2:
289 elif nargs + offset == 2:
284 c(name, new_value)
290 c(name, new_value)
285 elif nargs + offset == 3:
291 elif nargs + offset == 3:
286 c(name, old_value, new_value)
292 c(name, old_value, new_value)
287 else:
293 else:
288 raise TraitletError('a traitlet changed callback '
294 raise TraitletError('a traitlet changed callback '
289 'must have 0-3 arguments.')
295 'must have 0-3 arguments.')
290 else:
296 else:
291 raise TraitletError('a traitlet changed callback '
297 raise TraitletError('a traitlet changed callback '
292 'must be callable.')
298 'must be callable.')
293
299
294
300
295 def _add_notifiers(self, handler, name):
301 def _add_notifiers(self, handler, name):
296 if not self._traitlet_notifiers.has_key(name):
302 if not self._traitlet_notifiers.has_key(name):
297 nlist = []
303 nlist = []
298 self._traitlet_notifiers[name] = nlist
304 self._traitlet_notifiers[name] = nlist
299 else:
305 else:
300 nlist = self._traitlet_notifiers[name]
306 nlist = self._traitlet_notifiers[name]
301 if handler not in nlist:
307 if handler not in nlist:
302 nlist.append(handler)
308 nlist.append(handler)
303
309
304 def _remove_notifiers(self, handler, name):
310 def _remove_notifiers(self, handler, name):
305 if self._traitlet_notifiers.has_key(name):
311 if self._traitlet_notifiers.has_key(name):
306 nlist = self._traitlet_notifiers[name]
312 nlist = self._traitlet_notifiers[name]
307 try:
313 try:
308 index = nlist.index(handler)
314 index = nlist.index(handler)
309 except ValueError:
315 except ValueError:
310 pass
316 pass
311 else:
317 else:
312 del nlist[index]
318 del nlist[index]
313
319
314 def on_traitlet_change(self, handler, name=None, remove=False):
320 def on_traitlet_change(self, handler, name=None, remove=False):
315 """Setup a handler to be called when a traitlet changes.
321 """Setup a handler to be called when a traitlet changes.
316
322
317 This is used to setup dynamic notifications of traitlet changes.
323 This is used to setup dynamic notifications of traitlet changes.
318
324
319 Static handlers can be created by creating methods on a HasTraitlets
325 Static handlers can be created by creating methods on a HasTraitlets
320 subclass with the naming convention '_[traitletname]_changed'. Thus,
326 subclass with the naming convention '_[traitletname]_changed'. Thus,
321 to create static handler for the traitlet 'a', create the method
327 to create static handler for the traitlet 'a', create the method
322 _a_changed(self, name, old, new) (fewer arguments can be used, see
328 _a_changed(self, name, old, new) (fewer arguments can be used, see
323 below).
329 below).
324
330
325 Parameters
331 Parameters
326 ----------
332 ----------
327 handler : callable
333 handler : callable
328 A callable that is called when a traitlet changes. Its
334 A callable that is called when a traitlet changes. Its
329 signature can be handler(), handler(name), handler(name, new)
335 signature can be handler(), handler(name), handler(name, new)
330 or handler(name, old, new).
336 or handler(name, old, new).
331 name : list, str, None
337 name : list, str, None
332 If None, the handler will apply to all traitlets. If a list
338 If None, the handler will apply to all traitlets. If a list
333 of str, handler will apply to all names in the list. If a
339 of str, handler will apply to all names in the list. If a
334 str, the handler will apply just to that name.
340 str, the handler will apply just to that name.
335 remove : bool
341 remove : bool
336 If False (the default), then install the handler. If True
342 If False (the default), then install the handler. If True
337 then unintall it.
343 then unintall it.
338 """
344 """
339 if remove:
345 if remove:
340 names = parse_notifier_name(name)
346 names = parse_notifier_name(name)
341 for n in names:
347 for n in names:
342 self._remove_notifiers(handler, n)
348 self._remove_notifiers(handler, n)
343 else:
349 else:
344 names = parse_notifier_name(name)
350 names = parse_notifier_name(name)
345 for n in names:
351 for n in names:
346 self._add_notifiers(handler, n)
352 self._add_notifiers(handler, n)
347
353
348 def _add_class_traitlet(self, name, traitlet):
354 def _add_class_traitlet(self, name, traitlet):
349 """Add a class-level traitlet.
355 """Add a class-level traitlet.
350
356
351 This create a new traitlet attached to all instances of this class.
357 This create a new traitlet attached to all instances of this class.
352 But, the value can be different on each instance. But, this behavior
358 But, the value can be different on each instance. But, this behavior
353 is likely to trip up many folks as they would expect the traitlet
359 is likely to trip up many folks as they would expect the traitlet
354 type to be different on each instance.
360 type to be different on each instance.
355
361
356 Parameters
362 Parameters
357 ----------
363 ----------
358 name : str
364 name : str
359 The name of the traitlet.
365 The name of the traitlet.
360 traitlet : TraitletType or an instance of one
366 traitlet : TraitletType or an instance of one
361 The traitlet to assign to the name.
367 The traitlet to assign to the name.
362 """
368 """
363 if inspect.isclass(traitlet):
369 if inspect.isclass(traitlet):
364 inst = traitlet()
370 inst = traitlet()
365 else:
371 else:
366 inst = traitlet
372 inst = traitlet
367 assert isinstance(inst, TraitletType)
373 assert isinstance(inst, TraitletType)
368 inst.name = name
374 inst.name = name
369 setattr(self.__class__, name, inst)
375 setattr(self.__class__, name, inst)
370
376
377 def traitlet_keys(self):
378 """Get a list of all the names of this classes traitlets."""
379 return [memb[0] for memb in inspect.getmembers(self.__class__) if isinstance(memb[1], TraitletType)]
380
371
381
372 #-----------------------------------------------------------------------------
382 #-----------------------------------------------------------------------------
373 # Actual TraitletTypes implementations/subclasses
383 # Actual TraitletTypes implementations/subclasses
374 #-----------------------------------------------------------------------------
384 #-----------------------------------------------------------------------------
375
385
376 #-----------------------------------------------------------------------------
386 #-----------------------------------------------------------------------------
377 # TraitletTypes subclasses for handling classes and instances of classes
387 # TraitletTypes subclasses for handling classes and instances of classes
378 #-----------------------------------------------------------------------------
388 #-----------------------------------------------------------------------------
379
389
380
390
381 class BaseClassResolver(TraitletType):
391 class BaseClassResolver(TraitletType):
382 """Mixin class for traitlets that need to resolve classes by strings.
392 """Mixin class for traitlets that need to resolve classes by strings.
383
393
384 This class provides is a mixin that provides its subclasses with the
394 This class provides is a mixin that provides its subclasses with the
385 ability to resolve classes by specifying a string name (for example,
395 ability to resolve classes by specifying a string name (for example,
386 'foo.bar.MyClass'). An actual class can also be resolved.
396 'foo.bar.MyClass'). An actual class can also be resolved.
387
397
388 Any subclass must define instances with 'klass' and 'module' attributes
398 Any subclass must define instances with 'klass' and 'module' attributes
389 that contain the string name of the class (or actual class object) and
399 that contain the string name of the class (or actual class object) and
390 the module name that contained the original trait definition (used for
400 the module name that contained the original trait definition (used for
391 resolving local class names (e.g. 'LocalClass')).
401 resolving local class names (e.g. 'LocalClass')).
392 """
402 """
393
403
394 def resolve_class(self, obj, value):
404 def resolve_class(self, obj, value):
395 klass = self.validate_class(self.find_class(self.klass))
405 klass = self.validate_class(self.find_class(self.klass))
396 if klass is None:
406 if klass is None:
397 self.validate_failed(obj, value)
407 self.validate_failed(obj, value)
398
408
399 self.klass = klass
409 self.klass = klass
400
410
401 def validate_class(self, klass):
411 def validate_class(self, klass):
402 return klass
412 return klass
403
413
404 def find_class(self, klass):
414 def find_class(self, klass):
405 module = self.module
415 module = self.module
406 col = klass.rfind('.')
416 col = klass.rfind('.')
407 if col >= 0:
417 if col >= 0:
408 module = klass[ : col ]
418 module = klass[ : col ]
409 klass = klass[ col + 1: ]
419 klass = klass[ col + 1: ]
410
420
411 theClass = getattr(sys.modules.get(module), klass, None)
421 theClass = getattr(sys.modules.get(module), klass, None)
412 if (theClass is None) and (col >= 0):
422 if (theClass is None) and (col >= 0):
413 try:
423 try:
414 mod = __import__(module)
424 mod = __import__(module)
415 for component in module.split( '.' )[1:]:
425 for component in module.split( '.' )[1:]:
416 mod = getattr(mod, component)
426 mod = getattr(mod, component)
417
427
418 theClass = getattr(mod, klass, None)
428 theClass = getattr(mod, klass, None)
419 except:
429 except:
420 pass
430 pass
421
431
422 return theClass
432 return theClass
423
433
424 def validate_failed (self, obj, value):
434 def validate_failed (self, obj, value):
425 kind = type(value)
435 kind = type(value)
426 if kind is InstanceType:
436 if kind is InstanceType:
427 msg = 'class %s' % value.__class__.__name__
437 msg = 'class %s' % value.__class__.__name__
428 else:
438 else:
429 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
439 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
430
440
431 self.error(obj, msg)
441 self.error(obj, msg)
432
442
433
443
434 class Type(BaseClassResolver):
444 class Type(BaseClassResolver):
435 """A traitlet whose value must be a subclass of a specified class."""
445 """A traitlet whose value must be a subclass of a specified class."""
436
446
437 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
447 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
438 """Construct a Type traitlet
448 """Construct a Type traitlet
439
449
440 A Type traitlet specifies that its values must be subclasses of
450 A Type traitlet specifies that its values must be subclasses of
441 a particular class.
451 a particular class.
442
452
443 Parameters
453 Parameters
444 ----------
454 ----------
445 default_value : class or None
455 default_value : class or None
446 The default value must be a subclass of klass.
456 The default value must be a subclass of klass.
447 klass : class, str, None
457 klass : class, str, None
448 Values of this traitlet must be a subclass of klass. The klass
458 Values of this traitlet must be a subclass of klass. The klass
449 may be specified in a string like: 'foo.bar.MyClass'.
459 may be specified in a string like: 'foo.bar.MyClass'.
450 allow_none : boolean
460 allow_none : boolean
451 Indicates whether None is allowed as an assignable value. Even if
461 Indicates whether None is allowed as an assignable value. Even if
452 ``False``, the default value may be ``None``.
462 ``False``, the default value may be ``None``.
453 """
463 """
454 if default_value is None:
464 if default_value is None:
455 if klass is None:
465 if klass is None:
456 klass = object
466 klass = object
457 elif klass is None:
467 elif klass is None:
458 klass = default_value
468 klass = default_value
459
469
460 if isinstance(klass, basestring):
470 if isinstance(klass, basestring):
461 self.validate = self.resolve
471 self.validate = self.resolve
462 elif not isinstance(klass, ClassTypes):
472 elif not isinstance(klass, ClassTypes):
463 raise TraitletError("A Type traitlet must specify a class.")
473 raise TraitletError("A Type traitlet must specify a class.")
464
474
465 self.klass = klass
475 self.klass = klass
466 self._allow_none = allow_none
476 self._allow_none = allow_none
467 self.module = get_module_name()
477 self.module = get_module_name()
468
478
469 super(Type, self).__init__(default_value, **metadata)
479 super(Type, self).__init__(default_value, **metadata)
470
480
471 def validate(self, obj, value):
481 def validate(self, obj, value):
472 """Validates that the value is a valid object instance."""
482 """Validates that the value is a valid object instance."""
473 try:
483 try:
474 if issubclass(value, self.klass):
484 if issubclass(value, self.klass):
475 return value
485 return value
476 except:
486 except:
477 if (value is None) and (self._allow_none):
487 if (value is None) and (self._allow_none):
478 return value
488 return value
479
489
480 self.error(obj, value)
490 self.error(obj, value)
481
491
482 def resolve(self, obj, name, value):
492 def resolve(self, obj, name, value):
483 """ Resolves a class originally specified as a string into an actual
493 """ Resolves a class originally specified as a string into an actual
484 class, then resets the trait so that future calls will be handled by
494 class, then resets the trait so that future calls will be handled by
485 the normal validate method.
495 the normal validate method.
486 """
496 """
487 if isinstance(self.klass, basestring):
497 if isinstance(self.klass, basestring):
488 self.resolve_class(obj, value)
498 self.resolve_class(obj, value)
489 del self.validate
499 del self.validate
490
500
491 return self.validate(obj, value)
501 return self.validate(obj, value)
492
502
493 def info(self):
503 def info(self):
494 """ Returns a description of the trait."""
504 """ Returns a description of the trait."""
495 klass = self.klass
505 klass = self.klass
496 if not isinstance(klass, basestring):
506 if not isinstance(klass, basestring):
497 klass = klass.__name__
507 klass = klass.__name__
498
508
499 result = 'a subclass of ' + klass
509 result = 'a subclass of ' + klass
500
510
501 if self._allow_none:
511 if self._allow_none:
502 return result + ' or None'
512 return result + ' or None'
503
513
504 return result
514 return result
505
515
506 def get_default_value(self):
516 def get_default_value(self):
507 """ Returns a tuple of the form: ( default_value_type, default_value )
517 """ Returns a tuple of the form: ( default_value_type, default_value )
508 which describes the default value for this trait.
518 which describes the default value for this trait.
509 """
519 """
510 if not isinstance(self.default_value, basestring):
520 if not isinstance(self.default_value, basestring):
511 return super(Type, self).get_default_value()
521 return super(Type, self).get_default_value()
512
522
513 dv = self.resolve_default_value()
523 dv = self.resolve_default_value()
514 dvt = type(dv)
524 dvt = type(dv)
515 return (dvt, dv)
525 return (dvt, dv)
516
526
517 def resolve_default_value(self):
527 def resolve_default_value(self):
518 """ Resolves a class name into a class so that it can be used to
528 """ Resolves a class name into a class so that it can be used to
519 return the class as the default value of the trait.
529 return the class as the default value of the trait.
520 """
530 """
521 if isinstance(self.klass, basestring):
531 if isinstance(self.klass, basestring):
522 try:
532 try:
523 self.resolve_class(None, None)
533 self.resolve_class(None, None)
524 del self.validate
534 del self.validate
525 except:
535 except:
526 raise TraitletError('Could not resolve %s into a valid class' %
536 raise TraitletError('Could not resolve %s into a valid class' %
527 self.klass )
537 self.klass )
528
538
529 return self.klass
539 return self.klass
530
540
531
541
532 class DefaultValueGenerator(object):
542 class DefaultValueGenerator(object):
533 """A class for generating new default value instances."""
543 """A class for generating new default value instances."""
534
544
535 def __init__(self, klass, *args, **kw):
545 def __init__(self, klass, *args, **kw):
536 self.klass = klass
546 self.klass = klass
537 self.args = args
547 self.args = args
538 self.kw = kw
548 self.kw = kw
539
549
540
550
541 class Instance(BaseClassResolver):
551 class Instance(BaseClassResolver):
542 """A trait whose value must be an instance of a specified class.
552 """A trait whose value must be an instance of a specified class.
543
553
544 The value can also be an instance of a subclass of the specified class.
554 The value can also be an instance of a subclass of the specified class.
545 """
555 """
546
556
547 def __init__(self, klass=None, args=None, kw=None, allow_none=True,
557 def __init__(self, klass=None, args=None, kw=None, allow_none=True,
548 module = None, **metadata ):
558 module = None, **metadata ):
549 """Construct an Instance traitlet.
559 """Construct an Instance traitlet.
550
560
551 Parameters
561 Parameters
552 ----------
562 ----------
553 klass : class or instance
563 klass : class or instance
554 The object that forms the basis for the traitlet. If an instance
564 The object that forms the basis for the traitlet. If an instance
555 values must have isinstance(value, type(instance)).
565 values must have isinstance(value, type(instance)).
556 args : tuple
566 args : tuple
557 Positional arguments for generating the default value.
567 Positional arguments for generating the default value.
558 kw : dict
568 kw : dict
559 Keyword arguments for generating the default value.
569 Keyword arguments for generating the default value.
560 allow_none : bool
570 allow_none : bool
561 Indicates whether None is allowed as a value.
571 Indicates whether None is allowed as a value.
562
572
563 Default Value
573 Default Value
564 -------------
574 -------------
565 If klass is an instance, default value is None. If klass is a class
575 If klass is an instance, default value is None. If klass is a class
566 then the default value is obtained by calling ``klass(*args, **kw)``.
576 then the default value is obtained by calling ``klass(*args, **kw)``.
567 If klass is a str, it is first resolved to an actual class and then
577 If klass is a str, it is first resolved to an actual class and then
568 instantiated with ``klass(*args, **kw)``.
578 instantiated with ``klass(*args, **kw)``.
569 """
579 """
570
580
571 self._allow_none = allow_none
581 self._allow_none = allow_none
572 self.module = module or get_module_name()
582 self.module = module or get_module_name()
573
583
574 if klass is None:
584 if klass is None:
575 raise TraitletError('A %s traitlet must have a class specified.' %
585 raise TraitletError('A %s traitlet must have a class specified.' %
576 self.__class__.__name__ )
586 self.__class__.__name__ )
577 elif not isinstance(klass, (basestring,) + ClassTypes ):
587 elif not isinstance(klass, (basestring,) + ClassTypes ):
578 # klass is an instance so default value will be None
588 # klass is an instance so default value will be None
579 self.klass = klass.__class__
589 self.klass = klass.__class__
580 default_value = None
590 default_value = None
581 else:
591 else:
582 # klass is a str or class so we handle args, kw
592 # klass is a str or class so we handle args, kw
583 if args is None:
593 if args is None:
584 args = ()
594 args = ()
585 if kw is None:
595 if kw is None:
586 if isinstance(args, dict):
596 if isinstance(args, dict):
587 kw = args
597 kw = args
588 args = ()
598 args = ()
589 else:
599 else:
590 kw = {}
600 kw = {}
591 if not isinstance(kw, dict):
601 if not isinstance(kw, dict):
592 raise TraitletError("The 'kw' argument must be a dict.")
602 raise TraitletError("The 'kw' argument must be a dict.")
593 if not isinstance(args, tuple):
603 if not isinstance(args, tuple):
594 raise TraitletError("The 'args' argument must be a tuple.")
604 raise TraitletError("The 'args' argument must be a tuple.")
595 self.klass = klass
605 self.klass = klass
596 # This tells my get_default_value that the default value
606 # This tells my get_default_value that the default value
597 # instance needs to be generated when it is called. This
607 # instance needs to be generated when it is called. This
598 # is usually when TraitletType.__get__ is called for the 1st time.
608 # is usually when TraitletType.__get__ is called for the 1st time.
599
609
600 default_value = DefaultValueGenerator(klass, *args, **kw)
610 default_value = DefaultValueGenerator(klass, *args, **kw)
601
611
602 super(Instance, self).__init__(default_value, **metadata)
612 super(Instance, self).__init__(default_value, **metadata)
603
613
604 def validate(self, obj, value):
614 def validate(self, obj, value):
605 if value is None:
615 if value is None:
606 if self._allow_none:
616 if self._allow_none:
607 return value
617 return value
608 self.validate_failed(obj, value)
618 self.validate_failed(obj, value)
609
619
610 # This is where self.klass is turned into a real class if it was
620 # This is where self.klass is turned into a real class if it was
611 # a str initially. This happens the first time TraitletType.__set__
621 # a str initially. This happens the first time TraitletType.__set__
612 # is called. This does happen if a default value is generated by
622 # is called. This does happen if a default value is generated by
613 # TraitletType.__get__.
623 # TraitletType.__get__.
614 if isinstance(self.klass, basestring):
624 if isinstance(self.klass, basestring):
615 self.resolve_class(obj, value)
625 self.resolve_class(obj, value)
616
626
617 if isinstance(value, self.klass):
627 if isinstance(value, self.klass):
618 return value
628 return value
619 else:
629 else:
620 self.validate_failed(obj, value)
630 self.validate_failed(obj, value)
621
631
622 def info ( self ):
632 def info ( self ):
623 klass = self.klass
633 klass = self.klass
624 if not isinstance( klass, basestring ):
634 if not isinstance( klass, basestring ):
625 klass = klass.__name__
635 klass = klass.__name__
626 result = class_of(klass)
636 result = class_of(klass)
627 if self._allow_none:
637 if self._allow_none:
628 return result + ' or None'
638 return result + ' or None'
629
639
630 return result
640 return result
631
641
632 def get_default_value ( self ):
642 def get_default_value ( self ):
633 """Instantiate a default value instance.
643 """Instantiate a default value instance.
634
644
635 When TraitletType.__get__ is called the first time, this is called
645 When TraitletType.__get__ is called the first time, this is called
636 (if no value has been assigned) to get a default value instance.
646 (if no value has been assigned) to get a default value instance.
637 """
647 """
638 dv = self.default_value
648 dv = self.default_value
639 if isinstance(dv, DefaultValueGenerator):
649 if isinstance(dv, DefaultValueGenerator):
640 klass = dv.klass
650 klass = dv.klass
641 args = dv.args
651 args = dv.args
642 kw = dv.kw
652 kw = dv.kw
643 if isinstance(klass, basestring):
653 if isinstance(klass, basestring):
644 klass = self.validate_class(self.find_class(klass))
654 klass = self.validate_class(self.find_class(klass))
645 if klass is None:
655 if klass is None:
646 raise TraitletError('Unable to locate class: ' + dv.klass)
656 raise TraitletError('Unable to locate class: ' + dv.klass)
647 return klass(*args, **kw)
657 return klass(*args, **kw)
648 else:
658 else:
649 return dv
659 return dv
650
660
651
661
652 #-----------------------------------------------------------------------------
662 #-----------------------------------------------------------------------------
653 # Basic TraitletTypes implementations/subclasses
663 # Basic TraitletTypes implementations/subclasses
654 #-----------------------------------------------------------------------------
664 #-----------------------------------------------------------------------------
655
665
656
666
657 class Any(TraitletType):
667 class Any(TraitletType):
658 default_value = None
668 default_value = None
659 info_text = 'any value'
669 info_text = 'any value'
660
670
661
671
662 class Int(TraitletType):
672 class Int(TraitletType):
663 """A integer traitlet."""
673 """A integer traitlet."""
664
674
665 evaluate = int
675 evaluate = int
666 default_value = 0
676 default_value = 0
667 info_text = 'an integer'
677 info_text = 'an integer'
668
678
669 def validate(self, obj, value):
679 def validate(self, obj, value):
670 if isinstance(value, int):
680 if isinstance(value, int):
671 return value
681 return value
672 self.error(obj, value)
682 self.error(obj, value)
673
683
674 class CInt(Int):
684 class CInt(Int):
675 """A casting version of the int traitlet."""
685 """A casting version of the int traitlet."""
676
686
677 def validate(self, obj, value):
687 def validate(self, obj, value):
678 try:
688 try:
679 return int(value)
689 return int(value)
680 except:
690 except:
681 self.error(obj, value)
691 self.error(obj, value)
682
692
683
693
684 class Long(TraitletType):
694 class Long(TraitletType):
685 """A long integer traitlet."""
695 """A long integer traitlet."""
686
696
687 evaluate = long
697 evaluate = long
688 default_value = 0L
698 default_value = 0L
689 info_text = 'a long'
699 info_text = 'a long'
690
700
691 def validate(self, obj, value):
701 def validate(self, obj, value):
692 if isinstance(value, long):
702 if isinstance(value, long):
693 return value
703 return value
694 if isinstance(value, int):
704 if isinstance(value, int):
695 return long(value)
705 return long(value)
696 self.error(obj, value)
706 self.error(obj, value)
697
707
698
708
699 class CLong(Long):
709 class CLong(Long):
700 """A casting version of the long integer traitlet."""
710 """A casting version of the long integer traitlet."""
701
711
702 def validate(self, obj, value):
712 def validate(self, obj, value):
703 try:
713 try:
704 return long(value)
714 return long(value)
705 except:
715 except:
706 self.error(obj, value)
716 self.error(obj, value)
707
717
708
718
709 class Float(TraitletType):
719 class Float(TraitletType):
710 """A float traitlet."""
720 """A float traitlet."""
711
721
712 evaluate = float
722 evaluate = float
713 default_value = 0.0
723 default_value = 0.0
714 info_text = 'a float'
724 info_text = 'a float'
715
725
716 def validate(self, obj, value):
726 def validate(self, obj, value):
717 if isinstance(value, float):
727 if isinstance(value, float):
718 return value
728 return value
719 if isinstance(value, int):
729 if isinstance(value, int):
720 return float(value)
730 return float(value)
721 self.error(obj, value)
731 self.error(obj, value)
722
732
723
733
724 class CFloat(Float):
734 class CFloat(Float):
725 """A casting version of the float traitlet."""
735 """A casting version of the float traitlet."""
726
736
727 def validate(self, obj, value):
737 def validate(self, obj, value):
728 try:
738 try:
729 return float(value)
739 return float(value)
730 except:
740 except:
731 self.error(obj, value)
741 self.error(obj, value)
732
742
733 class Complex(TraitletType):
743 class Complex(TraitletType):
734 """A traitlet for complex numbers."""
744 """A traitlet for complex numbers."""
735
745
736 evaluate = complex
746 evaluate = complex
737 default_value = 0.0 + 0.0j
747 default_value = 0.0 + 0.0j
738 info_text = 'a complex number'
748 info_text = 'a complex number'
739
749
740 def validate(self, obj, value):
750 def validate(self, obj, value):
741 if isinstance(value, complex):
751 if isinstance(value, complex):
742 return value
752 return value
743 if isinstance(value, (float, int)):
753 if isinstance(value, (float, int)):
744 return complex(value)
754 return complex(value)
745 self.error(obj, value)
755 self.error(obj, value)
746
756
747
757
748 class CComplex(Complex):
758 class CComplex(Complex):
749 """A casting version of the complex number traitlet."""
759 """A casting version of the complex number traitlet."""
750
760
751 def validate (self, obj, value):
761 def validate (self, obj, value):
752 try:
762 try:
753 return complex(value)
763 return complex(value)
754 except:
764 except:
755 self.error(obj, value)
765 self.error(obj, value)
756
766
757
767
758 class Str(TraitletType):
768 class Str(TraitletType):
759 """A traitlet for strings."""
769 """A traitlet for strings."""
760
770
761 evaluate = lambda x: x
771 evaluate = lambda x: x
762 default_value = ''
772 default_value = ''
763 info_text = 'a string'
773 info_text = 'a string'
764
774
765 def validate(self, obj, value):
775 def validate(self, obj, value):
766 if isinstance(value, str):
776 if isinstance(value, str):
767 return value
777 return value
768 self.error(obj, value)
778 self.error(obj, value)
769
779
770
780
771 class CStr(Str):
781 class CStr(Str):
772 """A casting version of the string traitlet."""
782 """A casting version of the string traitlet."""
773
783
774 def validate(self, obj, value):
784 def validate(self, obj, value):
775 try:
785 try:
776 return str(value)
786 return str(value)
777 except:
787 except:
778 try:
788 try:
779 return unicode(value)
789 return unicode(value)
780 except:
790 except:
781 self.error(obj, value)
791 self.error(obj, value)
782
792
783
793
784 class Unicode(TraitletType):
794 class Unicode(TraitletType):
785 """A traitlet for unicode strings."""
795 """A traitlet for unicode strings."""
786
796
787 evaluate = unicode
797 evaluate = unicode
788 default_value = u''
798 default_value = u''
789 info_text = 'a unicode string'
799 info_text = 'a unicode string'
790
800
791 def validate(self, obj, value):
801 def validate(self, obj, value):
792 if isinstance(value, unicode):
802 if isinstance(value, unicode):
793 return value
803 return value
794 if isinstance(value, str):
804 if isinstance(value, str):
795 return unicode(value)
805 return unicode(value)
796 self.error(obj, value)
806 self.error(obj, value)
797
807
798
808
799 class CUnicode(Unicode):
809 class CUnicode(Unicode):
800 """A casting version of the unicode traitlet."""
810 """A casting version of the unicode traitlet."""
801
811
802 def validate(self, obj, value):
812 def validate(self, obj, value):
803 try:
813 try:
804 return unicode(value)
814 return unicode(value)
805 except:
815 except:
806 self.error(obj, value)
816 self.error(obj, value)
807
817
808
818
809 class Bool(TraitletType):
819 class Bool(TraitletType):
810 """A boolean (True, False) traitlet."""
820 """A boolean (True, False) traitlet."""
811 evaluate = bool
821 evaluate = bool
812 default_value = False
822 default_value = False
813 info_text = 'a boolean'
823 info_text = 'a boolean'
814
824
815 def validate(self, obj, value):
825 def validate(self, obj, value):
816 if isinstance(value, bool):
826 if isinstance(value, bool):
817 return value
827 return value
818 self.error(obj, value)
828 self.error(obj, value)
819
829
820
830
821 class CBool(Bool):
831 class CBool(Bool):
822 """A casting version of the boolean traitlet."""
832 """A casting version of the boolean traitlet."""
823
833
824 def validate(self, obj, value):
834 def validate(self, obj, value):
825 try:
835 try:
826 return bool(value)
836 return bool(value)
827 except:
837 except:
828 self.error(obj, value) No newline at end of file
838 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now