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