##// END OF EJS Templates
Skip the doc tests for Connect,...
Jonathan Frederic -
Show More
@@ -1,1479 +1,1481 b''
1 1 # encoding: utf-8
2 2 """
3 3 A lightweight Traits like module.
4 4
5 5 This is designed to provide a lightweight, simple, pure Python version of
6 6 many of the capabilities of enthought.traits. This includes:
7 7
8 8 * Validation
9 9 * Type specification with defaults
10 10 * Static and dynamic notification
11 11 * Basic predefined types
12 12 * An API that is similar to enthought.traits
13 13
14 14 We don't support:
15 15
16 16 * Delegation
17 17 * Automatic GUI generation
18 18 * A full set of trait types. Most importantly, we don't provide container
19 19 traits (list, dict, tuple) that can trigger notifications if their
20 20 contents change.
21 21 * API compatibility with enthought.traits
22 22
23 23 There are also some important difference in our design:
24 24
25 25 * enthought.traits does not validate default values. We do.
26 26
27 27 We choose to create this module because we need these capabilities, but
28 28 we need them to be pure Python so they work in all Python implementations,
29 29 including Jython and IronPython.
30 30
31 31 Inheritance diagram:
32 32
33 33 .. inheritance-diagram:: IPython.utils.traitlets
34 34 :parts: 3
35 35
36 36 Authors:
37 37
38 38 * Brian Granger
39 39 * Enthought, Inc. Some of the code in this file comes from enthought.traits
40 40 and is licensed under the BSD license. Also, many of the ideas also come
41 41 from enthought.traits even though our implementation is very different.
42 42 """
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Copyright (C) 2008-2011 The IPython Development Team
46 46 #
47 47 # Distributed under the terms of the BSD License. The full license is in
48 48 # the file COPYING, distributed as part of this software.
49 49 #-----------------------------------------------------------------------------
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Imports
53 53 #-----------------------------------------------------------------------------
54 54
55 55 import contextlib
56 56 import inspect
57 57 import re
58 58 import sys
59 59 import types
60 60 from types import FunctionType
61 61 try:
62 62 from types import ClassType, InstanceType
63 63 ClassTypes = (ClassType, type)
64 64 except:
65 65 ClassTypes = (type,)
66 66
67 67 from .importstring import import_item
68 68 from IPython.utils import py3compat
69 69 from IPython.utils.py3compat import iteritems
70 from IPython.testing.skipdoctest import skip_doctest
70 71
71 72 SequenceTypes = (list, tuple, set, frozenset)
72 73
73 74 #-----------------------------------------------------------------------------
74 75 # Basic classes
75 76 #-----------------------------------------------------------------------------
76 77
77 78
78 79 class NoDefaultSpecified ( object ): pass
79 80 NoDefaultSpecified = NoDefaultSpecified()
80 81
81 82
82 83 class Undefined ( object ): pass
83 84 Undefined = Undefined()
84 85
85 86 class TraitError(Exception):
86 87 pass
87 88
88 89 #-----------------------------------------------------------------------------
89 90 # Utilities
90 91 #-----------------------------------------------------------------------------
91 92
92 93
93 94 def class_of ( object ):
94 95 """ Returns a string containing the class name of an object with the
95 96 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
96 97 'a PlotValue').
97 98 """
98 99 if isinstance( object, py3compat.string_types ):
99 100 return add_article( object )
100 101
101 102 return add_article( object.__class__.__name__ )
102 103
103 104
104 105 def add_article ( name ):
105 106 """ Returns a string containing the correct indefinite article ('a' or 'an')
106 107 prefixed to the specified string.
107 108 """
108 109 if name[:1].lower() in 'aeiou':
109 110 return 'an ' + name
110 111
111 112 return 'a ' + name
112 113
113 114
114 115 def repr_type(obj):
115 116 """ Return a string representation of a value and its type for readable
116 117 error messages.
117 118 """
118 119 the_type = type(obj)
119 120 if (not py3compat.PY3) and the_type is InstanceType:
120 121 # Old-style class.
121 122 the_type = obj.__class__
122 123 msg = '%r %r' % (obj, the_type)
123 124 return msg
124 125
125 126
126 127 def is_trait(t):
127 128 """ Returns whether the given value is an instance or subclass of TraitType.
128 129 """
129 130 return (isinstance(t, TraitType) or
130 131 (isinstance(t, type) and issubclass(t, TraitType)))
131 132
132 133
133 134 def parse_notifier_name(name):
134 135 """Convert the name argument to a list of names.
135 136
136 137 Examples
137 138 --------
138 139
139 140 >>> parse_notifier_name('a')
140 141 ['a']
141 142 >>> parse_notifier_name(['a','b'])
142 143 ['a', 'b']
143 144 >>> parse_notifier_name(None)
144 145 ['anytrait']
145 146 """
146 147 if isinstance(name, str):
147 148 return [name]
148 149 elif name is None:
149 150 return ['anytrait']
150 151 elif isinstance(name, (list, tuple)):
151 152 for n in name:
152 153 assert isinstance(n, str), "names must be strings"
153 154 return name
154 155
155 156
156 157 class _SimpleTest:
157 158 def __init__ ( self, value ): self.value = value
158 159 def __call__ ( self, test ):
159 160 return test == self.value
160 161 def __repr__(self):
161 162 return "<SimpleTest(%r)" % self.value
162 163 def __str__(self):
163 164 return self.__repr__()
164 165
165 166
166 167 def getmembers(object, predicate=None):
167 168 """A safe version of inspect.getmembers that handles missing attributes.
168 169
169 170 This is useful when there are descriptor based attributes that for
170 171 some reason raise AttributeError even though they exist. This happens
171 172 in zope.inteface with the __provides__ attribute.
172 173 """
173 174 results = []
174 175 for key in dir(object):
175 176 try:
176 177 value = getattr(object, key)
177 178 except AttributeError:
178 179 pass
179 180 else:
180 181 if not predicate or predicate(value):
181 182 results.append((key, value))
182 183 results.sort()
183 184 return results
184 185
186 @skip_doctest
185 187 class Connect(object):
186 188 """Connect traits from different objects together so they remain in sync.
187 189
188 190 Parameters
189 191 ----------
190 192 obj : pairs of objects/attributes
191 193
192 194 Examples
193 195 --------
194 196
195 197 >>> c = Connect((obj1, 'value'), (obj2, 'value'), (obj3, 'value'))
196 198 >>> obj1.value = 5 # updates other objects as well
197 199 """
198 200 updating = False
199 201 def __init__(self, *args):
200 202 self.objects = args
201 203 for obj,attr in args:
202 204 obj.on_trait_change(self._update, attr)
203 205
204 206 @contextlib.contextmanager
205 207 def _busy_updating(self):
206 208 self.updating = True
207 209 try:
208 210 yield
209 211 finally:
210 212 self.updating = False
211 213
212 214 def _update(self, name, old, new):
213 215 if self.updating:
214 216 return
215 217 with self._busy_updating():
216 218 for obj,attr in self.objects:
217 219 setattr(obj, attr, new)
218 220
219 221 def disconnect(self):
220 222 for obj,attr in self.objects:
221 223 obj.on_trait_change(self._update, attr, remove=True)
222 224
223 225 #-----------------------------------------------------------------------------
224 226 # Base TraitType for all traits
225 227 #-----------------------------------------------------------------------------
226 228
227 229
228 230 class TraitType(object):
229 231 """A base class for all trait descriptors.
230 232
231 233 Notes
232 234 -----
233 235 Our implementation of traits is based on Python's descriptor
234 236 prototol. This class is the base class for all such descriptors. The
235 237 only magic we use is a custom metaclass for the main :class:`HasTraits`
236 238 class that does the following:
237 239
238 240 1. Sets the :attr:`name` attribute of every :class:`TraitType`
239 241 instance in the class dict to the name of the attribute.
240 242 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
241 243 instance in the class dict to the *class* that declared the trait.
242 244 This is used by the :class:`This` trait to allow subclasses to
243 245 accept superclasses for :class:`This` values.
244 246 """
245 247
246 248
247 249 metadata = {}
248 250 default_value = Undefined
249 251 info_text = 'any value'
250 252
251 253 def __init__(self, default_value=NoDefaultSpecified, **metadata):
252 254 """Create a TraitType.
253 255 """
254 256 if default_value is not NoDefaultSpecified:
255 257 self.default_value = default_value
256 258
257 259 if len(metadata) > 0:
258 260 if len(self.metadata) > 0:
259 261 self._metadata = self.metadata.copy()
260 262 self._metadata.update(metadata)
261 263 else:
262 264 self._metadata = metadata
263 265 else:
264 266 self._metadata = self.metadata
265 267
266 268 self.init()
267 269
268 270 def init(self):
269 271 pass
270 272
271 273 def get_default_value(self):
272 274 """Create a new instance of the default value."""
273 275 return self.default_value
274 276
275 277 def instance_init(self, obj):
276 278 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
277 279
278 280 Some stages of initialization must be delayed until the parent
279 281 :class:`HasTraits` instance has been created. This method is
280 282 called in :meth:`HasTraits.__new__` after the instance has been
281 283 created.
282 284
283 285 This method trigger the creation and validation of default values
284 286 and also things like the resolution of str given class names in
285 287 :class:`Type` and :class`Instance`.
286 288
287 289 Parameters
288 290 ----------
289 291 obj : :class:`HasTraits` instance
290 292 The parent :class:`HasTraits` instance that has just been
291 293 created.
292 294 """
293 295 self.set_default_value(obj)
294 296
295 297 def set_default_value(self, obj):
296 298 """Set the default value on a per instance basis.
297 299
298 300 This method is called by :meth:`instance_init` to create and
299 301 validate the default value. The creation and validation of
300 302 default values must be delayed until the parent :class:`HasTraits`
301 303 class has been instantiated.
302 304 """
303 305 # Check for a deferred initializer defined in the same class as the
304 306 # trait declaration or above.
305 307 mro = type(obj).mro()
306 308 meth_name = '_%s_default' % self.name
307 309 for cls in mro[:mro.index(self.this_class)+1]:
308 310 if meth_name in cls.__dict__:
309 311 break
310 312 else:
311 313 # We didn't find one. Do static initialization.
312 314 dv = self.get_default_value()
313 315 newdv = self._validate(obj, dv)
314 316 obj._trait_values[self.name] = newdv
315 317 return
316 318 # Complete the dynamic initialization.
317 319 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
318 320
319 321 def __get__(self, obj, cls=None):
320 322 """Get the value of the trait by self.name for the instance.
321 323
322 324 Default values are instantiated when :meth:`HasTraits.__new__`
323 325 is called. Thus by the time this method gets called either the
324 326 default value or a user defined value (they called :meth:`__set__`)
325 327 is in the :class:`HasTraits` instance.
326 328 """
327 329 if obj is None:
328 330 return self
329 331 else:
330 332 try:
331 333 value = obj._trait_values[self.name]
332 334 except KeyError:
333 335 # Check for a dynamic initializer.
334 336 if self.name in obj._trait_dyn_inits:
335 337 value = obj._trait_dyn_inits[self.name](obj)
336 338 # FIXME: Do we really validate here?
337 339 value = self._validate(obj, value)
338 340 obj._trait_values[self.name] = value
339 341 return value
340 342 else:
341 343 raise TraitError('Unexpected error in TraitType: '
342 344 'both default value and dynamic initializer are '
343 345 'absent.')
344 346 except Exception:
345 347 # HasTraits should call set_default_value to populate
346 348 # this. So this should never be reached.
347 349 raise TraitError('Unexpected error in TraitType: '
348 350 'default value not set properly')
349 351 else:
350 352 return value
351 353
352 354 def __set__(self, obj, value):
353 355 new_value = self._validate(obj, value)
354 356 old_value = self.__get__(obj)
355 357 obj._trait_values[self.name] = new_value
356 358 if old_value != new_value:
357 359 obj._notify_trait(self.name, old_value, new_value)
358 360
359 361 def _validate(self, obj, value):
360 362 if hasattr(self, 'validate'):
361 363 return self.validate(obj, value)
362 364 elif hasattr(self, 'is_valid_for'):
363 365 valid = self.is_valid_for(value)
364 366 if valid:
365 367 return value
366 368 else:
367 369 raise TraitError('invalid value for type: %r' % value)
368 370 elif hasattr(self, 'value_for'):
369 371 return self.value_for(value)
370 372 else:
371 373 return value
372 374
373 375 def info(self):
374 376 return self.info_text
375 377
376 378 def error(self, obj, value):
377 379 if obj is not None:
378 380 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
379 381 % (self.name, class_of(obj),
380 382 self.info(), repr_type(value))
381 383 else:
382 384 e = "The '%s' trait must be %s, but a value of %r was specified." \
383 385 % (self.name, self.info(), repr_type(value))
384 386 raise TraitError(e)
385 387
386 388 def get_metadata(self, key):
387 389 return getattr(self, '_metadata', {}).get(key, None)
388 390
389 391 def set_metadata(self, key, value):
390 392 getattr(self, '_metadata', {})[key] = value
391 393
392 394
393 395 #-----------------------------------------------------------------------------
394 396 # The HasTraits implementation
395 397 #-----------------------------------------------------------------------------
396 398
397 399
398 400 class MetaHasTraits(type):
399 401 """A metaclass for HasTraits.
400 402
401 403 This metaclass makes sure that any TraitType class attributes are
402 404 instantiated and sets their name attribute.
403 405 """
404 406
405 407 def __new__(mcls, name, bases, classdict):
406 408 """Create the HasTraits class.
407 409
408 410 This instantiates all TraitTypes in the class dict and sets their
409 411 :attr:`name` attribute.
410 412 """
411 413 # print "MetaHasTraitlets (mcls, name): ", mcls, name
412 414 # print "MetaHasTraitlets (bases): ", bases
413 415 # print "MetaHasTraitlets (classdict): ", classdict
414 416 for k,v in iteritems(classdict):
415 417 if isinstance(v, TraitType):
416 418 v.name = k
417 419 elif inspect.isclass(v):
418 420 if issubclass(v, TraitType):
419 421 vinst = v()
420 422 vinst.name = k
421 423 classdict[k] = vinst
422 424 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
423 425
424 426 def __init__(cls, name, bases, classdict):
425 427 """Finish initializing the HasTraits class.
426 428
427 429 This sets the :attr:`this_class` attribute of each TraitType in the
428 430 class dict to the newly created class ``cls``.
429 431 """
430 432 for k, v in iteritems(classdict):
431 433 if isinstance(v, TraitType):
432 434 v.this_class = cls
433 435 super(MetaHasTraits, cls).__init__(name, bases, classdict)
434 436
435 437 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
436 438
437 439 def __new__(cls, *args, **kw):
438 440 # This is needed because in Python 2.6 object.__new__ only accepts
439 441 # the cls argument.
440 442 new_meth = super(HasTraits, cls).__new__
441 443 if new_meth is object.__new__:
442 444 inst = new_meth(cls)
443 445 else:
444 446 inst = new_meth(cls, **kw)
445 447 inst._trait_values = {}
446 448 inst._trait_notifiers = {}
447 449 inst._trait_dyn_inits = {}
448 450 # Here we tell all the TraitType instances to set their default
449 451 # values on the instance.
450 452 for key in dir(cls):
451 453 # Some descriptors raise AttributeError like zope.interface's
452 454 # __provides__ attributes even though they exist. This causes
453 455 # AttributeErrors even though they are listed in dir(cls).
454 456 try:
455 457 value = getattr(cls, key)
456 458 except AttributeError:
457 459 pass
458 460 else:
459 461 if isinstance(value, TraitType):
460 462 value.instance_init(inst)
461 463
462 464 return inst
463 465
464 466 def __init__(self, *args, **kw):
465 467 # Allow trait values to be set using keyword arguments.
466 468 # We need to use setattr for this to trigger validation and
467 469 # notifications.
468 470 for key, value in iteritems(kw):
469 471 setattr(self, key, value)
470 472
471 473 def _notify_trait(self, name, old_value, new_value):
472 474
473 475 # First dynamic ones
474 476 callables = []
475 477 callables.extend(self._trait_notifiers.get(name,[]))
476 478 callables.extend(self._trait_notifiers.get('anytrait',[]))
477 479
478 480 # Now static ones
479 481 try:
480 482 cb = getattr(self, '_%s_changed' % name)
481 483 except:
482 484 pass
483 485 else:
484 486 callables.append(cb)
485 487
486 488 # Call them all now
487 489 for c in callables:
488 490 # Traits catches and logs errors here. I allow them to raise
489 491 if callable(c):
490 492 argspec = inspect.getargspec(c)
491 493 nargs = len(argspec[0])
492 494 # Bound methods have an additional 'self' argument
493 495 # I don't know how to treat unbound methods, but they
494 496 # can't really be used for callbacks.
495 497 if isinstance(c, types.MethodType):
496 498 offset = -1
497 499 else:
498 500 offset = 0
499 501 if nargs + offset == 0:
500 502 c()
501 503 elif nargs + offset == 1:
502 504 c(name)
503 505 elif nargs + offset == 2:
504 506 c(name, new_value)
505 507 elif nargs + offset == 3:
506 508 c(name, old_value, new_value)
507 509 else:
508 510 raise TraitError('a trait changed callback '
509 511 'must have 0-3 arguments.')
510 512 else:
511 513 raise TraitError('a trait changed callback '
512 514 'must be callable.')
513 515
514 516
515 517 def _add_notifiers(self, handler, name):
516 518 if name not in self._trait_notifiers:
517 519 nlist = []
518 520 self._trait_notifiers[name] = nlist
519 521 else:
520 522 nlist = self._trait_notifiers[name]
521 523 if handler not in nlist:
522 524 nlist.append(handler)
523 525
524 526 def _remove_notifiers(self, handler, name):
525 527 if name in self._trait_notifiers:
526 528 nlist = self._trait_notifiers[name]
527 529 try:
528 530 index = nlist.index(handler)
529 531 except ValueError:
530 532 pass
531 533 else:
532 534 del nlist[index]
533 535
534 536 def on_trait_change(self, handler, name=None, remove=False):
535 537 """Setup a handler to be called when a trait changes.
536 538
537 539 This is used to setup dynamic notifications of trait changes.
538 540
539 541 Static handlers can be created by creating methods on a HasTraits
540 542 subclass with the naming convention '_[traitname]_changed'. Thus,
541 543 to create static handler for the trait 'a', create the method
542 544 _a_changed(self, name, old, new) (fewer arguments can be used, see
543 545 below).
544 546
545 547 Parameters
546 548 ----------
547 549 handler : callable
548 550 A callable that is called when a trait changes. Its
549 551 signature can be handler(), handler(name), handler(name, new)
550 552 or handler(name, old, new).
551 553 name : list, str, None
552 554 If None, the handler will apply to all traits. If a list
553 555 of str, handler will apply to all names in the list. If a
554 556 str, the handler will apply just to that name.
555 557 remove : bool
556 558 If False (the default), then install the handler. If True
557 559 then unintall it.
558 560 """
559 561 if remove:
560 562 names = parse_notifier_name(name)
561 563 for n in names:
562 564 self._remove_notifiers(handler, n)
563 565 else:
564 566 names = parse_notifier_name(name)
565 567 for n in names:
566 568 self._add_notifiers(handler, n)
567 569
568 570 @classmethod
569 571 def class_trait_names(cls, **metadata):
570 572 """Get a list of all the names of this classes traits.
571 573
572 574 This method is just like the :meth:`trait_names` method, but is unbound.
573 575 """
574 576 return cls.class_traits(**metadata).keys()
575 577
576 578 @classmethod
577 579 def class_traits(cls, **metadata):
578 580 """Get a list of all the traits of this class.
579 581
580 582 This method is just like the :meth:`traits` method, but is unbound.
581 583
582 584 The TraitTypes returned don't know anything about the values
583 585 that the various HasTrait's instances are holding.
584 586
585 587 This follows the same algorithm as traits does and does not allow
586 588 for any simple way of specifying merely that a metadata name
587 589 exists, but has any value. This is because get_metadata returns
588 590 None if a metadata key doesn't exist.
589 591 """
590 592 traits = dict([memb for memb in getmembers(cls) if \
591 593 isinstance(memb[1], TraitType)])
592 594
593 595 if len(metadata) == 0:
594 596 return traits
595 597
596 598 for meta_name, meta_eval in metadata.items():
597 599 if type(meta_eval) is not FunctionType:
598 600 metadata[meta_name] = _SimpleTest(meta_eval)
599 601
600 602 result = {}
601 603 for name, trait in traits.items():
602 604 for meta_name, meta_eval in metadata.items():
603 605 if not meta_eval(trait.get_metadata(meta_name)):
604 606 break
605 607 else:
606 608 result[name] = trait
607 609
608 610 return result
609 611
610 612 def trait_names(self, **metadata):
611 613 """Get a list of all the names of this classes traits."""
612 614 return self.traits(**metadata).keys()
613 615
614 616 def traits(self, **metadata):
615 617 """Get a list of all the traits of this class.
616 618
617 619 The TraitTypes returned don't know anything about the values
618 620 that the various HasTrait's instances are holding.
619 621
620 622 This follows the same algorithm as traits does and does not allow
621 623 for any simple way of specifying merely that a metadata name
622 624 exists, but has any value. This is because get_metadata returns
623 625 None if a metadata key doesn't exist.
624 626 """
625 627 traits = dict([memb for memb in getmembers(self.__class__) if \
626 628 isinstance(memb[1], TraitType)])
627 629
628 630 if len(metadata) == 0:
629 631 return traits
630 632
631 633 for meta_name, meta_eval in metadata.items():
632 634 if type(meta_eval) is not FunctionType:
633 635 metadata[meta_name] = _SimpleTest(meta_eval)
634 636
635 637 result = {}
636 638 for name, trait in traits.items():
637 639 for meta_name, meta_eval in metadata.items():
638 640 if not meta_eval(trait.get_metadata(meta_name)):
639 641 break
640 642 else:
641 643 result[name] = trait
642 644
643 645 return result
644 646
645 647 def trait_metadata(self, traitname, key):
646 648 """Get metadata values for trait by key."""
647 649 try:
648 650 trait = getattr(self.__class__, traitname)
649 651 except AttributeError:
650 652 raise TraitError("Class %s does not have a trait named %s" %
651 653 (self.__class__.__name__, traitname))
652 654 else:
653 655 return trait.get_metadata(key)
654 656
655 657 #-----------------------------------------------------------------------------
656 658 # Actual TraitTypes implementations/subclasses
657 659 #-----------------------------------------------------------------------------
658 660
659 661 #-----------------------------------------------------------------------------
660 662 # TraitTypes subclasses for handling classes and instances of classes
661 663 #-----------------------------------------------------------------------------
662 664
663 665
664 666 class ClassBasedTraitType(TraitType):
665 667 """A trait with error reporting for Type, Instance and This."""
666 668
667 669 def error(self, obj, value):
668 670 kind = type(value)
669 671 if (not py3compat.PY3) and kind is InstanceType:
670 672 msg = 'class %s' % value.__class__.__name__
671 673 else:
672 674 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
673 675
674 676 if obj is not None:
675 677 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
676 678 % (self.name, class_of(obj),
677 679 self.info(), msg)
678 680 else:
679 681 e = "The '%s' trait must be %s, but a value of %r was specified." \
680 682 % (self.name, self.info(), msg)
681 683
682 684 raise TraitError(e)
683 685
684 686
685 687 class Type(ClassBasedTraitType):
686 688 """A trait whose value must be a subclass of a specified class."""
687 689
688 690 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
689 691 """Construct a Type trait
690 692
691 693 A Type trait specifies that its values must be subclasses of
692 694 a particular class.
693 695
694 696 If only ``default_value`` is given, it is used for the ``klass`` as
695 697 well.
696 698
697 699 Parameters
698 700 ----------
699 701 default_value : class, str or None
700 702 The default value must be a subclass of klass. If an str,
701 703 the str must be a fully specified class name, like 'foo.bar.Bah'.
702 704 The string is resolved into real class, when the parent
703 705 :class:`HasTraits` class is instantiated.
704 706 klass : class, str, None
705 707 Values of this trait must be a subclass of klass. The klass
706 708 may be specified in a string like: 'foo.bar.MyClass'.
707 709 The string is resolved into real class, when the parent
708 710 :class:`HasTraits` class is instantiated.
709 711 allow_none : boolean
710 712 Indicates whether None is allowed as an assignable value. Even if
711 713 ``False``, the default value may be ``None``.
712 714 """
713 715 if default_value is None:
714 716 if klass is None:
715 717 klass = object
716 718 elif klass is None:
717 719 klass = default_value
718 720
719 721 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
720 722 raise TraitError("A Type trait must specify a class.")
721 723
722 724 self.klass = klass
723 725 self._allow_none = allow_none
724 726
725 727 super(Type, self).__init__(default_value, **metadata)
726 728
727 729 def validate(self, obj, value):
728 730 """Validates that the value is a valid object instance."""
729 731 try:
730 732 if issubclass(value, self.klass):
731 733 return value
732 734 except:
733 735 if (value is None) and (self._allow_none):
734 736 return value
735 737
736 738 self.error(obj, value)
737 739
738 740 def info(self):
739 741 """ Returns a description of the trait."""
740 742 if isinstance(self.klass, py3compat.string_types):
741 743 klass = self.klass
742 744 else:
743 745 klass = self.klass.__name__
744 746 result = 'a subclass of ' + klass
745 747 if self._allow_none:
746 748 return result + ' or None'
747 749 return result
748 750
749 751 def instance_init(self, obj):
750 752 self._resolve_classes()
751 753 super(Type, self).instance_init(obj)
752 754
753 755 def _resolve_classes(self):
754 756 if isinstance(self.klass, py3compat.string_types):
755 757 self.klass = import_item(self.klass)
756 758 if isinstance(self.default_value, py3compat.string_types):
757 759 self.default_value = import_item(self.default_value)
758 760
759 761 def get_default_value(self):
760 762 return self.default_value
761 763
762 764
763 765 class DefaultValueGenerator(object):
764 766 """A class for generating new default value instances."""
765 767
766 768 def __init__(self, *args, **kw):
767 769 self.args = args
768 770 self.kw = kw
769 771
770 772 def generate(self, klass):
771 773 return klass(*self.args, **self.kw)
772 774
773 775
774 776 class Instance(ClassBasedTraitType):
775 777 """A trait whose value must be an instance of a specified class.
776 778
777 779 The value can also be an instance of a subclass of the specified class.
778 780 """
779 781
780 782 def __init__(self, klass=None, args=None, kw=None,
781 783 allow_none=True, **metadata ):
782 784 """Construct an Instance trait.
783 785
784 786 This trait allows values that are instances of a particular
785 787 class or its sublclasses. Our implementation is quite different
786 788 from that of enthough.traits as we don't allow instances to be used
787 789 for klass and we handle the ``args`` and ``kw`` arguments differently.
788 790
789 791 Parameters
790 792 ----------
791 793 klass : class, str
792 794 The class that forms the basis for the trait. Class names
793 795 can also be specified as strings, like 'foo.bar.Bar'.
794 796 args : tuple
795 797 Positional arguments for generating the default value.
796 798 kw : dict
797 799 Keyword arguments for generating the default value.
798 800 allow_none : bool
799 801 Indicates whether None is allowed as a value.
800 802
801 803 Notes
802 804 -----
803 805 If both ``args`` and ``kw`` are None, then the default value is None.
804 806 If ``args`` is a tuple and ``kw`` is a dict, then the default is
805 807 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
806 808 not (but not both), None is replace by ``()`` or ``{}``.
807 809 """
808 810
809 811 self._allow_none = allow_none
810 812
811 813 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types))):
812 814 raise TraitError('The klass argument must be a class'
813 815 ' you gave: %r' % klass)
814 816 self.klass = klass
815 817
816 818 # self.klass is a class, so handle default_value
817 819 if args is None and kw is None:
818 820 default_value = None
819 821 else:
820 822 if args is None:
821 823 # kw is not None
822 824 args = ()
823 825 elif kw is None:
824 826 # args is not None
825 827 kw = {}
826 828
827 829 if not isinstance(kw, dict):
828 830 raise TraitError("The 'kw' argument must be a dict or None.")
829 831 if not isinstance(args, tuple):
830 832 raise TraitError("The 'args' argument must be a tuple or None.")
831 833
832 834 default_value = DefaultValueGenerator(*args, **kw)
833 835
834 836 super(Instance, self).__init__(default_value, **metadata)
835 837
836 838 def validate(self, obj, value):
837 839 if value is None:
838 840 if self._allow_none:
839 841 return value
840 842 self.error(obj, value)
841 843
842 844 if isinstance(value, self.klass):
843 845 return value
844 846 else:
845 847 self.error(obj, value)
846 848
847 849 def info(self):
848 850 if isinstance(self.klass, py3compat.string_types):
849 851 klass = self.klass
850 852 else:
851 853 klass = self.klass.__name__
852 854 result = class_of(klass)
853 855 if self._allow_none:
854 856 return result + ' or None'
855 857
856 858 return result
857 859
858 860 def instance_init(self, obj):
859 861 self._resolve_classes()
860 862 super(Instance, self).instance_init(obj)
861 863
862 864 def _resolve_classes(self):
863 865 if isinstance(self.klass, py3compat.string_types):
864 866 self.klass = import_item(self.klass)
865 867
866 868 def get_default_value(self):
867 869 """Instantiate a default value instance.
868 870
869 871 This is called when the containing HasTraits classes'
870 872 :meth:`__new__` method is called to ensure that a unique instance
871 873 is created for each HasTraits instance.
872 874 """
873 875 dv = self.default_value
874 876 if isinstance(dv, DefaultValueGenerator):
875 877 return dv.generate(self.klass)
876 878 else:
877 879 return dv
878 880
879 881
880 882 class This(ClassBasedTraitType):
881 883 """A trait for instances of the class containing this trait.
882 884
883 885 Because how how and when class bodies are executed, the ``This``
884 886 trait can only have a default value of None. This, and because we
885 887 always validate default values, ``allow_none`` is *always* true.
886 888 """
887 889
888 890 info_text = 'an instance of the same type as the receiver or None'
889 891
890 892 def __init__(self, **metadata):
891 893 super(This, self).__init__(None, **metadata)
892 894
893 895 def validate(self, obj, value):
894 896 # What if value is a superclass of obj.__class__? This is
895 897 # complicated if it was the superclass that defined the This
896 898 # trait.
897 899 if isinstance(value, self.this_class) or (value is None):
898 900 return value
899 901 else:
900 902 self.error(obj, value)
901 903
902 904
903 905 #-----------------------------------------------------------------------------
904 906 # Basic TraitTypes implementations/subclasses
905 907 #-----------------------------------------------------------------------------
906 908
907 909
908 910 class Any(TraitType):
909 911 default_value = None
910 912 info_text = 'any value'
911 913
912 914
913 915 class Int(TraitType):
914 916 """An int trait."""
915 917
916 918 default_value = 0
917 919 info_text = 'an int'
918 920
919 921 def validate(self, obj, value):
920 922 if isinstance(value, int):
921 923 return value
922 924 self.error(obj, value)
923 925
924 926 class CInt(Int):
925 927 """A casting version of the int trait."""
926 928
927 929 def validate(self, obj, value):
928 930 try:
929 931 return int(value)
930 932 except:
931 933 self.error(obj, value)
932 934
933 935 if py3compat.PY3:
934 936 Long, CLong = Int, CInt
935 937 Integer = Int
936 938 else:
937 939 class Long(TraitType):
938 940 """A long integer trait."""
939 941
940 942 default_value = 0
941 943 info_text = 'a long'
942 944
943 945 def validate(self, obj, value):
944 946 if isinstance(value, long):
945 947 return value
946 948 if isinstance(value, int):
947 949 return long(value)
948 950 self.error(obj, value)
949 951
950 952
951 953 class CLong(Long):
952 954 """A casting version of the long integer trait."""
953 955
954 956 def validate(self, obj, value):
955 957 try:
956 958 return long(value)
957 959 except:
958 960 self.error(obj, value)
959 961
960 962 class Integer(TraitType):
961 963 """An integer trait.
962 964
963 965 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
964 966
965 967 default_value = 0
966 968 info_text = 'an integer'
967 969
968 970 def validate(self, obj, value):
969 971 if isinstance(value, int):
970 972 return value
971 973 if isinstance(value, long):
972 974 # downcast longs that fit in int:
973 975 # note that int(n > sys.maxint) returns a long, so
974 976 # we don't need a condition on this cast
975 977 return int(value)
976 978 if sys.platform == "cli":
977 979 from System import Int64
978 980 if isinstance(value, Int64):
979 981 return int(value)
980 982 self.error(obj, value)
981 983
982 984
983 985 class Float(TraitType):
984 986 """A float trait."""
985 987
986 988 default_value = 0.0
987 989 info_text = 'a float'
988 990
989 991 def validate(self, obj, value):
990 992 if isinstance(value, float):
991 993 return value
992 994 if isinstance(value, int):
993 995 return float(value)
994 996 self.error(obj, value)
995 997
996 998
997 999 class CFloat(Float):
998 1000 """A casting version of the float trait."""
999 1001
1000 1002 def validate(self, obj, value):
1001 1003 try:
1002 1004 return float(value)
1003 1005 except:
1004 1006 self.error(obj, value)
1005 1007
1006 1008 class Complex(TraitType):
1007 1009 """A trait for complex numbers."""
1008 1010
1009 1011 default_value = 0.0 + 0.0j
1010 1012 info_text = 'a complex number'
1011 1013
1012 1014 def validate(self, obj, value):
1013 1015 if isinstance(value, complex):
1014 1016 return value
1015 1017 if isinstance(value, (float, int)):
1016 1018 return complex(value)
1017 1019 self.error(obj, value)
1018 1020
1019 1021
1020 1022 class CComplex(Complex):
1021 1023 """A casting version of the complex number trait."""
1022 1024
1023 1025 def validate (self, obj, value):
1024 1026 try:
1025 1027 return complex(value)
1026 1028 except:
1027 1029 self.error(obj, value)
1028 1030
1029 1031 # We should always be explicit about whether we're using bytes or unicode, both
1030 1032 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1031 1033 # we don't have a Str type.
1032 1034 class Bytes(TraitType):
1033 1035 """A trait for byte strings."""
1034 1036
1035 1037 default_value = b''
1036 1038 info_text = 'a bytes object'
1037 1039
1038 1040 def validate(self, obj, value):
1039 1041 if isinstance(value, bytes):
1040 1042 return value
1041 1043 self.error(obj, value)
1042 1044
1043 1045
1044 1046 class CBytes(Bytes):
1045 1047 """A casting version of the byte string trait."""
1046 1048
1047 1049 def validate(self, obj, value):
1048 1050 try:
1049 1051 return bytes(value)
1050 1052 except:
1051 1053 self.error(obj, value)
1052 1054
1053 1055
1054 1056 class Unicode(TraitType):
1055 1057 """A trait for unicode strings."""
1056 1058
1057 1059 default_value = u''
1058 1060 info_text = 'a unicode string'
1059 1061
1060 1062 def validate(self, obj, value):
1061 1063 if isinstance(value, py3compat.unicode_type):
1062 1064 return value
1063 1065 if isinstance(value, bytes):
1064 1066 try:
1065 1067 return value.decode('ascii', 'strict')
1066 1068 except UnicodeDecodeError:
1067 1069 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1068 1070 raise TraitError(msg.format(value, self.name, class_of(obj)))
1069 1071 self.error(obj, value)
1070 1072
1071 1073
1072 1074 class CUnicode(Unicode):
1073 1075 """A casting version of the unicode trait."""
1074 1076
1075 1077 def validate(self, obj, value):
1076 1078 try:
1077 1079 return py3compat.unicode_type(value)
1078 1080 except:
1079 1081 self.error(obj, value)
1080 1082
1081 1083
1082 1084 class ObjectName(TraitType):
1083 1085 """A string holding a valid object name in this version of Python.
1084 1086
1085 1087 This does not check that the name exists in any scope."""
1086 1088 info_text = "a valid object identifier in Python"
1087 1089
1088 1090 if py3compat.PY3:
1089 1091 # Python 3:
1090 1092 coerce_str = staticmethod(lambda _,s: s)
1091 1093
1092 1094 else:
1093 1095 # Python 2:
1094 1096 def coerce_str(self, obj, value):
1095 1097 "In Python 2, coerce ascii-only unicode to str"
1096 1098 if isinstance(value, unicode):
1097 1099 try:
1098 1100 return str(value)
1099 1101 except UnicodeEncodeError:
1100 1102 self.error(obj, value)
1101 1103 return value
1102 1104
1103 1105 def validate(self, obj, value):
1104 1106 value = self.coerce_str(obj, value)
1105 1107
1106 1108 if isinstance(value, str) and py3compat.isidentifier(value):
1107 1109 return value
1108 1110 self.error(obj, value)
1109 1111
1110 1112 class DottedObjectName(ObjectName):
1111 1113 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1112 1114 def validate(self, obj, value):
1113 1115 value = self.coerce_str(obj, value)
1114 1116
1115 1117 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1116 1118 return value
1117 1119 self.error(obj, value)
1118 1120
1119 1121
1120 1122 class Bool(TraitType):
1121 1123 """A boolean (True, False) trait."""
1122 1124
1123 1125 default_value = False
1124 1126 info_text = 'a boolean'
1125 1127
1126 1128 def validate(self, obj, value):
1127 1129 if isinstance(value, bool):
1128 1130 return value
1129 1131 self.error(obj, value)
1130 1132
1131 1133
1132 1134 class CBool(Bool):
1133 1135 """A casting version of the boolean trait."""
1134 1136
1135 1137 def validate(self, obj, value):
1136 1138 try:
1137 1139 return bool(value)
1138 1140 except:
1139 1141 self.error(obj, value)
1140 1142
1141 1143
1142 1144 class Enum(TraitType):
1143 1145 """An enum that whose value must be in a given sequence."""
1144 1146
1145 1147 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1146 1148 self.values = values
1147 1149 self._allow_none = allow_none
1148 1150 super(Enum, self).__init__(default_value, **metadata)
1149 1151
1150 1152 def validate(self, obj, value):
1151 1153 if value is None:
1152 1154 if self._allow_none:
1153 1155 return value
1154 1156
1155 1157 if value in self.values:
1156 1158 return value
1157 1159 self.error(obj, value)
1158 1160
1159 1161 def info(self):
1160 1162 """ Returns a description of the trait."""
1161 1163 result = 'any of ' + repr(self.values)
1162 1164 if self._allow_none:
1163 1165 return result + ' or None'
1164 1166 return result
1165 1167
1166 1168 class CaselessStrEnum(Enum):
1167 1169 """An enum of strings that are caseless in validate."""
1168 1170
1169 1171 def validate(self, obj, value):
1170 1172 if value is None:
1171 1173 if self._allow_none:
1172 1174 return value
1173 1175
1174 1176 if not isinstance(value, py3compat.string_types):
1175 1177 self.error(obj, value)
1176 1178
1177 1179 for v in self.values:
1178 1180 if v.lower() == value.lower():
1179 1181 return v
1180 1182 self.error(obj, value)
1181 1183
1182 1184 class Container(Instance):
1183 1185 """An instance of a container (list, set, etc.)
1184 1186
1185 1187 To be subclassed by overriding klass.
1186 1188 """
1187 1189 klass = None
1188 1190 _valid_defaults = SequenceTypes
1189 1191 _trait = None
1190 1192
1191 1193 def __init__(self, trait=None, default_value=None, allow_none=True,
1192 1194 **metadata):
1193 1195 """Create a container trait type from a list, set, or tuple.
1194 1196
1195 1197 The default value is created by doing ``List(default_value)``,
1196 1198 which creates a copy of the ``default_value``.
1197 1199
1198 1200 ``trait`` can be specified, which restricts the type of elements
1199 1201 in the container to that TraitType.
1200 1202
1201 1203 If only one arg is given and it is not a Trait, it is taken as
1202 1204 ``default_value``:
1203 1205
1204 1206 ``c = List([1,2,3])``
1205 1207
1206 1208 Parameters
1207 1209 ----------
1208 1210
1209 1211 trait : TraitType [ optional ]
1210 1212 the type for restricting the contents of the Container. If unspecified,
1211 1213 types are not checked.
1212 1214
1213 1215 default_value : SequenceType [ optional ]
1214 1216 The default value for the Trait. Must be list/tuple/set, and
1215 1217 will be cast to the container type.
1216 1218
1217 1219 allow_none : Bool [ default True ]
1218 1220 Whether to allow the value to be None
1219 1221
1220 1222 **metadata : any
1221 1223 further keys for extensions to the Trait (e.g. config)
1222 1224
1223 1225 """
1224 1226 # allow List([values]):
1225 1227 if default_value is None and not is_trait(trait):
1226 1228 default_value = trait
1227 1229 trait = None
1228 1230
1229 1231 if default_value is None:
1230 1232 args = ()
1231 1233 elif isinstance(default_value, self._valid_defaults):
1232 1234 args = (default_value,)
1233 1235 else:
1234 1236 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1235 1237
1236 1238 if is_trait(trait):
1237 1239 self._trait = trait() if isinstance(trait, type) else trait
1238 1240 self._trait.name = 'element'
1239 1241 elif trait is not None:
1240 1242 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1241 1243
1242 1244 super(Container,self).__init__(klass=self.klass, args=args,
1243 1245 allow_none=allow_none, **metadata)
1244 1246
1245 1247 def element_error(self, obj, element, validator):
1246 1248 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1247 1249 % (self.name, class_of(obj), validator.info(), repr_type(element))
1248 1250 raise TraitError(e)
1249 1251
1250 1252 def validate(self, obj, value):
1251 1253 value = super(Container, self).validate(obj, value)
1252 1254 if value is None:
1253 1255 return value
1254 1256
1255 1257 value = self.validate_elements(obj, value)
1256 1258
1257 1259 return value
1258 1260
1259 1261 def validate_elements(self, obj, value):
1260 1262 validated = []
1261 1263 if self._trait is None or isinstance(self._trait, Any):
1262 1264 return value
1263 1265 for v in value:
1264 1266 try:
1265 1267 v = self._trait.validate(obj, v)
1266 1268 except TraitError:
1267 1269 self.element_error(obj, v, self._trait)
1268 1270 else:
1269 1271 validated.append(v)
1270 1272 return self.klass(validated)
1271 1273
1272 1274
1273 1275 class List(Container):
1274 1276 """An instance of a Python list."""
1275 1277 klass = list
1276 1278
1277 1279 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1278 1280 allow_none=True, **metadata):
1279 1281 """Create a List trait type from a list, set, or tuple.
1280 1282
1281 1283 The default value is created by doing ``List(default_value)``,
1282 1284 which creates a copy of the ``default_value``.
1283 1285
1284 1286 ``trait`` can be specified, which restricts the type of elements
1285 1287 in the container to that TraitType.
1286 1288
1287 1289 If only one arg is given and it is not a Trait, it is taken as
1288 1290 ``default_value``:
1289 1291
1290 1292 ``c = List([1,2,3])``
1291 1293
1292 1294 Parameters
1293 1295 ----------
1294 1296
1295 1297 trait : TraitType [ optional ]
1296 1298 the type for restricting the contents of the Container. If unspecified,
1297 1299 types are not checked.
1298 1300
1299 1301 default_value : SequenceType [ optional ]
1300 1302 The default value for the Trait. Must be list/tuple/set, and
1301 1303 will be cast to the container type.
1302 1304
1303 1305 minlen : Int [ default 0 ]
1304 1306 The minimum length of the input list
1305 1307
1306 1308 maxlen : Int [ default sys.maxsize ]
1307 1309 The maximum length of the input list
1308 1310
1309 1311 allow_none : Bool [ default True ]
1310 1312 Whether to allow the value to be None
1311 1313
1312 1314 **metadata : any
1313 1315 further keys for extensions to the Trait (e.g. config)
1314 1316
1315 1317 """
1316 1318 self._minlen = minlen
1317 1319 self._maxlen = maxlen
1318 1320 super(List, self).__init__(trait=trait, default_value=default_value,
1319 1321 allow_none=allow_none, **metadata)
1320 1322
1321 1323 def length_error(self, obj, value):
1322 1324 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1323 1325 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1324 1326 raise TraitError(e)
1325 1327
1326 1328 def validate_elements(self, obj, value):
1327 1329 length = len(value)
1328 1330 if length < self._minlen or length > self._maxlen:
1329 1331 self.length_error(obj, value)
1330 1332
1331 1333 return super(List, self).validate_elements(obj, value)
1332 1334
1333 1335
1334 1336 class Set(Container):
1335 1337 """An instance of a Python set."""
1336 1338 klass = set
1337 1339
1338 1340 class Tuple(Container):
1339 1341 """An instance of a Python tuple."""
1340 1342 klass = tuple
1341 1343
1342 1344 def __init__(self, *traits, **metadata):
1343 1345 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1344 1346
1345 1347 Create a tuple from a list, set, or tuple.
1346 1348
1347 1349 Create a fixed-type tuple with Traits:
1348 1350
1349 1351 ``t = Tuple(Int, Str, CStr)``
1350 1352
1351 1353 would be length 3, with Int,Str,CStr for each element.
1352 1354
1353 1355 If only one arg is given and it is not a Trait, it is taken as
1354 1356 default_value:
1355 1357
1356 1358 ``t = Tuple((1,2,3))``
1357 1359
1358 1360 Otherwise, ``default_value`` *must* be specified by keyword.
1359 1361
1360 1362 Parameters
1361 1363 ----------
1362 1364
1363 1365 *traits : TraitTypes [ optional ]
1364 1366 the tsype for restricting the contents of the Tuple. If unspecified,
1365 1367 types are not checked. If specified, then each positional argument
1366 1368 corresponds to an element of the tuple. Tuples defined with traits
1367 1369 are of fixed length.
1368 1370
1369 1371 default_value : SequenceType [ optional ]
1370 1372 The default value for the Tuple. Must be list/tuple/set, and
1371 1373 will be cast to a tuple. If `traits` are specified, the
1372 1374 `default_value` must conform to the shape and type they specify.
1373 1375
1374 1376 allow_none : Bool [ default True ]
1375 1377 Whether to allow the value to be None
1376 1378
1377 1379 **metadata : any
1378 1380 further keys for extensions to the Trait (e.g. config)
1379 1381
1380 1382 """
1381 1383 default_value = metadata.pop('default_value', None)
1382 1384 allow_none = metadata.pop('allow_none', True)
1383 1385
1384 1386 # allow Tuple((values,)):
1385 1387 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1386 1388 default_value = traits[0]
1387 1389 traits = ()
1388 1390
1389 1391 if default_value is None:
1390 1392 args = ()
1391 1393 elif isinstance(default_value, self._valid_defaults):
1392 1394 args = (default_value,)
1393 1395 else:
1394 1396 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1395 1397
1396 1398 self._traits = []
1397 1399 for trait in traits:
1398 1400 t = trait() if isinstance(trait, type) else trait
1399 1401 t.name = 'element'
1400 1402 self._traits.append(t)
1401 1403
1402 1404 if self._traits and default_value is None:
1403 1405 # don't allow default to be an empty container if length is specified
1404 1406 args = None
1405 1407 super(Container,self).__init__(klass=self.klass, args=args,
1406 1408 allow_none=allow_none, **metadata)
1407 1409
1408 1410 def validate_elements(self, obj, value):
1409 1411 if not self._traits:
1410 1412 # nothing to validate
1411 1413 return value
1412 1414 if len(value) != len(self._traits):
1413 1415 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1414 1416 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1415 1417 raise TraitError(e)
1416 1418
1417 1419 validated = []
1418 1420 for t,v in zip(self._traits, value):
1419 1421 try:
1420 1422 v = t.validate(obj, v)
1421 1423 except TraitError:
1422 1424 self.element_error(obj, v, t)
1423 1425 else:
1424 1426 validated.append(v)
1425 1427 return tuple(validated)
1426 1428
1427 1429
1428 1430 class Dict(Instance):
1429 1431 """An instance of a Python dict."""
1430 1432
1431 1433 def __init__(self, default_value=None, allow_none=True, **metadata):
1432 1434 """Create a dict trait type from a dict.
1433 1435
1434 1436 The default value is created by doing ``dict(default_value)``,
1435 1437 which creates a copy of the ``default_value``.
1436 1438 """
1437 1439 if default_value is None:
1438 1440 args = ((),)
1439 1441 elif isinstance(default_value, dict):
1440 1442 args = (default_value,)
1441 1443 elif isinstance(default_value, SequenceTypes):
1442 1444 args = (default_value,)
1443 1445 else:
1444 1446 raise TypeError('default value of Dict was %s' % default_value)
1445 1447
1446 1448 super(Dict,self).__init__(klass=dict, args=args,
1447 1449 allow_none=allow_none, **metadata)
1448 1450
1449 1451 class TCPAddress(TraitType):
1450 1452 """A trait for an (ip, port) tuple.
1451 1453
1452 1454 This allows for both IPv4 IP addresses as well as hostnames.
1453 1455 """
1454 1456
1455 1457 default_value = ('127.0.0.1', 0)
1456 1458 info_text = 'an (ip, port) tuple'
1457 1459
1458 1460 def validate(self, obj, value):
1459 1461 if isinstance(value, tuple):
1460 1462 if len(value) == 2:
1461 1463 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1462 1464 port = value[1]
1463 1465 if port >= 0 and port <= 65535:
1464 1466 return value
1465 1467 self.error(obj, value)
1466 1468
1467 1469 class CRegExp(TraitType):
1468 1470 """A casting compiled regular expression trait.
1469 1471
1470 1472 Accepts both strings and compiled regular expressions. The resulting
1471 1473 attribute will be a compiled regular expression."""
1472 1474
1473 1475 info_text = 'a regular expression'
1474 1476
1475 1477 def validate(self, obj, value):
1476 1478 try:
1477 1479 return re.compile(value)
1478 1480 except:
1479 1481 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now