##// END OF EJS Templates
Fix test of hold_trait_validation
Sylvain Corlay -
Show More
@@ -1,1617 +1,1617
1 1 # encoding: utf-8
2 2 """Tests for IPython.utils.traitlets."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6 #
7 7 # Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
8 8 # also under the terms of the Modified BSD License.
9 9
10 10 import pickle
11 11 import re
12 12 import sys
13 13 from unittest import TestCase
14 14
15 15 import nose.tools as nt
16 16 from nose import SkipTest
17 17
18 18 from IPython.utils.traitlets import (
19 19 HasTraits, MetaHasTraits, TraitType, Any, Bool, CBytes, Dict, Enum,
20 20 Int, Long, Integer, Float, Complex, Bytes, Unicode, TraitError,
21 21 Union, Undefined, Type, This, Instance, TCPAddress, List, Tuple,
22 22 ObjectName, DottedObjectName, CRegExp, link, directional_link,
23 23 EventfulList, EventfulDict, ForwardDeclaredType, ForwardDeclaredInstance,
24 24 )
25 25 from IPython.utils import py3compat
26 26 from IPython.testing.decorators import skipif
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Helper classes for testing
30 30 #-----------------------------------------------------------------------------
31 31
32 32
33 33 class HasTraitsStub(HasTraits):
34 34
35 35 def _notify_trait(self, name, old, new):
36 36 self._notify_name = name
37 37 self._notify_old = old
38 38 self._notify_new = new
39 39
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Test classes
43 43 #-----------------------------------------------------------------------------
44 44
45 45
46 46 class TestTraitType(TestCase):
47 47
48 48 def test_get_undefined(self):
49 49 class A(HasTraits):
50 50 a = TraitType
51 51 a = A()
52 52 self.assertEqual(a.a, Undefined)
53 53
54 54 def test_set(self):
55 55 class A(HasTraitsStub):
56 56 a = TraitType
57 57
58 58 a = A()
59 59 a.a = 10
60 60 self.assertEqual(a.a, 10)
61 61 self.assertEqual(a._notify_name, 'a')
62 62 self.assertEqual(a._notify_old, Undefined)
63 63 self.assertEqual(a._notify_new, 10)
64 64
65 65 def test_validate(self):
66 66 class MyTT(TraitType):
67 67 def validate(self, inst, value):
68 68 return -1
69 69 class A(HasTraitsStub):
70 70 tt = MyTT
71 71
72 72 a = A()
73 73 a.tt = 10
74 74 self.assertEqual(a.tt, -1)
75 75
76 76 def test_default_validate(self):
77 77 class MyIntTT(TraitType):
78 78 def validate(self, obj, value):
79 79 if isinstance(value, int):
80 80 return value
81 81 self.error(obj, value)
82 82 class A(HasTraits):
83 83 tt = MyIntTT(10)
84 84 a = A()
85 85 self.assertEqual(a.tt, 10)
86 86
87 87 # Defaults are validated when the HasTraits is instantiated
88 88 class B(HasTraits):
89 89 tt = MyIntTT('bad default')
90 90 self.assertRaises(TraitError, B)
91 91
92 92 def test_info(self):
93 93 class A(HasTraits):
94 94 tt = TraitType
95 95 a = A()
96 96 self.assertEqual(A.tt.info(), 'any value')
97 97
98 98 def test_error(self):
99 99 class A(HasTraits):
100 100 tt = TraitType
101 101 a = A()
102 102 self.assertRaises(TraitError, A.tt.error, a, 10)
103 103
104 104 def test_dynamic_initializer(self):
105 105 class A(HasTraits):
106 106 x = Int(10)
107 107 def _x_default(self):
108 108 return 11
109 109 class B(A):
110 110 x = Int(20)
111 111 class C(A):
112 112 def _x_default(self):
113 113 return 21
114 114
115 115 a = A()
116 116 self.assertEqual(a._trait_values, {})
117 117 self.assertEqual(list(a._trait_dyn_inits.keys()), ['x'])
118 118 self.assertEqual(a.x, 11)
119 119 self.assertEqual(a._trait_values, {'x': 11})
120 120 b = B()
121 121 self.assertEqual(b._trait_values, {'x': 20})
122 122 self.assertEqual(list(a._trait_dyn_inits.keys()), ['x'])
123 123 self.assertEqual(b.x, 20)
124 124 c = C()
125 125 self.assertEqual(c._trait_values, {})
126 126 self.assertEqual(list(a._trait_dyn_inits.keys()), ['x'])
127 127 self.assertEqual(c.x, 21)
128 128 self.assertEqual(c._trait_values, {'x': 21})
129 129 # Ensure that the base class remains unmolested when the _default
130 130 # initializer gets overridden in a subclass.
131 131 a = A()
132 132 c = C()
133 133 self.assertEqual(a._trait_values, {})
134 134 self.assertEqual(list(a._trait_dyn_inits.keys()), ['x'])
135 135 self.assertEqual(a.x, 11)
136 136 self.assertEqual(a._trait_values, {'x': 11})
137 137
138 138
139 139
140 140 class TestHasTraitsMeta(TestCase):
141 141
142 142 def test_metaclass(self):
143 143 self.assertEqual(type(HasTraits), MetaHasTraits)
144 144
145 145 class A(HasTraits):
146 146 a = Int
147 147
148 148 a = A()
149 149 self.assertEqual(type(a.__class__), MetaHasTraits)
150 150 self.assertEqual(a.a,0)
151 151 a.a = 10
152 152 self.assertEqual(a.a,10)
153 153
154 154 class B(HasTraits):
155 155 b = Int()
156 156
157 157 b = B()
158 158 self.assertEqual(b.b,0)
159 159 b.b = 10
160 160 self.assertEqual(b.b,10)
161 161
162 162 class C(HasTraits):
163 163 c = Int(30)
164 164
165 165 c = C()
166 166 self.assertEqual(c.c,30)
167 167 c.c = 10
168 168 self.assertEqual(c.c,10)
169 169
170 170 def test_this_class(self):
171 171 class A(HasTraits):
172 172 t = This()
173 173 tt = This()
174 174 class B(A):
175 175 tt = This()
176 176 ttt = This()
177 177 self.assertEqual(A.t.this_class, A)
178 178 self.assertEqual(B.t.this_class, A)
179 179 self.assertEqual(B.tt.this_class, B)
180 180 self.assertEqual(B.ttt.this_class, B)
181 181
182 182 class TestHasTraitsNotify(TestCase):
183 183
184 184 def setUp(self):
185 185 self._notify1 = []
186 186 self._notify2 = []
187 187
188 188 def notify1(self, name, old, new):
189 189 self._notify1.append((name, old, new))
190 190
191 191 def notify2(self, name, old, new):
192 192 self._notify2.append((name, old, new))
193 193
194 194 def test_notify_all(self):
195 195
196 196 class A(HasTraits):
197 197 a = Int
198 198 b = Float
199 199
200 200 a = A()
201 201 a.on_trait_change(self.notify1)
202 202 a.a = 0
203 203 self.assertEqual(len(self._notify1),0)
204 204 a.b = 0.0
205 205 self.assertEqual(len(self._notify1),0)
206 206 a.a = 10
207 207 self.assertTrue(('a',0,10) in self._notify1)
208 208 a.b = 10.0
209 209 self.assertTrue(('b',0.0,10.0) in self._notify1)
210 210 self.assertRaises(TraitError,setattr,a,'a','bad string')
211 211 self.assertRaises(TraitError,setattr,a,'b','bad string')
212 212 self._notify1 = []
213 213 a.on_trait_change(self.notify1,remove=True)
214 214 a.a = 20
215 215 a.b = 20.0
216 216 self.assertEqual(len(self._notify1),0)
217 217
218 218 def test_notify_one(self):
219 219
220 220 class A(HasTraits):
221 221 a = Int
222 222 b = Float
223 223
224 224 a = A()
225 225 a.on_trait_change(self.notify1, 'a')
226 226 a.a = 0
227 227 self.assertEqual(len(self._notify1),0)
228 228 a.a = 10
229 229 self.assertTrue(('a',0,10) in self._notify1)
230 230 self.assertRaises(TraitError,setattr,a,'a','bad string')
231 231
232 232 def test_subclass(self):
233 233
234 234 class A(HasTraits):
235 235 a = Int
236 236
237 237 class B(A):
238 238 b = Float
239 239
240 240 b = B()
241 241 self.assertEqual(b.a,0)
242 242 self.assertEqual(b.b,0.0)
243 243 b.a = 100
244 244 b.b = 100.0
245 245 self.assertEqual(b.a,100)
246 246 self.assertEqual(b.b,100.0)
247 247
248 248 def test_notify_subclass(self):
249 249
250 250 class A(HasTraits):
251 251 a = Int
252 252
253 253 class B(A):
254 254 b = Float
255 255
256 256 b = B()
257 257 b.on_trait_change(self.notify1, 'a')
258 258 b.on_trait_change(self.notify2, 'b')
259 259 b.a = 0
260 260 b.b = 0.0
261 261 self.assertEqual(len(self._notify1),0)
262 262 self.assertEqual(len(self._notify2),0)
263 263 b.a = 10
264 264 b.b = 10.0
265 265 self.assertTrue(('a',0,10) in self._notify1)
266 266 self.assertTrue(('b',0.0,10.0) in self._notify2)
267 267
268 268 def test_static_notify(self):
269 269
270 270 class A(HasTraits):
271 271 a = Int
272 272 _notify1 = []
273 273 def _a_changed(self, name, old, new):
274 274 self._notify1.append((name, old, new))
275 275
276 276 a = A()
277 277 a.a = 0
278 278 # This is broken!!!
279 279 self.assertEqual(len(a._notify1),0)
280 280 a.a = 10
281 281 self.assertTrue(('a',0,10) in a._notify1)
282 282
283 283 class B(A):
284 284 b = Float
285 285 _notify2 = []
286 286 def _b_changed(self, name, old, new):
287 287 self._notify2.append((name, old, new))
288 288
289 289 b = B()
290 290 b.a = 10
291 291 b.b = 10.0
292 292 self.assertTrue(('a',0,10) in b._notify1)
293 293 self.assertTrue(('b',0.0,10.0) in b._notify2)
294 294
295 295 def test_notify_args(self):
296 296
297 297 def callback0():
298 298 self.cb = ()
299 299 def callback1(name):
300 300 self.cb = (name,)
301 301 def callback2(name, new):
302 302 self.cb = (name, new)
303 303 def callback3(name, old, new):
304 304 self.cb = (name, old, new)
305 305
306 306 class A(HasTraits):
307 307 a = Int
308 308
309 309 a = A()
310 310 a.on_trait_change(callback0, 'a')
311 311 a.a = 10
312 312 self.assertEqual(self.cb,())
313 313 a.on_trait_change(callback0, 'a', remove=True)
314 314
315 315 a.on_trait_change(callback1, 'a')
316 316 a.a = 100
317 317 self.assertEqual(self.cb,('a',))
318 318 a.on_trait_change(callback1, 'a', remove=True)
319 319
320 320 a.on_trait_change(callback2, 'a')
321 321 a.a = 1000
322 322 self.assertEqual(self.cb,('a',1000))
323 323 a.on_trait_change(callback2, 'a', remove=True)
324 324
325 325 a.on_trait_change(callback3, 'a')
326 326 a.a = 10000
327 327 self.assertEqual(self.cb,('a',1000,10000))
328 328 a.on_trait_change(callback3, 'a', remove=True)
329 329
330 330 self.assertEqual(len(a._trait_notifiers['a']),0)
331 331
332 332 def test_notify_only_once(self):
333 333
334 334 class A(HasTraits):
335 335 listen_to = ['a']
336 336
337 337 a = Int(0)
338 338 b = 0
339 339
340 340 def __init__(self, **kwargs):
341 341 super(A, self).__init__(**kwargs)
342 342 self.on_trait_change(self.listener1, ['a'])
343 343
344 344 def listener1(self, name, old, new):
345 345 self.b += 1
346 346
347 347 class B(A):
348 348
349 349 c = 0
350 350 d = 0
351 351
352 352 def __init__(self, **kwargs):
353 353 super(B, self).__init__(**kwargs)
354 354 self.on_trait_change(self.listener2)
355 355
356 356 def listener2(self, name, old, new):
357 357 self.c += 1
358 358
359 359 def _a_changed(self, name, old, new):
360 360 self.d += 1
361 361
362 362 b = B()
363 363 b.a += 1
364 364 self.assertEqual(b.b, b.c)
365 365 self.assertEqual(b.b, b.d)
366 366 b.a += 1
367 367 self.assertEqual(b.b, b.c)
368 368 self.assertEqual(b.b, b.d)
369 369
370 370
371 371 class TestHasTraits(TestCase):
372 372
373 373 def test_trait_names(self):
374 374 class A(HasTraits):
375 375 i = Int
376 376 f = Float
377 377 a = A()
378 378 self.assertEqual(sorted(a.trait_names()),['f','i'])
379 379 self.assertEqual(sorted(A.class_trait_names()),['f','i'])
380 380
381 381 def test_trait_metadata(self):
382 382 class A(HasTraits):
383 383 i = Int(config_key='MY_VALUE')
384 384 a = A()
385 385 self.assertEqual(a.trait_metadata('i','config_key'), 'MY_VALUE')
386 386
387 387 def test_trait_metadata_default(self):
388 388 class A(HasTraits):
389 389 i = Int()
390 390 a = A()
391 391 self.assertEqual(a.trait_metadata('i', 'config_key'), None)
392 392 self.assertEqual(a.trait_metadata('i', 'config_key', 'default'), 'default')
393 393
394 394 def test_traits(self):
395 395 class A(HasTraits):
396 396 i = Int
397 397 f = Float
398 398 a = A()
399 399 self.assertEqual(a.traits(), dict(i=A.i, f=A.f))
400 400 self.assertEqual(A.class_traits(), dict(i=A.i, f=A.f))
401 401
402 402 def test_traits_metadata(self):
403 403 class A(HasTraits):
404 404 i = Int(config_key='VALUE1', other_thing='VALUE2')
405 405 f = Float(config_key='VALUE3', other_thing='VALUE2')
406 406 j = Int(0)
407 407 a = A()
408 408 self.assertEqual(a.traits(), dict(i=A.i, f=A.f, j=A.j))
409 409 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
410 410 self.assertEqual(traits, dict(i=A.i))
411 411
412 412 # This passes, but it shouldn't because I am replicating a bug in
413 413 # traits.
414 414 traits = a.traits(config_key=lambda v: True)
415 415 self.assertEqual(traits, dict(i=A.i, f=A.f, j=A.j))
416 416
417 417 def test_init(self):
418 418 class A(HasTraits):
419 419 i = Int()
420 420 x = Float()
421 421 a = A(i=1, x=10.0)
422 422 self.assertEqual(a.i, 1)
423 423 self.assertEqual(a.x, 10.0)
424 424
425 425 def test_positional_args(self):
426 426 class A(HasTraits):
427 427 i = Int(0)
428 428 def __init__(self, i):
429 429 super(A, self).__init__()
430 430 self.i = i
431 431
432 432 a = A(5)
433 433 self.assertEqual(a.i, 5)
434 434 # should raise TypeError if no positional arg given
435 435 self.assertRaises(TypeError, A)
436 436
437 437 #-----------------------------------------------------------------------------
438 438 # Tests for specific trait types
439 439 #-----------------------------------------------------------------------------
440 440
441 441
442 442 class TestType(TestCase):
443 443
444 444 def test_default(self):
445 445
446 446 class B(object): pass
447 447 class A(HasTraits):
448 448 klass = Type(allow_none=True)
449 449
450 450 a = A()
451 451 self.assertEqual(a.klass, None)
452 452
453 453 a.klass = B
454 454 self.assertEqual(a.klass, B)
455 455 self.assertRaises(TraitError, setattr, a, 'klass', 10)
456 456
457 457 def test_value(self):
458 458
459 459 class B(object): pass
460 460 class C(object): pass
461 461 class A(HasTraits):
462 462 klass = Type(B)
463 463
464 464 a = A()
465 465 self.assertEqual(a.klass, B)
466 466 self.assertRaises(TraitError, setattr, a, 'klass', C)
467 467 self.assertRaises(TraitError, setattr, a, 'klass', object)
468 468 a.klass = B
469 469
470 470 def test_allow_none(self):
471 471
472 472 class B(object): pass
473 473 class C(B): pass
474 474 class A(HasTraits):
475 475 klass = Type(B)
476 476
477 477 a = A()
478 478 self.assertEqual(a.klass, B)
479 479 self.assertRaises(TraitError, setattr, a, 'klass', None)
480 480 a.klass = C
481 481 self.assertEqual(a.klass, C)
482 482
483 483 def test_validate_klass(self):
484 484
485 485 class A(HasTraits):
486 486 klass = Type('no strings allowed')
487 487
488 488 self.assertRaises(ImportError, A)
489 489
490 490 class A(HasTraits):
491 491 klass = Type('rub.adub.Duck')
492 492
493 493 self.assertRaises(ImportError, A)
494 494
495 495 def test_validate_default(self):
496 496
497 497 class B(object): pass
498 498 class A(HasTraits):
499 499 klass = Type('bad default', B)
500 500
501 501 self.assertRaises(ImportError, A)
502 502
503 503 class C(HasTraits):
504 504 klass = Type(None, B)
505 505
506 506 self.assertRaises(TraitError, C)
507 507
508 508 def test_str_klass(self):
509 509
510 510 class A(HasTraits):
511 511 klass = Type('IPython.utils.ipstruct.Struct')
512 512
513 513 from IPython.utils.ipstruct import Struct
514 514 a = A()
515 515 a.klass = Struct
516 516 self.assertEqual(a.klass, Struct)
517 517
518 518 self.assertRaises(TraitError, setattr, a, 'klass', 10)
519 519
520 520 def test_set_str_klass(self):
521 521
522 522 class A(HasTraits):
523 523 klass = Type()
524 524
525 525 a = A(klass='IPython.utils.ipstruct.Struct')
526 526 from IPython.utils.ipstruct import Struct
527 527 self.assertEqual(a.klass, Struct)
528 528
529 529 class TestInstance(TestCase):
530 530
531 531 def test_basic(self):
532 532 class Foo(object): pass
533 533 class Bar(Foo): pass
534 534 class Bah(object): pass
535 535
536 536 class A(HasTraits):
537 537 inst = Instance(Foo, allow_none=True)
538 538
539 539 a = A()
540 540 self.assertTrue(a.inst is None)
541 541 a.inst = Foo()
542 542 self.assertTrue(isinstance(a.inst, Foo))
543 543 a.inst = Bar()
544 544 self.assertTrue(isinstance(a.inst, Foo))
545 545 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
546 546 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
547 547 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
548 548
549 549 def test_default_klass(self):
550 550 class Foo(object): pass
551 551 class Bar(Foo): pass
552 552 class Bah(object): pass
553 553
554 554 class FooInstance(Instance):
555 555 klass = Foo
556 556
557 557 class A(HasTraits):
558 558 inst = FooInstance(allow_none=True)
559 559
560 560 a = A()
561 561 self.assertTrue(a.inst is None)
562 562 a.inst = Foo()
563 563 self.assertTrue(isinstance(a.inst, Foo))
564 564 a.inst = Bar()
565 565 self.assertTrue(isinstance(a.inst, Foo))
566 566 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
567 567 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
568 568 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
569 569
570 570 def test_unique_default_value(self):
571 571 class Foo(object): pass
572 572 class A(HasTraits):
573 573 inst = Instance(Foo,(),{})
574 574
575 575 a = A()
576 576 b = A()
577 577 self.assertTrue(a.inst is not b.inst)
578 578
579 579 def test_args_kw(self):
580 580 class Foo(object):
581 581 def __init__(self, c): self.c = c
582 582 class Bar(object): pass
583 583 class Bah(object):
584 584 def __init__(self, c, d):
585 585 self.c = c; self.d = d
586 586
587 587 class A(HasTraits):
588 588 inst = Instance(Foo, (10,))
589 589 a = A()
590 590 self.assertEqual(a.inst.c, 10)
591 591
592 592 class B(HasTraits):
593 593 inst = Instance(Bah, args=(10,), kw=dict(d=20))
594 594 b = B()
595 595 self.assertEqual(b.inst.c, 10)
596 596 self.assertEqual(b.inst.d, 20)
597 597
598 598 class C(HasTraits):
599 599 inst = Instance(Foo, allow_none=True)
600 600 c = C()
601 601 self.assertTrue(c.inst is None)
602 602
603 603 def test_bad_default(self):
604 604 class Foo(object): pass
605 605
606 606 class A(HasTraits):
607 607 inst = Instance(Foo)
608 608
609 609 self.assertRaises(TraitError, A)
610 610
611 611 def test_instance(self):
612 612 class Foo(object): pass
613 613
614 614 def inner():
615 615 class A(HasTraits):
616 616 inst = Instance(Foo())
617 617
618 618 self.assertRaises(TraitError, inner)
619 619
620 620
621 621 class TestThis(TestCase):
622 622
623 623 def test_this_class(self):
624 624 class Foo(HasTraits):
625 625 this = This
626 626
627 627 f = Foo()
628 628 self.assertEqual(f.this, None)
629 629 g = Foo()
630 630 f.this = g
631 631 self.assertEqual(f.this, g)
632 632 self.assertRaises(TraitError, setattr, f, 'this', 10)
633 633
634 634 def test_this_inst(self):
635 635 class Foo(HasTraits):
636 636 this = This()
637 637
638 638 f = Foo()
639 639 f.this = Foo()
640 640 self.assertTrue(isinstance(f.this, Foo))
641 641
642 642 def test_subclass(self):
643 643 class Foo(HasTraits):
644 644 t = This()
645 645 class Bar(Foo):
646 646 pass
647 647 f = Foo()
648 648 b = Bar()
649 649 f.t = b
650 650 b.t = f
651 651 self.assertEqual(f.t, b)
652 652 self.assertEqual(b.t, f)
653 653
654 654 def test_subclass_override(self):
655 655 class Foo(HasTraits):
656 656 t = This()
657 657 class Bar(Foo):
658 658 t = This()
659 659 f = Foo()
660 660 b = Bar()
661 661 f.t = b
662 662 self.assertEqual(f.t, b)
663 663 self.assertRaises(TraitError, setattr, b, 't', f)
664 664
665 665 def test_this_in_container(self):
666 666
667 667 class Tree(HasTraits):
668 668 value = Unicode()
669 669 leaves = List(This())
670 670
671 671 tree = Tree(
672 672 value='foo',
673 673 leaves=[Tree('bar'), Tree('buzz')]
674 674 )
675 675
676 676 with self.assertRaises(TraitError):
677 677 tree.leaves = [1, 2]
678 678
679 679 class TraitTestBase(TestCase):
680 680 """A best testing class for basic trait types."""
681 681
682 682 def assign(self, value):
683 683 self.obj.value = value
684 684
685 685 def coerce(self, value):
686 686 return value
687 687
688 688 def test_good_values(self):
689 689 if hasattr(self, '_good_values'):
690 690 for value in self._good_values:
691 691 self.assign(value)
692 692 self.assertEqual(self.obj.value, self.coerce(value))
693 693
694 694 def test_bad_values(self):
695 695 if hasattr(self, '_bad_values'):
696 696 for value in self._bad_values:
697 697 try:
698 698 self.assertRaises(TraitError, self.assign, value)
699 699 except AssertionError:
700 700 assert False, value
701 701
702 702 def test_default_value(self):
703 703 if hasattr(self, '_default_value'):
704 704 self.assertEqual(self._default_value, self.obj.value)
705 705
706 706 def test_allow_none(self):
707 707 if (hasattr(self, '_bad_values') and hasattr(self, '_good_values') and
708 708 None in self._bad_values):
709 709 trait=self.obj.traits()['value']
710 710 try:
711 711 trait.allow_none = True
712 712 self._bad_values.remove(None)
713 713 #skip coerce. Allow None casts None to None.
714 714 self.assign(None)
715 715 self.assertEqual(self.obj.value,None)
716 716 self.test_good_values()
717 717 self.test_bad_values()
718 718 finally:
719 719 #tear down
720 720 trait.allow_none = False
721 721 self._bad_values.append(None)
722 722
723 723 def tearDown(self):
724 724 # restore default value after tests, if set
725 725 if hasattr(self, '_default_value'):
726 726 self.obj.value = self._default_value
727 727
728 728
729 729 class AnyTrait(HasTraits):
730 730
731 731 value = Any
732 732
733 733 class AnyTraitTest(TraitTestBase):
734 734
735 735 obj = AnyTrait()
736 736
737 737 _default_value = None
738 738 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
739 739 _bad_values = []
740 740
741 741 class UnionTrait(HasTraits):
742 742
743 743 value = Union([Type(), Bool()])
744 744
745 745 class UnionTraitTest(TraitTestBase):
746 746
747 747 obj = UnionTrait(value='IPython.utils.ipstruct.Struct')
748 748 _good_values = [int, float, True]
749 749 _bad_values = [[], (0,), 1j]
750 750
751 751 class OrTrait(HasTraits):
752 752
753 753 value = Bool() | Unicode()
754 754
755 755 class OrTraitTest(TraitTestBase):
756 756
757 757 obj = OrTrait()
758 758 _good_values = [True, False, 'ten']
759 759 _bad_values = [[], (0,), 1j]
760 760
761 761 class IntTrait(HasTraits):
762 762
763 763 value = Int(99)
764 764
765 765 class TestInt(TraitTestBase):
766 766
767 767 obj = IntTrait()
768 768 _default_value = 99
769 769 _good_values = [10, -10]
770 770 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j,
771 771 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
772 772 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
773 773 if not py3compat.PY3:
774 774 _bad_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint])
775 775
776 776
777 777 class LongTrait(HasTraits):
778 778
779 779 value = Long(99 if py3compat.PY3 else long(99))
780 780
781 781 class TestLong(TraitTestBase):
782 782
783 783 obj = LongTrait()
784 784
785 785 _default_value = 99 if py3compat.PY3 else long(99)
786 786 _good_values = [10, -10]
787 787 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,),
788 788 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
789 789 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
790 790 u'-10.1']
791 791 if not py3compat.PY3:
792 792 # maxint undefined on py3, because int == long
793 793 _good_values.extend([long(10), long(-10), 10*sys.maxint, -10*sys.maxint])
794 794 _bad_values.extend([[long(10)], (long(10),)])
795 795
796 796 @skipif(py3compat.PY3, "not relevant on py3")
797 797 def test_cast_small(self):
798 798 """Long casts ints to long"""
799 799 self.obj.value = 10
800 800 self.assertEqual(type(self.obj.value), long)
801 801
802 802
803 803 class IntegerTrait(HasTraits):
804 804 value = Integer(1)
805 805
806 806 class TestInteger(TestLong):
807 807 obj = IntegerTrait()
808 808 _default_value = 1
809 809
810 810 def coerce(self, n):
811 811 return int(n)
812 812
813 813 @skipif(py3compat.PY3, "not relevant on py3")
814 814 def test_cast_small(self):
815 815 """Integer casts small longs to int"""
816 816 if py3compat.PY3:
817 817 raise SkipTest("not relevant on py3")
818 818
819 819 self.obj.value = long(100)
820 820 self.assertEqual(type(self.obj.value), int)
821 821
822 822
823 823 class FloatTrait(HasTraits):
824 824
825 825 value = Float(99.0)
826 826
827 827 class TestFloat(TraitTestBase):
828 828
829 829 obj = FloatTrait()
830 830
831 831 _default_value = 99.0
832 832 _good_values = [10, -10, 10.1, -10.1]
833 833 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None,
834 834 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
835 835 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
836 836 if not py3compat.PY3:
837 837 _bad_values.extend([long(10), long(-10)])
838 838
839 839
840 840 class ComplexTrait(HasTraits):
841 841
842 842 value = Complex(99.0-99.0j)
843 843
844 844 class TestComplex(TraitTestBase):
845 845
846 846 obj = ComplexTrait()
847 847
848 848 _default_value = 99.0-99.0j
849 849 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
850 850 10.1j, 10.1+10.1j, 10.1-10.1j]
851 851 _bad_values = [u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
852 852 if not py3compat.PY3:
853 853 _bad_values.extend([long(10), long(-10)])
854 854
855 855
856 856 class BytesTrait(HasTraits):
857 857
858 858 value = Bytes(b'string')
859 859
860 860 class TestBytes(TraitTestBase):
861 861
862 862 obj = BytesTrait()
863 863
864 864 _default_value = b'string'
865 865 _good_values = [b'10', b'-10', b'10L',
866 866 b'-10L', b'10.1', b'-10.1', b'string']
867 867 _bad_values = [10, -10, 10.1, -10.1, 1j, [10],
868 868 ['ten'],{'ten': 10},(10,), None, u'string']
869 869 if not py3compat.PY3:
870 870 _bad_values.extend([long(10), long(-10)])
871 871
872 872
873 873 class UnicodeTrait(HasTraits):
874 874
875 875 value = Unicode(u'unicode')
876 876
877 877 class TestUnicode(TraitTestBase):
878 878
879 879 obj = UnicodeTrait()
880 880
881 881 _default_value = u'unicode'
882 882 _good_values = ['10', '-10', '10L', '-10L', '10.1',
883 883 '-10.1', '', u'', 'string', u'string', u"€"]
884 884 _bad_values = [10, -10, 10.1, -10.1, 1j,
885 885 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
886 886 if not py3compat.PY3:
887 887 _bad_values.extend([long(10), long(-10)])
888 888
889 889
890 890 class ObjectNameTrait(HasTraits):
891 891 value = ObjectName("abc")
892 892
893 893 class TestObjectName(TraitTestBase):
894 894 obj = ObjectNameTrait()
895 895
896 896 _default_value = "abc"
897 897 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
898 898 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
899 899 None, object(), object]
900 900 if sys.version_info[0] < 3:
901 901 _bad_values.append(u"ΓΎ")
902 902 else:
903 903 _good_values.append(u"ΓΎ") # ΓΎ=1 is valid in Python 3 (PEP 3131).
904 904
905 905
906 906 class DottedObjectNameTrait(HasTraits):
907 907 value = DottedObjectName("a.b")
908 908
909 909 class TestDottedObjectName(TraitTestBase):
910 910 obj = DottedObjectNameTrait()
911 911
912 912 _default_value = "a.b"
913 913 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
914 914 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc.", None]
915 915 if sys.version_info[0] < 3:
916 916 _bad_values.append(u"t.ΓΎ")
917 917 else:
918 918 _good_values.append(u"t.ΓΎ")
919 919
920 920
921 921 class TCPAddressTrait(HasTraits):
922 922 value = TCPAddress()
923 923
924 924 class TestTCPAddress(TraitTestBase):
925 925
926 926 obj = TCPAddressTrait()
927 927
928 928 _default_value = ('127.0.0.1',0)
929 929 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
930 930 _bad_values = [(0,0),('localhost',10.0),('localhost',-1), None]
931 931
932 932 class ListTrait(HasTraits):
933 933
934 934 value = List(Int)
935 935
936 936 class TestList(TraitTestBase):
937 937
938 938 obj = ListTrait()
939 939
940 940 _default_value = []
941 941 _good_values = [[], [1], list(range(10)), (1,2)]
942 942 _bad_values = [10, [1,'a'], 'a']
943 943
944 944 def coerce(self, value):
945 945 if value is not None:
946 946 value = list(value)
947 947 return value
948 948
949 949 class Foo(object):
950 950 pass
951 951
952 952 class NoneInstanceListTrait(HasTraits):
953 953
954 954 value = List(Instance(Foo))
955 955
956 956 class TestNoneInstanceList(TraitTestBase):
957 957
958 958 obj = NoneInstanceListTrait()
959 959
960 960 _default_value = []
961 961 _good_values = [[Foo(), Foo()], []]
962 962 _bad_values = [[None], [Foo(), None]]
963 963
964 964
965 965 class InstanceListTrait(HasTraits):
966 966
967 967 value = List(Instance(__name__+'.Foo'))
968 968
969 969 class TestInstanceList(TraitTestBase):
970 970
971 971 obj = InstanceListTrait()
972 972
973 973 def test_klass(self):
974 974 """Test that the instance klass is properly assigned."""
975 975 self.assertIs(self.obj.traits()['value']._trait.klass, Foo)
976 976
977 977 _default_value = []
978 978 _good_values = [[Foo(), Foo()], []]
979 979 _bad_values = [['1', 2,], '1', [Foo], None]
980 980
981 981 class UnionListTrait(HasTraits):
982 982
983 983 value = List(Int() | Bool())
984 984
985 985 class TestUnionListTrait(HasTraits):
986 986
987 987 obj = UnionListTrait()
988 988
989 989 _default_value = []
990 990 _good_values = [[True, 1], [False, True]]
991 991 _bad_values = [[1, 'True'], False]
992 992
993 993
994 994 class LenListTrait(HasTraits):
995 995
996 996 value = List(Int, [0], minlen=1, maxlen=2)
997 997
998 998 class TestLenList(TraitTestBase):
999 999
1000 1000 obj = LenListTrait()
1001 1001
1002 1002 _default_value = [0]
1003 1003 _good_values = [[1], [1,2], (1,2)]
1004 1004 _bad_values = [10, [1,'a'], 'a', [], list(range(3))]
1005 1005
1006 1006 def coerce(self, value):
1007 1007 if value is not None:
1008 1008 value = list(value)
1009 1009 return value
1010 1010
1011 1011 class TupleTrait(HasTraits):
1012 1012
1013 1013 value = Tuple(Int(allow_none=True))
1014 1014
1015 1015 class TestTupleTrait(TraitTestBase):
1016 1016
1017 1017 obj = TupleTrait()
1018 1018
1019 1019 _default_value = None
1020 1020 _good_values = [(1,), None, (0,), [1], (None,)]
1021 1021 _bad_values = [10, (1,2), ('a'), ()]
1022 1022
1023 1023 def coerce(self, value):
1024 1024 if value is not None:
1025 1025 value = tuple(value)
1026 1026 return value
1027 1027
1028 1028 def test_invalid_args(self):
1029 1029 self.assertRaises(TypeError, Tuple, 5)
1030 1030 self.assertRaises(TypeError, Tuple, default_value='hello')
1031 1031 t = Tuple(Int, CBytes, default_value=(1,5))
1032 1032
1033 1033 class LooseTupleTrait(HasTraits):
1034 1034
1035 1035 value = Tuple((1,2,3))
1036 1036
1037 1037 class TestLooseTupleTrait(TraitTestBase):
1038 1038
1039 1039 obj = LooseTupleTrait()
1040 1040
1041 1041 _default_value = (1,2,3)
1042 1042 _good_values = [(1,), None, [1], (0,), tuple(range(5)), tuple('hello'), ('a',5), ()]
1043 1043 _bad_values = [10, 'hello', {}]
1044 1044
1045 1045 def coerce(self, value):
1046 1046 if value is not None:
1047 1047 value = tuple(value)
1048 1048 return value
1049 1049
1050 1050 def test_invalid_args(self):
1051 1051 self.assertRaises(TypeError, Tuple, 5)
1052 1052 self.assertRaises(TypeError, Tuple, default_value='hello')
1053 1053 t = Tuple(Int, CBytes, default_value=(1,5))
1054 1054
1055 1055
1056 1056 class MultiTupleTrait(HasTraits):
1057 1057
1058 1058 value = Tuple(Int, Bytes, default_value=[99,b'bottles'])
1059 1059
1060 1060 class TestMultiTuple(TraitTestBase):
1061 1061
1062 1062 obj = MultiTupleTrait()
1063 1063
1064 1064 _default_value = (99,b'bottles')
1065 1065 _good_values = [(1,b'a'), (2,b'b')]
1066 1066 _bad_values = ((),10, b'a', (1,b'a',3), (b'a',1), (1, u'a'))
1067 1067
1068 1068 class CRegExpTrait(HasTraits):
1069 1069
1070 1070 value = CRegExp(r'')
1071 1071
1072 1072 class TestCRegExp(TraitTestBase):
1073 1073
1074 1074 def coerce(self, value):
1075 1075 return re.compile(value)
1076 1076
1077 1077 obj = CRegExpTrait()
1078 1078
1079 1079 _default_value = re.compile(r'')
1080 1080 _good_values = [r'\d+', re.compile(r'\d+')]
1081 1081 _bad_values = ['(', None, ()]
1082 1082
1083 1083 class DictTrait(HasTraits):
1084 1084 value = Dict()
1085 1085
1086 1086 def test_dict_assignment():
1087 1087 d = dict()
1088 1088 c = DictTrait()
1089 1089 c.value = d
1090 1090 d['a'] = 5
1091 1091 nt.assert_equal(d, c.value)
1092 1092 nt.assert_true(c.value is d)
1093 1093
1094 1094 class ValidatedDictTrait(HasTraits):
1095 1095
1096 1096 value = Dict(Unicode())
1097 1097
1098 1098 class TestInstanceDict(TraitTestBase):
1099 1099
1100 1100 obj = ValidatedDictTrait()
1101 1101
1102 1102 _default_value = {}
1103 1103 _good_values = [{'0': 'foo'}, {'1': 'bar'}]
1104 1104 _bad_values = [{'0': 0}, {'1': 1}]
1105 1105
1106 1106
1107 1107 def test_dict_default_value():
1108 1108 """Check that the `{}` default value of the Dict traitlet constructor is
1109 1109 actually copied."""
1110 1110
1111 1111 d1, d2 = Dict(), Dict()
1112 1112 nt.assert_false(d1.get_default_value() is d2.get_default_value())
1113 1113
1114 1114
1115 1115 class TestValidationHook(TestCase):
1116 1116
1117 1117 def test_parity_trait(self):
1118 1118 """Verify that the early validation hook is effective"""
1119 1119
1120 1120 class Parity(HasTraits):
1121 1121
1122 1122 value = Int(0)
1123 1123 parity = Enum(['odd', 'even'], default_value='even')
1124 1124
1125 1125 def _value_validate(self, value, trait):
1126 1126 if self.parity == 'even' and value % 2:
1127 1127 raise TraitError('Expected an even number')
1128 1128 if self.parity == 'odd' and (value % 2 == 0):
1129 1129 raise TraitError('Expected an odd number')
1130 1130 return value
1131 1131
1132 1132 u = Parity()
1133 1133 u.parity = 'odd'
1134 1134 u.value = 1 # OK
1135 1135 with self.assertRaises(TraitError):
1136 1136 u.value = 2 # Trait Error
1137 1137
1138 1138 u.parity = 'even'
1139 1139 u.value = 2 # OK
1140 1140
1141 1141
1142 1142 class TestLink(TestCase):
1143 1143
1144 1144 def test_connect_same(self):
1145 1145 """Verify two traitlets of the same type can be linked together using link."""
1146 1146
1147 1147 # Create two simple classes with Int traitlets.
1148 1148 class A(HasTraits):
1149 1149 value = Int()
1150 1150 a = A(value=9)
1151 1151 b = A(value=8)
1152 1152
1153 1153 # Conenct the two classes.
1154 1154 c = link((a, 'value'), (b, 'value'))
1155 1155
1156 1156 # Make sure the values are the same at the point of linking.
1157 1157 self.assertEqual(a.value, b.value)
1158 1158
1159 1159 # Change one of the values to make sure they stay in sync.
1160 1160 a.value = 5
1161 1161 self.assertEqual(a.value, b.value)
1162 1162 b.value = 6
1163 1163 self.assertEqual(a.value, b.value)
1164 1164
1165 1165 def test_link_different(self):
1166 1166 """Verify two traitlets of different types can be linked together using link."""
1167 1167
1168 1168 # Create two simple classes with Int traitlets.
1169 1169 class A(HasTraits):
1170 1170 value = Int()
1171 1171 class B(HasTraits):
1172 1172 count = Int()
1173 1173 a = A(value=9)
1174 1174 b = B(count=8)
1175 1175
1176 1176 # Conenct the two classes.
1177 1177 c = link((a, 'value'), (b, 'count'))
1178 1178
1179 1179 # Make sure the values are the same at the point of linking.
1180 1180 self.assertEqual(a.value, b.count)
1181 1181
1182 1182 # Change one of the values to make sure they stay in sync.
1183 1183 a.value = 5
1184 1184 self.assertEqual(a.value, b.count)
1185 1185 b.count = 4
1186 1186 self.assertEqual(a.value, b.count)
1187 1187
1188 1188 def test_unlink(self):
1189 1189 """Verify two linked traitlets can be unlinked."""
1190 1190
1191 1191 # Create two simple classes with Int traitlets.
1192 1192 class A(HasTraits):
1193 1193 value = Int()
1194 1194 a = A(value=9)
1195 1195 b = A(value=8)
1196 1196
1197 1197 # Connect the two classes.
1198 1198 c = link((a, 'value'), (b, 'value'))
1199 1199 a.value = 4
1200 1200 c.unlink()
1201 1201
1202 1202 # Change one of the values to make sure they don't stay in sync.
1203 1203 a.value = 5
1204 1204 self.assertNotEqual(a.value, b.value)
1205 1205
1206 1206 def test_callbacks(self):
1207 1207 """Verify two linked traitlets have their callbacks called once."""
1208 1208
1209 1209 # Create two simple classes with Int traitlets.
1210 1210 class A(HasTraits):
1211 1211 value = Int()
1212 1212 class B(HasTraits):
1213 1213 count = Int()
1214 1214 a = A(value=9)
1215 1215 b = B(count=8)
1216 1216
1217 1217 # Register callbacks that count.
1218 1218 callback_count = []
1219 1219 def a_callback(name, old, new):
1220 1220 callback_count.append('a')
1221 1221 a.on_trait_change(a_callback, 'value')
1222 1222 def b_callback(name, old, new):
1223 1223 callback_count.append('b')
1224 1224 b.on_trait_change(b_callback, 'count')
1225 1225
1226 1226 # Connect the two classes.
1227 1227 c = link((a, 'value'), (b, 'count'))
1228 1228
1229 1229 # Make sure b's count was set to a's value once.
1230 1230 self.assertEqual(''.join(callback_count), 'b')
1231 1231 del callback_count[:]
1232 1232
1233 1233 # Make sure a's value was set to b's count once.
1234 1234 b.count = 5
1235 1235 self.assertEqual(''.join(callback_count), 'ba')
1236 1236 del callback_count[:]
1237 1237
1238 1238 # Make sure b's count was set to a's value once.
1239 1239 a.value = 4
1240 1240 self.assertEqual(''.join(callback_count), 'ab')
1241 1241 del callback_count[:]
1242 1242
1243 1243 class TestDirectionalLink(TestCase):
1244 1244 def test_connect_same(self):
1245 1245 """Verify two traitlets of the same type can be linked together using directional_link."""
1246 1246
1247 1247 # Create two simple classes with Int traitlets.
1248 1248 class A(HasTraits):
1249 1249 value = Int()
1250 1250 a = A(value=9)
1251 1251 b = A(value=8)
1252 1252
1253 1253 # Conenct the two classes.
1254 1254 c = directional_link((a, 'value'), (b, 'value'))
1255 1255
1256 1256 # Make sure the values are the same at the point of linking.
1257 1257 self.assertEqual(a.value, b.value)
1258 1258
1259 1259 # Change one the value of the source and check that it synchronizes the target.
1260 1260 a.value = 5
1261 1261 self.assertEqual(b.value, 5)
1262 1262 # Change one the value of the target and check that it has no impact on the source
1263 1263 b.value = 6
1264 1264 self.assertEqual(a.value, 5)
1265 1265
1266 1266 def test_link_different(self):
1267 1267 """Verify two traitlets of different types can be linked together using link."""
1268 1268
1269 1269 # Create two simple classes with Int traitlets.
1270 1270 class A(HasTraits):
1271 1271 value = Int()
1272 1272 class B(HasTraits):
1273 1273 count = Int()
1274 1274 a = A(value=9)
1275 1275 b = B(count=8)
1276 1276
1277 1277 # Conenct the two classes.
1278 1278 c = directional_link((a, 'value'), (b, 'count'))
1279 1279
1280 1280 # Make sure the values are the same at the point of linking.
1281 1281 self.assertEqual(a.value, b.count)
1282 1282
1283 1283 # Change one the value of the source and check that it synchronizes the target.
1284 1284 a.value = 5
1285 1285 self.assertEqual(b.count, 5)
1286 1286 # Change one the value of the target and check that it has no impact on the source
1287 1287 b.value = 6
1288 1288 self.assertEqual(a.value, 5)
1289 1289
1290 1290 def test_unlink(self):
1291 1291 """Verify two linked traitlets can be unlinked."""
1292 1292
1293 1293 # Create two simple classes with Int traitlets.
1294 1294 class A(HasTraits):
1295 1295 value = Int()
1296 1296 a = A(value=9)
1297 1297 b = A(value=8)
1298 1298
1299 1299 # Connect the two classes.
1300 1300 c = directional_link((a, 'value'), (b, 'value'))
1301 1301 a.value = 4
1302 1302 c.unlink()
1303 1303
1304 1304 # Change one of the values to make sure they don't stay in sync.
1305 1305 a.value = 5
1306 1306 self.assertNotEqual(a.value, b.value)
1307 1307
1308 1308 class Pickleable(HasTraits):
1309 1309 i = Int()
1310 1310 j = Int()
1311 1311
1312 1312 def _i_default(self):
1313 1313 return 1
1314 1314
1315 1315 def _i_changed(self, name, old, new):
1316 1316 self.j = new
1317 1317
1318 1318 def test_pickle_hastraits():
1319 1319 c = Pickleable()
1320 1320 for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
1321 1321 p = pickle.dumps(c, protocol)
1322 1322 c2 = pickle.loads(p)
1323 1323 nt.assert_equal(c2.i, c.i)
1324 1324 nt.assert_equal(c2.j, c.j)
1325 1325
1326 1326 c.i = 5
1327 1327 for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
1328 1328 p = pickle.dumps(c, protocol)
1329 1329 c2 = pickle.loads(p)
1330 1330 nt.assert_equal(c2.i, c.i)
1331 1331 nt.assert_equal(c2.j, c.j)
1332 1332
1333 1333
1334 1334 def test_hold_trait_notifications():
1335 1335 changes = []
1336 1336 class Test(HasTraits):
1337 1337 a = Integer(0)
1338 1338 def _a_changed(self, name, old, new):
1339 1339 changes.append((old, new))
1340 1340
1341 1341 t = Test()
1342 1342 with t.hold_trait_notifications():
1343 1343 with t.hold_trait_notifications():
1344 1344 t.a = 1
1345 1345 nt.assert_equal(t.a, 1)
1346 1346 nt.assert_equal(changes, [])
1347 1347 t.a = 2
1348 1348 nt.assert_equal(t.a, 2)
1349 1349 with t.hold_trait_notifications():
1350 1350 t.a = 3
1351 1351 nt.assert_equal(t.a, 3)
1352 1352 nt.assert_equal(changes, [])
1353 1353 t.a = 4
1354 1354 nt.assert_equal(t.a, 4)
1355 1355 nt.assert_equal(changes, [])
1356 1356 t.a = 4
1357 1357 nt.assert_equal(t.a, 4)
1358 1358 nt.assert_equal(changes, [])
1359 nt.assert_equal(changes, [(0,1), (1,2), (2,3), (3,4)])
1359 nt.assert_equal(changes, [(3,4)])
1360 1360
1361 1361
1362 1362 class OrderTraits(HasTraits):
1363 1363 notified = Dict()
1364 1364
1365 1365 a = Unicode()
1366 1366 b = Unicode()
1367 1367 c = Unicode()
1368 1368 d = Unicode()
1369 1369 e = Unicode()
1370 1370 f = Unicode()
1371 1371 g = Unicode()
1372 1372 h = Unicode()
1373 1373 i = Unicode()
1374 1374 j = Unicode()
1375 1375 k = Unicode()
1376 1376 l = Unicode()
1377 1377
1378 1378 def _notify(self, name, old, new):
1379 1379 """check the value of all traits when each trait change is triggered
1380 1380
1381 1381 This verifies that the values are not sensitive
1382 1382 to dict ordering when loaded from kwargs
1383 1383 """
1384 1384 # check the value of the other traits
1385 1385 # when a given trait change notification fires
1386 1386 self.notified[name] = {
1387 1387 c: getattr(self, c) for c in 'abcdefghijkl'
1388 1388 }
1389 1389
1390 1390 def __init__(self, **kwargs):
1391 1391 self.on_trait_change(self._notify)
1392 1392 super(OrderTraits, self).__init__(**kwargs)
1393 1393
1394 1394 def test_notification_order():
1395 1395 d = {c:c for c in 'abcdefghijkl'}
1396 1396 obj = OrderTraits()
1397 1397 nt.assert_equal(obj.notified, {})
1398 1398 obj = OrderTraits(**d)
1399 1399 notifications = {
1400 1400 c: d for c in 'abcdefghijkl'
1401 1401 }
1402 1402 nt.assert_equal(obj.notified, notifications)
1403 1403
1404 1404
1405 1405 class TestEventful(TestCase):
1406 1406
1407 1407 def test_list(self):
1408 1408 """Does the EventfulList work?"""
1409 1409 event_cache = []
1410 1410
1411 1411 class A(HasTraits):
1412 1412 x = EventfulList([c for c in 'abc'])
1413 1413 a = A()
1414 1414 a.x.on_events(lambda i, x: event_cache.append('insert'), \
1415 1415 lambda i, x: event_cache.append('set'), \
1416 1416 lambda i: event_cache.append('del'), \
1417 1417 lambda: event_cache.append('reverse'), \
1418 1418 lambda *p, **k: event_cache.append('sort'))
1419 1419
1420 1420 a.x.remove('c')
1421 1421 # ab
1422 1422 a.x.insert(0, 'z')
1423 1423 # zab
1424 1424 del a.x[1]
1425 1425 # zb
1426 1426 a.x.reverse()
1427 1427 # bz
1428 1428 a.x[1] = 'o'
1429 1429 # bo
1430 1430 a.x.append('a')
1431 1431 # boa
1432 1432 a.x.sort()
1433 1433 # abo
1434 1434
1435 1435 # Were the correct events captured?
1436 1436 self.assertEqual(event_cache, ['del', 'insert', 'del', 'reverse', 'set', 'set', 'sort'])
1437 1437
1438 1438 # Is the output correct?
1439 1439 self.assertEqual(a.x, [c for c in 'abo'])
1440 1440
1441 1441 def test_dict(self):
1442 1442 """Does the EventfulDict work?"""
1443 1443 event_cache = []
1444 1444
1445 1445 class A(HasTraits):
1446 1446 x = EventfulDict({c: c for c in 'abc'})
1447 1447 a = A()
1448 1448 a.x.on_events(lambda k, v: event_cache.append('add'), \
1449 1449 lambda k, v: event_cache.append('set'), \
1450 1450 lambda k: event_cache.append('del'))
1451 1451
1452 1452 del a.x['c']
1453 1453 # ab
1454 1454 a.x['z'] = 1
1455 1455 # abz
1456 1456 a.x['z'] = 'z'
1457 1457 # abz
1458 1458 a.x.pop('a')
1459 1459 # bz
1460 1460
1461 1461 # Were the correct events captured?
1462 1462 self.assertEqual(event_cache, ['del', 'add', 'set', 'del'])
1463 1463
1464 1464 # Is the output correct?
1465 1465 self.assertEqual(a.x, {c: c for c in 'bz'})
1466 1466
1467 1467 ###
1468 1468 # Traits for Forward Declaration Tests
1469 1469 ###
1470 1470 class ForwardDeclaredInstanceTrait(HasTraits):
1471 1471
1472 1472 value = ForwardDeclaredInstance('ForwardDeclaredBar', allow_none=True)
1473 1473
1474 1474 class ForwardDeclaredTypeTrait(HasTraits):
1475 1475
1476 1476 value = ForwardDeclaredType('ForwardDeclaredBar', allow_none=True)
1477 1477
1478 1478 class ForwardDeclaredInstanceListTrait(HasTraits):
1479 1479
1480 1480 value = List(ForwardDeclaredInstance('ForwardDeclaredBar'))
1481 1481
1482 1482 class ForwardDeclaredTypeListTrait(HasTraits):
1483 1483
1484 1484 value = List(ForwardDeclaredType('ForwardDeclaredBar'))
1485 1485 ###
1486 1486 # End Traits for Forward Declaration Tests
1487 1487 ###
1488 1488
1489 1489 ###
1490 1490 # Classes for Forward Declaration Tests
1491 1491 ###
1492 1492 class ForwardDeclaredBar(object):
1493 1493 pass
1494 1494
1495 1495 class ForwardDeclaredBarSub(ForwardDeclaredBar):
1496 1496 pass
1497 1497 ###
1498 1498 # End Classes for Forward Declaration Tests
1499 1499 ###
1500 1500
1501 1501 ###
1502 1502 # Forward Declaration Tests
1503 1503 ###
1504 1504 class TestForwardDeclaredInstanceTrait(TraitTestBase):
1505 1505
1506 1506 obj = ForwardDeclaredInstanceTrait()
1507 1507 _default_value = None
1508 1508 _good_values = [None, ForwardDeclaredBar(), ForwardDeclaredBarSub()]
1509 1509 _bad_values = ['foo', 3, ForwardDeclaredBar, ForwardDeclaredBarSub]
1510 1510
1511 1511 class TestForwardDeclaredTypeTrait(TraitTestBase):
1512 1512
1513 1513 obj = ForwardDeclaredTypeTrait()
1514 1514 _default_value = None
1515 1515 _good_values = [None, ForwardDeclaredBar, ForwardDeclaredBarSub]
1516 1516 _bad_values = ['foo', 3, ForwardDeclaredBar(), ForwardDeclaredBarSub()]
1517 1517
1518 1518 class TestForwardDeclaredInstanceList(TraitTestBase):
1519 1519
1520 1520 obj = ForwardDeclaredInstanceListTrait()
1521 1521
1522 1522 def test_klass(self):
1523 1523 """Test that the instance klass is properly assigned."""
1524 1524 self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar)
1525 1525
1526 1526 _default_value = []
1527 1527 _good_values = [
1528 1528 [ForwardDeclaredBar(), ForwardDeclaredBarSub()],
1529 1529 [],
1530 1530 ]
1531 1531 _bad_values = [
1532 1532 ForwardDeclaredBar(),
1533 1533 [ForwardDeclaredBar(), 3, None],
1534 1534 '1',
1535 1535 # Note that this is the type, not an instance.
1536 1536 [ForwardDeclaredBar],
1537 1537 [None],
1538 1538 None,
1539 1539 ]
1540 1540
1541 1541 class TestForwardDeclaredTypeList(TraitTestBase):
1542 1542
1543 1543 obj = ForwardDeclaredTypeListTrait()
1544 1544
1545 1545 def test_klass(self):
1546 1546 """Test that the instance klass is properly assigned."""
1547 1547 self.assertIs(self.obj.traits()['value']._trait.klass, ForwardDeclaredBar)
1548 1548
1549 1549 _default_value = []
1550 1550 _good_values = [
1551 1551 [ForwardDeclaredBar, ForwardDeclaredBarSub],
1552 1552 [],
1553 1553 ]
1554 1554 _bad_values = [
1555 1555 ForwardDeclaredBar,
1556 1556 [ForwardDeclaredBar, 3],
1557 1557 '1',
1558 1558 # Note that this is an instance, not the type.
1559 1559 [ForwardDeclaredBar()],
1560 1560 [None],
1561 1561 None,
1562 1562 ]
1563 1563 ###
1564 1564 # End Forward Declaration Tests
1565 1565 ###
1566 1566
1567 1567 class TestDynamicTraits(TestCase):
1568 1568
1569 1569 def setUp(self):
1570 1570 self._notify1 = []
1571 1571
1572 1572 def notify1(self, name, old, new):
1573 1573 self._notify1.append((name, old, new))
1574 1574
1575 1575 def test_notify_all(self):
1576 1576
1577 1577 class A(HasTraits):
1578 1578 pass
1579 1579
1580 1580 a = A()
1581 1581 self.assertTrue(not hasattr(a, 'x'))
1582 1582 self.assertTrue(not hasattr(a, 'y'))
1583 1583
1584 1584 # Dynamically add trait x.
1585 1585 a.add_trait('x', Int())
1586 1586 self.assertTrue(hasattr(a, 'x'))
1587 1587 self.assertTrue(isinstance(a, (A, )))
1588 1588
1589 1589 # Dynamically add trait y.
1590 1590 a.add_trait('y', Float())
1591 1591 self.assertTrue(hasattr(a, 'y'))
1592 1592 self.assertTrue(isinstance(a, (A, )))
1593 1593 self.assertEqual(a.__class__.__name__, A.__name__)
1594 1594
1595 1595 # Create a new instance and verify that x and y
1596 1596 # aren't defined.
1597 1597 b = A()
1598 1598 self.assertTrue(not hasattr(b, 'x'))
1599 1599 self.assertTrue(not hasattr(b, 'y'))
1600 1600
1601 1601 # Verify that notification works like normal.
1602 1602 a.on_trait_change(self.notify1)
1603 1603 a.x = 0
1604 1604 self.assertEqual(len(self._notify1), 0)
1605 1605 a.y = 0.0
1606 1606 self.assertEqual(len(self._notify1), 0)
1607 1607 a.x = 10
1608 1608 self.assertTrue(('x', 0, 10) in self._notify1)
1609 1609 a.y = 10.0
1610 1610 self.assertTrue(('y', 0.0, 10.0) in self._notify1)
1611 1611 self.assertRaises(TraitError, setattr, a, 'x', 'bad string')
1612 1612 self.assertRaises(TraitError, setattr, a, 'y', 'bad string')
1613 1613 self._notify1 = []
1614 1614 a.on_trait_change(self.notify1, remove=True)
1615 1615 a.x = 20
1616 1616 a.y = 20.0
1617 1617 self.assertEqual(len(self._notify1), 0)
General Comments 0
You need to be logged in to leave comments. Login now