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