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