##// END OF EJS Templates
STY: Whitespace between class definitions.
Scott Sanderson -
Show More
@@ -1,1653 +1,1656 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 """A trait with error reporting for Type, Instance and This."""
752 752
753 753 def error(self, obj, value):
754 754 kind = type(value)
755 755 if (not py3compat.PY3) and kind is InstanceType:
756 756 msg = 'class %s' % value.__class__.__name__
757 757 else:
758 758 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
759 759
760 760 if obj is not None:
761 761 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
762 762 % (self.name, class_of(obj),
763 763 self.info(), msg)
764 764 else:
765 765 e = "The '%s' trait must be %s, but a value of %r was specified." \
766 766 % (self.name, self.info(), msg)
767 767
768 768 raise TraitError(e)
769 769
770 770
771 771 class Type(ClassBasedTraitType):
772 772 """A trait whose value must be a subclass of a specified class."""
773 773
774 774 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
775 775 """Construct a Type trait
776 776
777 777 A Type trait specifies that its values must be subclasses of
778 778 a particular class.
779 779
780 780 If only ``default_value`` is given, it is used for the ``klass`` as
781 781 well.
782 782
783 783 Parameters
784 784 ----------
785 785 default_value : class, str or None
786 786 The default value must be a subclass of klass. If an str,
787 787 the str must be a fully specified class name, like 'foo.bar.Bah'.
788 788 The string is resolved into real class, when the parent
789 789 :class:`HasTraits` class is instantiated.
790 790 klass : class, str, None
791 791 Values of this trait must be a subclass of klass. The klass
792 792 may be specified in a string like: 'foo.bar.MyClass'.
793 793 The string is resolved into real class, when the parent
794 794 :class:`HasTraits` class is instantiated.
795 795 allow_none : boolean
796 796 Indicates whether None is allowed as an assignable value. Even if
797 797 ``False``, the default value may be ``None``.
798 798 """
799 799 if default_value is None:
800 800 if klass is None:
801 801 klass = object
802 802 elif klass is None:
803 803 klass = default_value
804 804
805 805 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
806 806 raise TraitError("A Type trait must specify a class.")
807 807
808 808 self.klass = klass
809 809
810 810 super(Type, self).__init__(default_value, allow_none=allow_none, **metadata)
811 811
812 812 def validate(self, obj, value):
813 813 """Validates that the value is a valid object instance."""
814 814 if isinstance(value, py3compat.string_types):
815 815 try:
816 816 value = import_item(value)
817 817 except ImportError:
818 818 raise TraitError("The '%s' trait of %s instance must be a type, but "
819 819 "%r could not be imported" % (self.name, obj, value))
820 820 try:
821 821 if issubclass(value, self.klass):
822 822 return value
823 823 except:
824 824 pass
825 825
826 826 self.error(obj, value)
827 827
828 828 def info(self):
829 829 """ Returns a description of the trait."""
830 830 if isinstance(self.klass, py3compat.string_types):
831 831 klass = self.klass
832 832 else:
833 833 klass = self.klass.__name__
834 834 result = 'a subclass of ' + klass
835 835 if self.allow_none:
836 836 return result + ' or None'
837 837 return result
838 838
839 839 def instance_init(self, obj):
840 840 self._resolve_classes()
841 841 super(Type, self).instance_init(obj)
842 842
843 843 def _resolve_classes(self):
844 844 if isinstance(self.klass, py3compat.string_types):
845 845 self.klass = import_item(self.klass)
846 846 if isinstance(self.default_value, py3compat.string_types):
847 847 self.default_value = import_item(self.default_value)
848 848
849 849 def get_default_value(self):
850 850 return self.default_value
851 851
852 852
853 853 class DefaultValueGenerator(object):
854 854 """A class for generating new default value instances."""
855 855
856 856 def __init__(self, *args, **kw):
857 857 self.args = args
858 858 self.kw = kw
859 859
860 860 def generate(self, klass):
861 861 return klass(*self.args, **self.kw)
862 862
863 863
864 864 class Instance(ClassBasedTraitType):
865 865 """A trait whose value must be an instance of a specified class.
866 866
867 867 The value can also be an instance of a subclass of the specified class.
868 868
869 869 Subclasses can declare default classes by overriding the klass attribute
870 870 """
871 871
872 872 klass = None
873 873
874 874 def __init__(self, klass=None, args=None, kw=None,
875 875 allow_none=True, **metadata ):
876 876 """Construct an Instance trait.
877 877
878 878 This trait allows values that are instances of a particular
879 879 class or its sublclasses. Our implementation is quite different
880 880 from that of enthough.traits as we don't allow instances to be used
881 881 for klass and we handle the ``args`` and ``kw`` arguments differently.
882 882
883 883 Parameters
884 884 ----------
885 885 klass : class, str
886 886 The class that forms the basis for the trait. Class names
887 887 can also be specified as strings, like 'foo.bar.Bar'.
888 888 args : tuple
889 889 Positional arguments for generating the default value.
890 890 kw : dict
891 891 Keyword arguments for generating the default value.
892 892 allow_none : bool
893 893 Indicates whether None is allowed as a value.
894 894
895 895 Notes
896 896 -----
897 897 If both ``args`` and ``kw`` are None, then the default value is None.
898 898 If ``args`` is a tuple and ``kw`` is a dict, then the default is
899 899 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
900 900 None, the None is replaced by ``()`` or ``{}``, respectively.
901 901 """
902 902 if klass is None:
903 903 klass = self.klass
904 904
905 905 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
906 906 self.klass = klass
907 907 else:
908 908 raise TraitError('The klass attribute must be a class'
909 909 ' not: %r' % klass)
910 910
911 911 # self.klass is a class, so handle default_value
912 912 if args is None and kw is None:
913 913 default_value = None
914 914 else:
915 915 if args is None:
916 916 # kw is not None
917 917 args = ()
918 918 elif kw is None:
919 919 # args is not None
920 920 kw = {}
921 921
922 922 if not isinstance(kw, dict):
923 923 raise TraitError("The 'kw' argument must be a dict or None.")
924 924 if not isinstance(args, tuple):
925 925 raise TraitError("The 'args' argument must be a tuple or None.")
926 926
927 927 default_value = DefaultValueGenerator(*args, **kw)
928 928
929 929 super(Instance, self).__init__(default_value, allow_none=allow_none, **metadata)
930 930
931 931 def validate(self, obj, value):
932 932 if isinstance(value, self.klass):
933 933 return value
934 934 else:
935 935 self.error(obj, value)
936 936
937 937 def info(self):
938 938 if isinstance(self.klass, py3compat.string_types):
939 939 klass = self.klass
940 940 else:
941 941 klass = self.klass.__name__
942 942 result = class_of(klass)
943 943 if self.allow_none:
944 944 return result + ' or None'
945 945
946 946 return result
947 947
948 948 def instance_init(self, obj):
949 949 self._resolve_classes()
950 950 super(Instance, self).instance_init(obj)
951 951
952 952 def _resolve_classes(self):
953 953 if isinstance(self.klass, py3compat.string_types):
954 954 self.klass = import_item(self.klass)
955 955
956 956 def get_default_value(self):
957 957 """Instantiate a default value instance.
958 958
959 959 This is called when the containing HasTraits classes'
960 960 :meth:`__new__` method is called to ensure that a unique instance
961 961 is created for each HasTraits instance.
962 962 """
963 963 dv = self.default_value
964 964 if isinstance(dv, DefaultValueGenerator):
965 965 return dv.generate(self.klass)
966 966 else:
967 967 return dv
968 968
969 969
970 970 class ForwardDeclaredMixin(object):
971 971 """
972 972 Mixin for forward-declared versions of Instance and Type.
973 973 """
974 974 def _resolve_classes(self):
975 975 """
976 976 Find the specified class name by looking for it in the module in which
977 977 our this_class attribute was defined.
978 978 """
979 979 try:
980 980 modname = self.this_class.__module__
981 981 self.klass = import_item('.'.join([modname, self.klass]))
982 982 except AttributeError:
983 983 raise ImportError(
984 984 "Module {} has no attribute {}".format(modname, self.klass)
985 985 )
986 986
987
987 988 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
988 989 """
989 990 Forward-declared version of Type.
990 991 """
991 992 pass
992 993
994
993 995 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
994 996 """
995 997 Forward-declared version of Instance.
996 998 """
997 999 pass
998 1000
1001
999 1002 class This(ClassBasedTraitType):
1000 1003 """A trait for instances of the class containing this trait.
1001 1004
1002 1005 Because how how and when class bodies are executed, the ``This``
1003 1006 trait can only have a default value of None. This, and because we
1004 1007 always validate default values, ``allow_none`` is *always* true.
1005 1008 """
1006 1009
1007 1010 info_text = 'an instance of the same type as the receiver or None'
1008 1011
1009 1012 def __init__(self, **metadata):
1010 1013 super(This, self).__init__(None, **metadata)
1011 1014
1012 1015 def validate(self, obj, value):
1013 1016 # What if value is a superclass of obj.__class__? This is
1014 1017 # complicated if it was the superclass that defined the This
1015 1018 # trait.
1016 1019 if isinstance(value, self.this_class) or (value is None):
1017 1020 return value
1018 1021 else:
1019 1022 self.error(obj, value)
1020 1023
1021 1024
1022 1025 #-----------------------------------------------------------------------------
1023 1026 # Basic TraitTypes implementations/subclasses
1024 1027 #-----------------------------------------------------------------------------
1025 1028
1026 1029
1027 1030 class Any(TraitType):
1028 1031 default_value = None
1029 1032 info_text = 'any value'
1030 1033
1031 1034
1032 1035 class Int(TraitType):
1033 1036 """An int trait."""
1034 1037
1035 1038 default_value = 0
1036 1039 info_text = 'an int'
1037 1040
1038 1041 def validate(self, obj, value):
1039 1042 if isinstance(value, int):
1040 1043 return value
1041 1044 self.error(obj, value)
1042 1045
1043 1046 class CInt(Int):
1044 1047 """A casting version of the int trait."""
1045 1048
1046 1049 def validate(self, obj, value):
1047 1050 try:
1048 1051 return int(value)
1049 1052 except:
1050 1053 self.error(obj, value)
1051 1054
1052 1055 if py3compat.PY3:
1053 1056 Long, CLong = Int, CInt
1054 1057 Integer = Int
1055 1058 else:
1056 1059 class Long(TraitType):
1057 1060 """A long integer trait."""
1058 1061
1059 1062 default_value = 0
1060 1063 info_text = 'a long'
1061 1064
1062 1065 def validate(self, obj, value):
1063 1066 if isinstance(value, long):
1064 1067 return value
1065 1068 if isinstance(value, int):
1066 1069 return long(value)
1067 1070 self.error(obj, value)
1068 1071
1069 1072
1070 1073 class CLong(Long):
1071 1074 """A casting version of the long integer trait."""
1072 1075
1073 1076 def validate(self, obj, value):
1074 1077 try:
1075 1078 return long(value)
1076 1079 except:
1077 1080 self.error(obj, value)
1078 1081
1079 1082 class Integer(TraitType):
1080 1083 """An integer trait.
1081 1084
1082 1085 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
1083 1086
1084 1087 default_value = 0
1085 1088 info_text = 'an integer'
1086 1089
1087 1090 def validate(self, obj, value):
1088 1091 if isinstance(value, int):
1089 1092 return value
1090 1093 if isinstance(value, long):
1091 1094 # downcast longs that fit in int:
1092 1095 # note that int(n > sys.maxint) returns a long, so
1093 1096 # we don't need a condition on this cast
1094 1097 return int(value)
1095 1098 if sys.platform == "cli":
1096 1099 from System import Int64
1097 1100 if isinstance(value, Int64):
1098 1101 return int(value)
1099 1102 self.error(obj, value)
1100 1103
1101 1104
1102 1105 class Float(TraitType):
1103 1106 """A float trait."""
1104 1107
1105 1108 default_value = 0.0
1106 1109 info_text = 'a float'
1107 1110
1108 1111 def validate(self, obj, value):
1109 1112 if isinstance(value, float):
1110 1113 return value
1111 1114 if isinstance(value, int):
1112 1115 return float(value)
1113 1116 self.error(obj, value)
1114 1117
1115 1118
1116 1119 class CFloat(Float):
1117 1120 """A casting version of the float trait."""
1118 1121
1119 1122 def validate(self, obj, value):
1120 1123 try:
1121 1124 return float(value)
1122 1125 except:
1123 1126 self.error(obj, value)
1124 1127
1125 1128 class Complex(TraitType):
1126 1129 """A trait for complex numbers."""
1127 1130
1128 1131 default_value = 0.0 + 0.0j
1129 1132 info_text = 'a complex number'
1130 1133
1131 1134 def validate(self, obj, value):
1132 1135 if isinstance(value, complex):
1133 1136 return value
1134 1137 if isinstance(value, (float, int)):
1135 1138 return complex(value)
1136 1139 self.error(obj, value)
1137 1140
1138 1141
1139 1142 class CComplex(Complex):
1140 1143 """A casting version of the complex number trait."""
1141 1144
1142 1145 def validate (self, obj, value):
1143 1146 try:
1144 1147 return complex(value)
1145 1148 except:
1146 1149 self.error(obj, value)
1147 1150
1148 1151 # We should always be explicit about whether we're using bytes or unicode, both
1149 1152 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1150 1153 # we don't have a Str type.
1151 1154 class Bytes(TraitType):
1152 1155 """A trait for byte strings."""
1153 1156
1154 1157 default_value = b''
1155 1158 info_text = 'a bytes object'
1156 1159
1157 1160 def validate(self, obj, value):
1158 1161 if isinstance(value, bytes):
1159 1162 return value
1160 1163 self.error(obj, value)
1161 1164
1162 1165
1163 1166 class CBytes(Bytes):
1164 1167 """A casting version of the byte string trait."""
1165 1168
1166 1169 def validate(self, obj, value):
1167 1170 try:
1168 1171 return bytes(value)
1169 1172 except:
1170 1173 self.error(obj, value)
1171 1174
1172 1175
1173 1176 class Unicode(TraitType):
1174 1177 """A trait for unicode strings."""
1175 1178
1176 1179 default_value = u''
1177 1180 info_text = 'a unicode string'
1178 1181
1179 1182 def validate(self, obj, value):
1180 1183 if isinstance(value, py3compat.unicode_type):
1181 1184 return value
1182 1185 if isinstance(value, bytes):
1183 1186 try:
1184 1187 return value.decode('ascii', 'strict')
1185 1188 except UnicodeDecodeError:
1186 1189 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1187 1190 raise TraitError(msg.format(value, self.name, class_of(obj)))
1188 1191 self.error(obj, value)
1189 1192
1190 1193
1191 1194 class CUnicode(Unicode):
1192 1195 """A casting version of the unicode trait."""
1193 1196
1194 1197 def validate(self, obj, value):
1195 1198 try:
1196 1199 return py3compat.unicode_type(value)
1197 1200 except:
1198 1201 self.error(obj, value)
1199 1202
1200 1203
1201 1204 class ObjectName(TraitType):
1202 1205 """A string holding a valid object name in this version of Python.
1203 1206
1204 1207 This does not check that the name exists in any scope."""
1205 1208 info_text = "a valid object identifier in Python"
1206 1209
1207 1210 if py3compat.PY3:
1208 1211 # Python 3:
1209 1212 coerce_str = staticmethod(lambda _,s: s)
1210 1213
1211 1214 else:
1212 1215 # Python 2:
1213 1216 def coerce_str(self, obj, value):
1214 1217 "In Python 2, coerce ascii-only unicode to str"
1215 1218 if isinstance(value, unicode):
1216 1219 try:
1217 1220 return str(value)
1218 1221 except UnicodeEncodeError:
1219 1222 self.error(obj, value)
1220 1223 return value
1221 1224
1222 1225 def validate(self, obj, value):
1223 1226 value = self.coerce_str(obj, value)
1224 1227
1225 1228 if isinstance(value, string_types) and py3compat.isidentifier(value):
1226 1229 return value
1227 1230 self.error(obj, value)
1228 1231
1229 1232 class DottedObjectName(ObjectName):
1230 1233 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1231 1234 def validate(self, obj, value):
1232 1235 value = self.coerce_str(obj, value)
1233 1236
1234 1237 if isinstance(value, string_types) and py3compat.isidentifier(value, dotted=True):
1235 1238 return value
1236 1239 self.error(obj, value)
1237 1240
1238 1241
1239 1242 class Bool(TraitType):
1240 1243 """A boolean (True, False) trait."""
1241 1244
1242 1245 default_value = False
1243 1246 info_text = 'a boolean'
1244 1247
1245 1248 def validate(self, obj, value):
1246 1249 if isinstance(value, bool):
1247 1250 return value
1248 1251 self.error(obj, value)
1249 1252
1250 1253
1251 1254 class CBool(Bool):
1252 1255 """A casting version of the boolean trait."""
1253 1256
1254 1257 def validate(self, obj, value):
1255 1258 try:
1256 1259 return bool(value)
1257 1260 except:
1258 1261 self.error(obj, value)
1259 1262
1260 1263
1261 1264 class Enum(TraitType):
1262 1265 """An enum that whose value must be in a given sequence."""
1263 1266
1264 1267 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1265 1268 self.values = values
1266 1269 super(Enum, self).__init__(default_value, allow_none=allow_none, **metadata)
1267 1270
1268 1271 def validate(self, obj, value):
1269 1272 if value in self.values:
1270 1273 return value
1271 1274 self.error(obj, value)
1272 1275
1273 1276 def info(self):
1274 1277 """ Returns a description of the trait."""
1275 1278 result = 'any of ' + repr(self.values)
1276 1279 if self.allow_none:
1277 1280 return result + ' or None'
1278 1281 return result
1279 1282
1280 1283 class CaselessStrEnum(Enum):
1281 1284 """An enum of strings that are caseless in validate."""
1282 1285
1283 1286 def validate(self, obj, value):
1284 1287 if not isinstance(value, py3compat.string_types):
1285 1288 self.error(obj, value)
1286 1289
1287 1290 for v in self.values:
1288 1291 if v.lower() == value.lower():
1289 1292 return v
1290 1293 self.error(obj, value)
1291 1294
1292 1295 class Container(Instance):
1293 1296 """An instance of a container (list, set, etc.)
1294 1297
1295 1298 To be subclassed by overriding klass.
1296 1299 """
1297 1300 klass = None
1298 1301 _cast_types = ()
1299 1302 _valid_defaults = SequenceTypes
1300 1303 _trait = None
1301 1304
1302 1305 def __init__(self, trait=None, default_value=None, allow_none=True,
1303 1306 **metadata):
1304 1307 """Create a container trait type from a list, set, or tuple.
1305 1308
1306 1309 The default value is created by doing ``List(default_value)``,
1307 1310 which creates a copy of the ``default_value``.
1308 1311
1309 1312 ``trait`` can be specified, which restricts the type of elements
1310 1313 in the container to that TraitType.
1311 1314
1312 1315 If only one arg is given and it is not a Trait, it is taken as
1313 1316 ``default_value``:
1314 1317
1315 1318 ``c = List([1,2,3])``
1316 1319
1317 1320 Parameters
1318 1321 ----------
1319 1322
1320 1323 trait : TraitType [ optional ]
1321 1324 the type for restricting the contents of the Container. If unspecified,
1322 1325 types are not checked.
1323 1326
1324 1327 default_value : SequenceType [ optional ]
1325 1328 The default value for the Trait. Must be list/tuple/set, and
1326 1329 will be cast to the container type.
1327 1330
1328 1331 allow_none : Bool [ default True ]
1329 1332 Whether to allow the value to be None
1330 1333
1331 1334 **metadata : any
1332 1335 further keys for extensions to the Trait (e.g. config)
1333 1336
1334 1337 """
1335 1338 # allow List([values]):
1336 1339 if default_value is None and not is_trait(trait):
1337 1340 default_value = trait
1338 1341 trait = None
1339 1342
1340 1343 if default_value is None:
1341 1344 args = ()
1342 1345 elif isinstance(default_value, self._valid_defaults):
1343 1346 args = (default_value,)
1344 1347 else:
1345 1348 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1346 1349
1347 1350 if is_trait(trait):
1348 1351 self._trait = trait() if isinstance(trait, type) else trait
1349 1352 self._trait.name = 'element'
1350 1353 elif trait is not None:
1351 1354 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1352 1355
1353 1356 super(Container,self).__init__(klass=self.klass, args=args,
1354 1357 allow_none=allow_none, **metadata)
1355 1358
1356 1359 def element_error(self, obj, element, validator):
1357 1360 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1358 1361 % (self.name, class_of(obj), validator.info(), repr_type(element))
1359 1362 raise TraitError(e)
1360 1363
1361 1364 def validate(self, obj, value):
1362 1365 if isinstance(value, self._cast_types):
1363 1366 value = self.klass(value)
1364 1367 value = super(Container, self).validate(obj, value)
1365 1368 if value is None:
1366 1369 return value
1367 1370
1368 1371 value = self.validate_elements(obj, value)
1369 1372
1370 1373 return value
1371 1374
1372 1375 def validate_elements(self, obj, value):
1373 1376 validated = []
1374 1377 if self._trait is None or isinstance(self._trait, Any):
1375 1378 return value
1376 1379 for v in value:
1377 1380 try:
1378 1381 v = self._trait._validate(obj, v)
1379 1382 except TraitError:
1380 1383 self.element_error(obj, v, self._trait)
1381 1384 else:
1382 1385 validated.append(v)
1383 1386 return self.klass(validated)
1384 1387
1385 1388 def instance_init(self, obj):
1386 1389 if isinstance(self._trait, TraitType):
1387 1390 self._trait.this_class = self.this_class
1388 1391 if isinstance(self._trait, Instance):
1389 1392 self._trait._resolve_classes()
1390 1393 super(Container, self).instance_init(obj)
1391 1394
1392 1395
1393 1396 class List(Container):
1394 1397 """An instance of a Python list."""
1395 1398 klass = list
1396 1399 _cast_types = (tuple,)
1397 1400
1398 1401 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1399 1402 allow_none=True, **metadata):
1400 1403 """Create a List trait type from a list, set, or tuple.
1401 1404
1402 1405 The default value is created by doing ``List(default_value)``,
1403 1406 which creates a copy of the ``default_value``.
1404 1407
1405 1408 ``trait`` can be specified, which restricts the type of elements
1406 1409 in the container to that TraitType.
1407 1410
1408 1411 If only one arg is given and it is not a Trait, it is taken as
1409 1412 ``default_value``:
1410 1413
1411 1414 ``c = List([1,2,3])``
1412 1415
1413 1416 Parameters
1414 1417 ----------
1415 1418
1416 1419 trait : TraitType [ optional ]
1417 1420 the type for restricting the contents of the Container. If unspecified,
1418 1421 types are not checked.
1419 1422
1420 1423 default_value : SequenceType [ optional ]
1421 1424 The default value for the Trait. Must be list/tuple/set, and
1422 1425 will be cast to the container type.
1423 1426
1424 1427 minlen : Int [ default 0 ]
1425 1428 The minimum length of the input list
1426 1429
1427 1430 maxlen : Int [ default sys.maxsize ]
1428 1431 The maximum length of the input list
1429 1432
1430 1433 allow_none : Bool [ default True ]
1431 1434 Whether to allow the value to be None
1432 1435
1433 1436 **metadata : any
1434 1437 further keys for extensions to the Trait (e.g. config)
1435 1438
1436 1439 """
1437 1440 self._minlen = minlen
1438 1441 self._maxlen = maxlen
1439 1442 super(List, self).__init__(trait=trait, default_value=default_value,
1440 1443 allow_none=allow_none, **metadata)
1441 1444
1442 1445 def length_error(self, obj, value):
1443 1446 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1444 1447 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1445 1448 raise TraitError(e)
1446 1449
1447 1450 def validate_elements(self, obj, value):
1448 1451 length = len(value)
1449 1452 if length < self._minlen or length > self._maxlen:
1450 1453 self.length_error(obj, value)
1451 1454
1452 1455 return super(List, self).validate_elements(obj, value)
1453 1456
1454 1457 def validate(self, obj, value):
1455 1458 value = super(List, self).validate(obj, value)
1456 1459
1457 1460 value = self.validate_elements(obj, value)
1458 1461
1459 1462 return value
1460 1463
1461 1464
1462 1465
1463 1466 class Set(List):
1464 1467 """An instance of a Python set."""
1465 1468 klass = set
1466 1469 _cast_types = (tuple, list)
1467 1470
1468 1471 class Tuple(Container):
1469 1472 """An instance of a Python tuple."""
1470 1473 klass = tuple
1471 1474 _cast_types = (list,)
1472 1475
1473 1476 def __init__(self, *traits, **metadata):
1474 1477 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1475 1478
1476 1479 Create a tuple from a list, set, or tuple.
1477 1480
1478 1481 Create a fixed-type tuple with Traits:
1479 1482
1480 1483 ``t = Tuple(Int, Str, CStr)``
1481 1484
1482 1485 would be length 3, with Int,Str,CStr for each element.
1483 1486
1484 1487 If only one arg is given and it is not a Trait, it is taken as
1485 1488 default_value:
1486 1489
1487 1490 ``t = Tuple((1,2,3))``
1488 1491
1489 1492 Otherwise, ``default_value`` *must* be specified by keyword.
1490 1493
1491 1494 Parameters
1492 1495 ----------
1493 1496
1494 1497 *traits : TraitTypes [ optional ]
1495 1498 the tsype for restricting the contents of the Tuple. If unspecified,
1496 1499 types are not checked. If specified, then each positional argument
1497 1500 corresponds to an element of the tuple. Tuples defined with traits
1498 1501 are of fixed length.
1499 1502
1500 1503 default_value : SequenceType [ optional ]
1501 1504 The default value for the Tuple. Must be list/tuple/set, and
1502 1505 will be cast to a tuple. If `traits` are specified, the
1503 1506 `default_value` must conform to the shape and type they specify.
1504 1507
1505 1508 allow_none : Bool [ default True ]
1506 1509 Whether to allow the value to be None
1507 1510
1508 1511 **metadata : any
1509 1512 further keys for extensions to the Trait (e.g. config)
1510 1513
1511 1514 """
1512 1515 default_value = metadata.pop('default_value', None)
1513 1516 allow_none = metadata.pop('allow_none', True)
1514 1517
1515 1518 # allow Tuple((values,)):
1516 1519 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1517 1520 default_value = traits[0]
1518 1521 traits = ()
1519 1522
1520 1523 if default_value is None:
1521 1524 args = ()
1522 1525 elif isinstance(default_value, self._valid_defaults):
1523 1526 args = (default_value,)
1524 1527 else:
1525 1528 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1526 1529
1527 1530 self._traits = []
1528 1531 for trait in traits:
1529 1532 t = trait() if isinstance(trait, type) else trait
1530 1533 t.name = 'element'
1531 1534 self._traits.append(t)
1532 1535
1533 1536 if self._traits and default_value is None:
1534 1537 # don't allow default to be an empty container if length is specified
1535 1538 args = None
1536 1539 super(Container,self).__init__(klass=self.klass, args=args,
1537 1540 allow_none=allow_none, **metadata)
1538 1541
1539 1542 def validate_elements(self, obj, value):
1540 1543 if not self._traits:
1541 1544 # nothing to validate
1542 1545 return value
1543 1546 if len(value) != len(self._traits):
1544 1547 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1545 1548 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1546 1549 raise TraitError(e)
1547 1550
1548 1551 validated = []
1549 1552 for t,v in zip(self._traits, value):
1550 1553 try:
1551 1554 v = t._validate(obj, v)
1552 1555 except TraitError:
1553 1556 self.element_error(obj, v, t)
1554 1557 else:
1555 1558 validated.append(v)
1556 1559 return tuple(validated)
1557 1560
1558 1561
1559 1562 class Dict(Instance):
1560 1563 """An instance of a Python dict."""
1561 1564
1562 1565 def __init__(self, default_value=None, allow_none=True, **metadata):
1563 1566 """Create a dict trait type from a dict.
1564 1567
1565 1568 The default value is created by doing ``dict(default_value)``,
1566 1569 which creates a copy of the ``default_value``.
1567 1570 """
1568 1571 if default_value is None:
1569 1572 args = ((),)
1570 1573 elif isinstance(default_value, dict):
1571 1574 args = (default_value,)
1572 1575 elif isinstance(default_value, SequenceTypes):
1573 1576 args = (default_value,)
1574 1577 else:
1575 1578 raise TypeError('default value of Dict was %s' % default_value)
1576 1579
1577 1580 super(Dict,self).__init__(klass=dict, args=args,
1578 1581 allow_none=allow_none, **metadata)
1579 1582
1580 1583
1581 1584 class EventfulDict(Instance):
1582 1585 """An instance of an EventfulDict."""
1583 1586
1584 1587 def __init__(self, default_value=None, allow_none=True, **metadata):
1585 1588 """Create a EventfulDict trait type from a dict.
1586 1589
1587 1590 The default value is created by doing
1588 1591 ``eventful.EvenfulDict(default_value)``, which creates a copy of the
1589 1592 ``default_value``.
1590 1593 """
1591 1594 if default_value is None:
1592 1595 args = ((),)
1593 1596 elif isinstance(default_value, dict):
1594 1597 args = (default_value,)
1595 1598 elif isinstance(default_value, SequenceTypes):
1596 1599 args = (default_value,)
1597 1600 else:
1598 1601 raise TypeError('default value of EventfulDict was %s' % default_value)
1599 1602
1600 1603 super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
1601 1604 allow_none=allow_none, **metadata)
1602 1605
1603 1606
1604 1607 class EventfulList(Instance):
1605 1608 """An instance of an EventfulList."""
1606 1609
1607 1610 def __init__(self, default_value=None, allow_none=True, **metadata):
1608 1611 """Create a EventfulList trait type from a dict.
1609 1612
1610 1613 The default value is created by doing
1611 1614 ``eventful.EvenfulList(default_value)``, which creates a copy of the
1612 1615 ``default_value``.
1613 1616 """
1614 1617 if default_value is None:
1615 1618 args = ((),)
1616 1619 else:
1617 1620 args = (default_value,)
1618 1621
1619 1622 super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
1620 1623 allow_none=allow_none, **metadata)
1621 1624
1622 1625
1623 1626 class TCPAddress(TraitType):
1624 1627 """A trait for an (ip, port) tuple.
1625 1628
1626 1629 This allows for both IPv4 IP addresses as well as hostnames.
1627 1630 """
1628 1631
1629 1632 default_value = ('127.0.0.1', 0)
1630 1633 info_text = 'an (ip, port) tuple'
1631 1634
1632 1635 def validate(self, obj, value):
1633 1636 if isinstance(value, tuple):
1634 1637 if len(value) == 2:
1635 1638 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1636 1639 port = value[1]
1637 1640 if port >= 0 and port <= 65535:
1638 1641 return value
1639 1642 self.error(obj, value)
1640 1643
1641 1644 class CRegExp(TraitType):
1642 1645 """A casting compiled regular expression trait.
1643 1646
1644 1647 Accepts both strings and compiled regular expressions. The resulting
1645 1648 attribute will be a compiled regular expression."""
1646 1649
1647 1650 info_text = 'a regular expression'
1648 1651
1649 1652 def validate(self, obj, value):
1650 1653 try:
1651 1654 return re.compile(value)
1652 1655 except:
1653 1656 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now