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