##// END OF EJS Templates
CBytes and CStr only try casting to bytes (=str), not to unicode.
Thomas Kluyver -
Show More
@@ -1,1358 +1,1355 b''
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 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 962 if isinstance(value, bytes):
963 963 return value
964 964 self.error(obj, value)
965 965
966 966
967 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 972 return bytes(value)
973 973 except:
974 try:
975 return unicode(value)
976 except:
977 974 self.error(obj, value)
978 975
979 976
980 977 class Unicode(TraitType):
981 978 """A trait for unicode strings."""
982 979
983 980 default_value = u''
984 981 info_text = 'a unicode string'
985 982
986 983 def validate(self, obj, value):
987 984 if isinstance(value, unicode):
988 985 return value
989 986 if isinstance(value, bytes):
990 987 return unicode(value)
991 988 self.error(obj, value)
992 989
993 990
994 991 class CUnicode(Unicode):
995 992 """A casting version of the unicode trait."""
996 993
997 994 def validate(self, obj, value):
998 995 try:
999 996 return unicode(value)
1000 997 except:
1001 998 self.error(obj, value)
1002 999
1003 1000 if sys.version_info[0] < 3:
1004 1001 Str, CStr = Bytes, CBytes
1005 1002 else:
1006 1003 Str, CStr = Unicode, CUnicode
1007 1004
1008 1005
1009 1006 class Bool(TraitType):
1010 1007 """A boolean (True, False) trait."""
1011 1008
1012 1009 default_value = False
1013 1010 info_text = 'a boolean'
1014 1011
1015 1012 def validate(self, obj, value):
1016 1013 if isinstance(value, bool):
1017 1014 return value
1018 1015 self.error(obj, value)
1019 1016
1020 1017
1021 1018 class CBool(Bool):
1022 1019 """A casting version of the boolean trait."""
1023 1020
1024 1021 def validate(self, obj, value):
1025 1022 try:
1026 1023 return bool(value)
1027 1024 except:
1028 1025 self.error(obj, value)
1029 1026
1030 1027
1031 1028 class Enum(TraitType):
1032 1029 """An enum that whose value must be in a given sequence."""
1033 1030
1034 1031 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1035 1032 self.values = values
1036 1033 self._allow_none = allow_none
1037 1034 super(Enum, self).__init__(default_value, **metadata)
1038 1035
1039 1036 def validate(self, obj, value):
1040 1037 if value is None:
1041 1038 if self._allow_none:
1042 1039 return value
1043 1040
1044 1041 if value in self.values:
1045 1042 return value
1046 1043 self.error(obj, value)
1047 1044
1048 1045 def info(self):
1049 1046 """ Returns a description of the trait."""
1050 1047 result = 'any of ' + repr(self.values)
1051 1048 if self._allow_none:
1052 1049 return result + ' or None'
1053 1050 return result
1054 1051
1055 1052 class CaselessStrEnum(Enum):
1056 1053 """An enum of strings that are caseless in validate."""
1057 1054
1058 1055 def validate(self, obj, value):
1059 1056 if value is None:
1060 1057 if self._allow_none:
1061 1058 return value
1062 1059
1063 1060 if not isinstance(value, str):
1064 1061 self.error(obj, value)
1065 1062
1066 1063 for v in self.values:
1067 1064 if v.lower() == value.lower():
1068 1065 return v
1069 1066 self.error(obj, value)
1070 1067
1071 1068 class Container(Instance):
1072 1069 """An instance of a container (list, set, etc.)
1073 1070
1074 1071 To be subclassed by overriding klass.
1075 1072 """
1076 1073 klass = None
1077 1074 _valid_defaults = SequenceTypes
1078 1075 _trait = None
1079 1076
1080 1077 def __init__(self, trait=None, default_value=None, allow_none=True,
1081 1078 **metadata):
1082 1079 """Create a container trait type from a list, set, or tuple.
1083 1080
1084 1081 The default value is created by doing ``List(default_value)``,
1085 1082 which creates a copy of the ``default_value``.
1086 1083
1087 1084 ``trait`` can be specified, which restricts the type of elements
1088 1085 in the container to that TraitType.
1089 1086
1090 1087 If only one arg is given and it is not a Trait, it is taken as
1091 1088 ``default_value``:
1092 1089
1093 1090 ``c = List([1,2,3])``
1094 1091
1095 1092 Parameters
1096 1093 ----------
1097 1094
1098 1095 trait : TraitType [ optional ]
1099 1096 the type for restricting the contents of the Container. If unspecified,
1100 1097 types are not checked.
1101 1098
1102 1099 default_value : SequenceType [ optional ]
1103 1100 The default value for the Trait. Must be list/tuple/set, and
1104 1101 will be cast to the container type.
1105 1102
1106 1103 allow_none : Bool [ default True ]
1107 1104 Whether to allow the value to be None
1108 1105
1109 1106 **metadata : any
1110 1107 further keys for extensions to the Trait (e.g. config)
1111 1108
1112 1109 """
1113 1110 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1114 1111
1115 1112 # allow List([values]):
1116 1113 if default_value is None and not istrait(trait):
1117 1114 default_value = trait
1118 1115 trait = None
1119 1116
1120 1117 if default_value is None:
1121 1118 args = ()
1122 1119 elif isinstance(default_value, self._valid_defaults):
1123 1120 args = (default_value,)
1124 1121 else:
1125 1122 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1126 1123
1127 1124 if istrait(trait):
1128 1125 self._trait = trait()
1129 1126 self._trait.name = 'element'
1130 1127 elif trait is not None:
1131 1128 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1132 1129
1133 1130 super(Container,self).__init__(klass=self.klass, args=args,
1134 1131 allow_none=allow_none, **metadata)
1135 1132
1136 1133 def element_error(self, obj, element, validator):
1137 1134 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1138 1135 % (self.name, class_of(obj), validator.info(), repr_type(element))
1139 1136 raise TraitError(e)
1140 1137
1141 1138 def validate(self, obj, value):
1142 1139 value = super(Container, self).validate(obj, value)
1143 1140 if value is None:
1144 1141 return value
1145 1142
1146 1143 value = self.validate_elements(obj, value)
1147 1144
1148 1145 return value
1149 1146
1150 1147 def validate_elements(self, obj, value):
1151 1148 validated = []
1152 1149 if self._trait is None or isinstance(self._trait, Any):
1153 1150 return value
1154 1151 for v in value:
1155 1152 try:
1156 1153 v = self._trait.validate(obj, v)
1157 1154 except TraitError:
1158 1155 self.element_error(obj, v, self._trait)
1159 1156 else:
1160 1157 validated.append(v)
1161 1158 return self.klass(validated)
1162 1159
1163 1160
1164 1161 class List(Container):
1165 1162 """An instance of a Python list."""
1166 1163 klass = list
1167 1164
1168 1165 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint,
1169 1166 allow_none=True, **metadata):
1170 1167 """Create a List trait type from a list, set, or tuple.
1171 1168
1172 1169 The default value is created by doing ``List(default_value)``,
1173 1170 which creates a copy of the ``default_value``.
1174 1171
1175 1172 ``trait`` can be specified, which restricts the type of elements
1176 1173 in the container to that TraitType.
1177 1174
1178 1175 If only one arg is given and it is not a Trait, it is taken as
1179 1176 ``default_value``:
1180 1177
1181 1178 ``c = List([1,2,3])``
1182 1179
1183 1180 Parameters
1184 1181 ----------
1185 1182
1186 1183 trait : TraitType [ optional ]
1187 1184 the type for restricting the contents of the Container. If unspecified,
1188 1185 types are not checked.
1189 1186
1190 1187 default_value : SequenceType [ optional ]
1191 1188 The default value for the Trait. Must be list/tuple/set, and
1192 1189 will be cast to the container type.
1193 1190
1194 1191 minlen : Int [ default 0 ]
1195 1192 The minimum length of the input list
1196 1193
1197 1194 maxlen : Int [ default sys.maxint ]
1198 1195 The maximum length of the input list
1199 1196
1200 1197 allow_none : Bool [ default True ]
1201 1198 Whether to allow the value to be None
1202 1199
1203 1200 **metadata : any
1204 1201 further keys for extensions to the Trait (e.g. config)
1205 1202
1206 1203 """
1207 1204 self._minlen = minlen
1208 1205 self._maxlen = maxlen
1209 1206 super(List, self).__init__(trait=trait, default_value=default_value,
1210 1207 allow_none=allow_none, **metadata)
1211 1208
1212 1209 def length_error(self, obj, value):
1213 1210 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1214 1211 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1215 1212 raise TraitError(e)
1216 1213
1217 1214 def validate_elements(self, obj, value):
1218 1215 length = len(value)
1219 1216 if length < self._minlen or length > self._maxlen:
1220 1217 self.length_error(obj, value)
1221 1218
1222 1219 return super(List, self).validate_elements(obj, value)
1223 1220
1224 1221
1225 1222 class Set(Container):
1226 1223 """An instance of a Python set."""
1227 1224 klass = set
1228 1225
1229 1226 class Tuple(Container):
1230 1227 """An instance of a Python tuple."""
1231 1228 klass = tuple
1232 1229
1233 1230 def __init__(self, *traits, **metadata):
1234 1231 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1235 1232
1236 1233 Create a tuple from a list, set, or tuple.
1237 1234
1238 1235 Create a fixed-type tuple with Traits:
1239 1236
1240 1237 ``t = Tuple(Int, Str, CStr)``
1241 1238
1242 1239 would be length 3, with Int,Str,CStr for each element.
1243 1240
1244 1241 If only one arg is given and it is not a Trait, it is taken as
1245 1242 default_value:
1246 1243
1247 1244 ``t = Tuple((1,2,3))``
1248 1245
1249 1246 Otherwise, ``default_value`` *must* be specified by keyword.
1250 1247
1251 1248 Parameters
1252 1249 ----------
1253 1250
1254 1251 *traits : TraitTypes [ optional ]
1255 1252 the tsype for restricting the contents of the Tuple. If unspecified,
1256 1253 types are not checked. If specified, then each positional argument
1257 1254 corresponds to an element of the tuple. Tuples defined with traits
1258 1255 are of fixed length.
1259 1256
1260 1257 default_value : SequenceType [ optional ]
1261 1258 The default value for the Tuple. Must be list/tuple/set, and
1262 1259 will be cast to a tuple. If `traits` are specified, the
1263 1260 `default_value` must conform to the shape and type they specify.
1264 1261
1265 1262 allow_none : Bool [ default True ]
1266 1263 Whether to allow the value to be None
1267 1264
1268 1265 **metadata : any
1269 1266 further keys for extensions to the Trait (e.g. config)
1270 1267
1271 1268 """
1272 1269 default_value = metadata.pop('default_value', None)
1273 1270 allow_none = metadata.pop('allow_none', True)
1274 1271
1275 1272 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1276 1273
1277 1274 # allow Tuple((values,)):
1278 1275 if len(traits) == 1 and default_value is None and not istrait(traits[0]):
1279 1276 default_value = traits[0]
1280 1277 traits = ()
1281 1278
1282 1279 if default_value is None:
1283 1280 args = ()
1284 1281 elif isinstance(default_value, self._valid_defaults):
1285 1282 args = (default_value,)
1286 1283 else:
1287 1284 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1288 1285
1289 1286 self._traits = []
1290 1287 for trait in traits:
1291 1288 t = trait()
1292 1289 t.name = 'element'
1293 1290 self._traits.append(t)
1294 1291
1295 1292 if self._traits and default_value is None:
1296 1293 # don't allow default to be an empty container if length is specified
1297 1294 args = None
1298 1295 super(Container,self).__init__(klass=self.klass, args=args,
1299 1296 allow_none=allow_none, **metadata)
1300 1297
1301 1298 def validate_elements(self, obj, value):
1302 1299 if not self._traits:
1303 1300 # nothing to validate
1304 1301 return value
1305 1302 if len(value) != len(self._traits):
1306 1303 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1307 1304 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1308 1305 raise TraitError(e)
1309 1306
1310 1307 validated = []
1311 1308 for t,v in zip(self._traits, value):
1312 1309 try:
1313 1310 v = t.validate(obj, v)
1314 1311 except TraitError:
1315 1312 self.element_error(obj, v, t)
1316 1313 else:
1317 1314 validated.append(v)
1318 1315 return tuple(validated)
1319 1316
1320 1317
1321 1318 class Dict(Instance):
1322 1319 """An instance of a Python dict."""
1323 1320
1324 1321 def __init__(self, default_value=None, allow_none=True, **metadata):
1325 1322 """Create a dict trait type from a dict.
1326 1323
1327 1324 The default value is created by doing ``dict(default_value)``,
1328 1325 which creates a copy of the ``default_value``.
1329 1326 """
1330 1327 if default_value is None:
1331 1328 args = ((),)
1332 1329 elif isinstance(default_value, dict):
1333 1330 args = (default_value,)
1334 1331 elif isinstance(default_value, SequenceTypes):
1335 1332 args = (default_value,)
1336 1333 else:
1337 1334 raise TypeError('default value of Dict was %s' % default_value)
1338 1335
1339 1336 super(Dict,self).__init__(klass=dict, args=args,
1340 1337 allow_none=allow_none, **metadata)
1341 1338
1342 1339 class TCPAddress(TraitType):
1343 1340 """A trait for an (ip, port) tuple.
1344 1341
1345 1342 This allows for both IPv4 IP addresses as well as hostnames.
1346 1343 """
1347 1344
1348 1345 default_value = ('127.0.0.1', 0)
1349 1346 info_text = 'an (ip, port) tuple'
1350 1347
1351 1348 def validate(self, obj, value):
1352 1349 if isinstance(value, tuple):
1353 1350 if len(value) == 2:
1354 1351 if isinstance(value[0], basestring) and isinstance(value[1], int):
1355 1352 port = value[1]
1356 1353 if port >= 0 and port <= 65535:
1357 1354 return value
1358 1355 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now