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