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