##// END OF EJS Templates
Allow trait values to be set in the keyword arguments of __init__.
Brian Granger -
Show More
@@ -1,1047 +1,1048 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
60 60 def import_item(name):
61 61 """Import and return bar given the string foo.bar."""
62 62 package = '.'.join(name.split('.')[0:-1])
63 63 obj = name.split('.')[-1]
64 64 execString = 'from %s import %s' % (package, obj)
65 65 try:
66 66 exec execString
67 67 except SyntaxError:
68 68 raise ImportError("Invalid class specification: %s" % name)
69 69 exec 'temp = %s' % obj
70 70 return temp
71 71
72 72
73 73 ClassTypes = (ClassType, type)
74 74
75 75 SequenceTypes = (ListType, TupleType)
76 76
77 77 #-----------------------------------------------------------------------------
78 78 # Basic classes
79 79 #-----------------------------------------------------------------------------
80 80
81 81
82 82 class NoDefaultSpecified ( object ): pass
83 83 NoDefaultSpecified = NoDefaultSpecified()
84 84
85 85
86 86 class Undefined ( object ): pass
87 87 Undefined = Undefined()
88 88
89 89 class TraitError(Exception):
90 90 pass
91 91
92 92 #-----------------------------------------------------------------------------
93 93 # Utilities
94 94 #-----------------------------------------------------------------------------
95 95
96 96
97 97 def class_of ( object ):
98 98 """ Returns a string containing the class name of an object with the
99 99 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
100 100 'a PlotValue').
101 101 """
102 102 if isinstance( object, basestring ):
103 103 return add_article( object )
104 104
105 105 return add_article( object.__class__.__name__ )
106 106
107 107
108 108 def add_article ( name ):
109 109 """ Returns a string containing the correct indefinite article ('a' or 'an')
110 110 prefixed to the specified string.
111 111 """
112 112 if name[:1].lower() in 'aeiou':
113 113 return 'an ' + name
114 114
115 115 return 'a ' + name
116 116
117 117
118 118 def repr_type(obj):
119 119 """ Return a string representation of a value and its type for readable
120 120 error messages.
121 121 """
122 122 the_type = type(obj)
123 123 if the_type is InstanceType:
124 124 # Old-style class.
125 125 the_type = obj.__class__
126 126 msg = '%r %r' % (obj, the_type)
127 127 return msg
128 128
129 129
130 130 def parse_notifier_name(name):
131 131 """Convert the name argument to a list of names.
132 132
133 133 Examples
134 134 --------
135 135
136 136 >>> parse_notifier_name('a')
137 137 ['a']
138 138 >>> parse_notifier_name(['a','b'])
139 139 ['a', 'b']
140 140 >>> parse_notifier_name(None)
141 141 ['anytrait']
142 142 """
143 143 if isinstance(name, str):
144 144 return [name]
145 145 elif name is None:
146 146 return ['anytrait']
147 147 elif isinstance(name, (list, tuple)):
148 148 for n in name:
149 149 assert isinstance(n, str), "names must be strings"
150 150 return name
151 151
152 152
153 153 class _SimpleTest:
154 154 def __init__ ( self, value ): self.value = value
155 155 def __call__ ( self, test ):
156 156 return test == self.value
157 157 def __repr__(self):
158 158 return "<SimpleTest(%r)" % self.value
159 159 def __str__(self):
160 160 return self.__repr__()
161 161
162 162
163 163 def getmembers(object, predicate=None):
164 164 """A safe version of inspect.getmembers that handles missing attributes.
165 165
166 166 This is useful when there are descriptor based attributes that for
167 167 some reason raise AttributeError even though they exist. This happens
168 168 in zope.inteface with the __provides__ attribute.
169 169 """
170 170 results = []
171 171 for key in dir(object):
172 172 try:
173 173 value = getattr(object, key)
174 174 except AttributeError:
175 175 pass
176 176 else:
177 177 if not predicate or predicate(value):
178 178 results.append((key, value))
179 179 results.sort()
180 180 return results
181 181
182 182
183 183 #-----------------------------------------------------------------------------
184 184 # Base TraitType for all traits
185 185 #-----------------------------------------------------------------------------
186 186
187 187
188 188 class TraitType(object):
189 189 """A base class for all trait descriptors.
190 190
191 191 Notes
192 192 -----
193 193 Our implementation of traits is based on Python's descriptor
194 194 prototol. This class is the base class for all such descriptors. The
195 195 only magic we use is a custom metaclass for the main :class:`HasTraits`
196 196 class that does the following:
197 197
198 198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
199 199 instance in the class dict to the name of the attribute.
200 200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
201 201 instance in the class dict to the *class* that declared the trait.
202 202 This is used by the :class:`This` trait to allow subclasses to
203 203 accept superclasses for :class:`This` values.
204 204 """
205 205
206 206
207 207 metadata = {}
208 208 default_value = Undefined
209 209 info_text = 'any value'
210 210
211 211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
212 212 """Create a TraitType.
213 213 """
214 214 if default_value is not NoDefaultSpecified:
215 215 self.default_value = default_value
216 216
217 217 if len(metadata) > 0:
218 218 if len(self.metadata) > 0:
219 219 self._metadata = self.metadata.copy()
220 220 self._metadata.update(metadata)
221 221 else:
222 222 self._metadata = metadata
223 223 else:
224 224 self._metadata = self.metadata
225 225
226 226 self.init()
227 227
228 228 def init(self):
229 229 pass
230 230
231 231 def get_default_value(self):
232 232 """Create a new instance of the default value."""
233 233 dv = self.default_value
234 234 return dv
235 235
236 236 def instance_init(self, obj):
237 237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
238 238
239 239 Some stages of initialization must be delayed until the parent
240 240 :class:`HasTraits` instance has been created. This method is
241 241 called in :meth:`HasTraits.__new__` after the instance has been
242 242 created.
243 243
244 244 This method trigger the creation and validation of default values
245 245 and also things like the resolution of str given class names in
246 246 :class:`Type` and :class`Instance`.
247 247
248 248 Parameters
249 249 ----------
250 250 obj : :class:`HasTraits` instance
251 251 The parent :class:`HasTraits` instance that has just been
252 252 created.
253 253 """
254 254 self.set_default_value(obj)
255 255
256 256 def set_default_value(self, obj):
257 257 """Set the default value on a per instance basis.
258 258
259 259 This method is called by :meth:`instance_init` to create and
260 260 validate the default value. The creation and validation of
261 261 default values must be delayed until the parent :class:`HasTraits`
262 262 class has been instantiated.
263 263 """
264 264 dv = self.get_default_value()
265 265 newdv = self._validate(obj, dv)
266 266 obj._trait_values[self.name] = newdv
267 267
268 268 def __get__(self, obj, cls=None):
269 269 """Get the value of the trait by self.name for the instance.
270 270
271 271 Default values are instantiated when :meth:`HasTraits.__new__`
272 272 is called. Thus by the time this method gets called either the
273 273 default value or a user defined value (they called :meth:`__set__`)
274 274 is in the :class:`HasTraits` instance.
275 275 """
276 276 if obj is None:
277 277 return self
278 278 else:
279 279 try:
280 280 value = obj._trait_values[self.name]
281 281 except:
282 282 # HasTraits should call set_default_value to populate
283 283 # this. So this should never be reached.
284 284 raise TraitError('Unexpected error in TraitType: '
285 285 'default value not set properly')
286 286 else:
287 287 return value
288 288
289 289 def __set__(self, obj, value):
290 290 new_value = self._validate(obj, value)
291 291 old_value = self.__get__(obj)
292 292 if old_value != new_value:
293 293 obj._trait_values[self.name] = new_value
294 294 obj._notify_trait(self.name, old_value, new_value)
295 295
296 296 def _validate(self, obj, value):
297 297 if hasattr(self, 'validate'):
298 298 return self.validate(obj, value)
299 299 elif hasattr(self, 'is_valid_for'):
300 300 valid = self.is_valid_for(value)
301 301 if valid:
302 302 return value
303 303 else:
304 304 raise TraitError('invalid value for type: %r' % value)
305 305 elif hasattr(self, 'value_for'):
306 306 return self.value_for(value)
307 307 else:
308 308 return value
309 309
310 310 def info(self):
311 311 return self.info_text
312 312
313 313 def error(self, obj, value):
314 314 if obj is not None:
315 315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
316 316 % (self.name, class_of(obj),
317 317 self.info(), repr_type(value))
318 318 else:
319 319 e = "The '%s' trait must be %s, but a value of %r was specified." \
320 320 % (self.name, self.info(), repr_type(value))
321 321 raise TraitError(e)
322 322
323 323 def get_metadata(self, key):
324 324 return getattr(self, '_metadata', {}).get(key, None)
325 325
326 326 def set_metadata(self, key, value):
327 327 getattr(self, '_metadata', {})[key] = value
328 328
329 329
330 330 #-----------------------------------------------------------------------------
331 331 # The HasTraits implementation
332 332 #-----------------------------------------------------------------------------
333 333
334 334
335 335 class MetaHasTraits(type):
336 336 """A metaclass for HasTraits.
337 337
338 338 This metaclass makes sure that any TraitType class attributes are
339 339 instantiated and sets their name attribute.
340 340 """
341 341
342 342 def __new__(mcls, name, bases, classdict):
343 343 """Create the HasTraits class.
344 344
345 345 This instantiates all TraitTypes in the class dict and sets their
346 346 :attr:`name` attribute.
347 347 """
348 348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
349 349 # print "MetaHasTraitlets (bases): ", bases
350 350 # print "MetaHasTraitlets (classdict): ", classdict
351 351 for k,v in classdict.iteritems():
352 352 if isinstance(v, TraitType):
353 353 v.name = k
354 354 elif inspect.isclass(v):
355 355 if issubclass(v, TraitType):
356 356 vinst = v()
357 357 vinst.name = k
358 358 classdict[k] = vinst
359 359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
360 360
361 361 def __init__(cls, name, bases, classdict):
362 362 """Finish initializing the HasTraits class.
363 363
364 364 This sets the :attr:`this_class` attribute of each TraitType in the
365 365 class dict to the newly created class ``cls``.
366 366 """
367 367 for k, v in classdict.iteritems():
368 368 if isinstance(v, TraitType):
369 369 v.this_class = cls
370 370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
371 371
372 372 class HasTraits(object):
373 373
374 374 __metaclass__ = MetaHasTraits
375 375
376 def __new__(cls, *args, **kw):
376 def __new__(cls, **kw):
377 377 # This is needed because in Python 2.6 object.__new__ only accepts
378 378 # the cls argument.
379 379 new_meth = super(HasTraits, cls).__new__
380 380 if new_meth is object.__new__:
381 381 inst = new_meth(cls)
382 382 else:
383 inst = new_meth(cls, *args, **kw)
383 inst = new_meth(cls, **kw)
384 384 inst._trait_values = {}
385 385 inst._trait_notifiers = {}
386 386 # Here we tell all the TraitType instances to set their default
387 387 # values on the instance.
388 388 for key in dir(cls):
389 389 # Some descriptors raise AttributeError like zope.interface's
390 390 # __provides__ attributes even though they exist. This causes
391 391 # AttributeErrors even though they are listed in dir(cls).
392 392 try:
393 393 value = getattr(cls, key)
394 394 except AttributeError:
395 395 pass
396 396 else:
397 397 if isinstance(value, TraitType):
398 398 value.instance_init(inst)
399 399
400 400 return inst
401 401
402 # def __init__(self):
403 # self._trait_values = {}
404 # self._trait_notifiers = {}
402 def __init__(self, *kw):
403 # Allow trait values to be set using keywork arguments.
404 for key, value in kw.iteritems():
405 setattr(self, key, value)
405 406
406 407 def _notify_trait(self, name, old_value, new_value):
407 408
408 409 # First dynamic ones
409 410 callables = self._trait_notifiers.get(name,[])
410 411 more_callables = self._trait_notifiers.get('anytrait',[])
411 412 callables.extend(more_callables)
412 413
413 414 # Now static ones
414 415 try:
415 416 cb = getattr(self, '_%s_changed' % name)
416 417 except:
417 418 pass
418 419 else:
419 420 callables.append(cb)
420 421
421 422 # Call them all now
422 423 for c in callables:
423 424 # Traits catches and logs errors here. I allow them to raise
424 425 if callable(c):
425 426 argspec = inspect.getargspec(c)
426 427 nargs = len(argspec[0])
427 428 # Bound methods have an additional 'self' argument
428 429 # I don't know how to treat unbound methods, but they
429 430 # can't really be used for callbacks.
430 431 if isinstance(c, types.MethodType):
431 432 offset = -1
432 433 else:
433 434 offset = 0
434 435 if nargs + offset == 0:
435 436 c()
436 437 elif nargs + offset == 1:
437 438 c(name)
438 439 elif nargs + offset == 2:
439 440 c(name, new_value)
440 441 elif nargs + offset == 3:
441 442 c(name, old_value, new_value)
442 443 else:
443 444 raise TraitError('a trait changed callback '
444 445 'must have 0-3 arguments.')
445 446 else:
446 447 raise TraitError('a trait changed callback '
447 448 'must be callable.')
448 449
449 450
450 451 def _add_notifiers(self, handler, name):
451 452 if not self._trait_notifiers.has_key(name):
452 453 nlist = []
453 454 self._trait_notifiers[name] = nlist
454 455 else:
455 456 nlist = self._trait_notifiers[name]
456 457 if handler not in nlist:
457 458 nlist.append(handler)
458 459
459 460 def _remove_notifiers(self, handler, name):
460 461 if self._trait_notifiers.has_key(name):
461 462 nlist = self._trait_notifiers[name]
462 463 try:
463 464 index = nlist.index(handler)
464 465 except ValueError:
465 466 pass
466 467 else:
467 468 del nlist[index]
468 469
469 470 def on_trait_change(self, handler, name=None, remove=False):
470 471 """Setup a handler to be called when a trait changes.
471 472
472 473 This is used to setup dynamic notifications of trait changes.
473 474
474 475 Static handlers can be created by creating methods on a HasTraits
475 476 subclass with the naming convention '_[traitname]_changed'. Thus,
476 477 to create static handler for the trait 'a', create the method
477 478 _a_changed(self, name, old, new) (fewer arguments can be used, see
478 479 below).
479 480
480 481 Parameters
481 482 ----------
482 483 handler : callable
483 484 A callable that is called when a trait changes. Its
484 485 signature can be handler(), handler(name), handler(name, new)
485 486 or handler(name, old, new).
486 487 name : list, str, None
487 488 If None, the handler will apply to all traits. If a list
488 489 of str, handler will apply to all names in the list. If a
489 490 str, the handler will apply just to that name.
490 491 remove : bool
491 492 If False (the default), then install the handler. If True
492 493 then unintall it.
493 494 """
494 495 if remove:
495 496 names = parse_notifier_name(name)
496 497 for n in names:
497 498 self._remove_notifiers(handler, n)
498 499 else:
499 500 names = parse_notifier_name(name)
500 501 for n in names:
501 502 self._add_notifiers(handler, n)
502 503
503 504 def trait_names(self, **metadata):
504 505 """Get a list of all the names of this classes traits."""
505 506 return self.traits(**metadata).keys()
506 507
507 508 def traits(self, **metadata):
508 509 """Get a list of all the traits of this class.
509 510
510 511 The TraitTypes returned don't know anything about the values
511 512 that the various HasTrait's instances are holding.
512 513
513 514 This follows the same algorithm as traits does and does not allow
514 515 for any simple way of specifying merely that a metadata name
515 516 exists, but has any value. This is because get_metadata returns
516 517 None if a metadata key doesn't exist.
517 518 """
518 519 traits = dict([memb for memb in getmembers(self.__class__) if \
519 520 isinstance(memb[1], TraitType)])
520 521
521 522 if len(metadata) == 0:
522 523 return traits
523 524
524 525 for meta_name, meta_eval in metadata.items():
525 526 if type(meta_eval) is not FunctionType:
526 527 metadata[meta_name] = _SimpleTest(meta_eval)
527 528
528 529 result = {}
529 530 for name, trait in traits.items():
530 531 for meta_name, meta_eval in metadata.items():
531 532 if not meta_eval(trait.get_metadata(meta_name)):
532 533 break
533 534 else:
534 535 result[name] = trait
535 536
536 537 return result
537 538
538 539 def trait_metadata(self, traitname, key):
539 540 """Get metadata values for trait by key."""
540 541 try:
541 542 trait = getattr(self.__class__, traitname)
542 543 except AttributeError:
543 544 raise TraitError("Class %s does not have a trait named %s" %
544 545 (self.__class__.__name__, traitname))
545 546 else:
546 547 return trait.get_metadata(key)
547 548
548 549 #-----------------------------------------------------------------------------
549 550 # Actual TraitTypes implementations/subclasses
550 551 #-----------------------------------------------------------------------------
551 552
552 553 #-----------------------------------------------------------------------------
553 554 # TraitTypes subclasses for handling classes and instances of classes
554 555 #-----------------------------------------------------------------------------
555 556
556 557
557 558 class ClassBasedTraitType(TraitType):
558 559 """A trait with error reporting for Type, Instance and This."""
559 560
560 561 def error(self, obj, value):
561 562 kind = type(value)
562 563 if kind is InstanceType:
563 564 msg = 'class %s' % value.__class__.__name__
564 565 else:
565 566 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
566 567
567 568 super(ClassBasedTraitType, self).error(obj, msg)
568 569
569 570
570 571 class Type(ClassBasedTraitType):
571 572 """A trait whose value must be a subclass of a specified class."""
572 573
573 574 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
574 575 """Construct a Type trait
575 576
576 577 A Type trait specifies that its values must be subclasses of
577 578 a particular class.
578 579
579 580 If only ``default_value`` is given, it is used for the ``klass`` as
580 581 well.
581 582
582 583 Parameters
583 584 ----------
584 585 default_value : class, str or None
585 586 The default value must be a subclass of klass. If an str,
586 587 the str must be a fully specified class name, like 'foo.bar.Bah'.
587 588 The string is resolved into real class, when the parent
588 589 :class:`HasTraits` class is instantiated.
589 590 klass : class, str, None
590 591 Values of this trait must be a subclass of klass. The klass
591 592 may be specified in a string like: 'foo.bar.MyClass'.
592 593 The string is resolved into real class, when the parent
593 594 :class:`HasTraits` class is instantiated.
594 595 allow_none : boolean
595 596 Indicates whether None is allowed as an assignable value. Even if
596 597 ``False``, the default value may be ``None``.
597 598 """
598 599 if default_value is None:
599 600 if klass is None:
600 601 klass = object
601 602 elif klass is None:
602 603 klass = default_value
603 604
604 605 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
605 606 raise TraitError("A Type trait must specify a class.")
606 607
607 608 self.klass = klass
608 609 self._allow_none = allow_none
609 610
610 611 super(Type, self).__init__(default_value, **metadata)
611 612
612 613 def validate(self, obj, value):
613 614 """Validates that the value is a valid object instance."""
614 615 try:
615 616 if issubclass(value, self.klass):
616 617 return value
617 618 except:
618 619 if (value is None) and (self._allow_none):
619 620 return value
620 621
621 622 self.error(obj, value)
622 623
623 624 def info(self):
624 625 """ Returns a description of the trait."""
625 626 if isinstance(self.klass, basestring):
626 627 klass = self.klass
627 628 else:
628 629 klass = self.klass.__name__
629 630 result = 'a subclass of ' + klass
630 631 if self._allow_none:
631 632 return result + ' or None'
632 633 return result
633 634
634 635 def instance_init(self, obj):
635 636 self._resolve_classes()
636 637 super(Type, self).instance_init(obj)
637 638
638 639 def _resolve_classes(self):
639 640 if isinstance(self.klass, basestring):
640 641 self.klass = import_item(self.klass)
641 642 if isinstance(self.default_value, basestring):
642 643 self.default_value = import_item(self.default_value)
643 644
644 645 def get_default_value(self):
645 646 return self.default_value
646 647
647 648
648 649 class DefaultValueGenerator(object):
649 650 """A class for generating new default value instances."""
650 651
651 652 def __init__(self, *args, **kw):
652 653 self.args = args
653 654 self.kw = kw
654 655
655 656 def generate(self, klass):
656 657 return klass(*self.args, **self.kw)
657 658
658 659
659 660 class Instance(ClassBasedTraitType):
660 661 """A trait whose value must be an instance of a specified class.
661 662
662 663 The value can also be an instance of a subclass of the specified class.
663 664 """
664 665
665 666 def __init__(self, klass=None, args=None, kw=None,
666 667 allow_none=True, **metadata ):
667 668 """Construct an Instance trait.
668 669
669 670 This trait allows values that are instances of a particular
670 671 class or its sublclasses. Our implementation is quite different
671 672 from that of enthough.traits as we don't allow instances to be used
672 673 for klass and we handle the ``args`` and ``kw`` arguments differently.
673 674
674 675 Parameters
675 676 ----------
676 677 klass : class, str
677 678 The class that forms the basis for the trait. Class names
678 679 can also be specified as strings, like 'foo.bar.Bar'.
679 680 args : tuple
680 681 Positional arguments for generating the default value.
681 682 kw : dict
682 683 Keyword arguments for generating the default value.
683 684 allow_none : bool
684 685 Indicates whether None is allowed as a value.
685 686
686 687 Default Value
687 688 -------------
688 689 If both ``args`` and ``kw`` are None, then the default value is None.
689 690 If ``args`` is a tuple and ``kw`` is a dict, then the default is
690 691 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
691 692 not (but not both), None is replace by ``()`` or ``{}``.
692 693 """
693 694
694 695 self._allow_none = allow_none
695 696
696 697 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
697 698 raise TraitError('The klass argument must be a class'
698 699 ' you gave: %r' % klass)
699 700 self.klass = klass
700 701
701 702 # self.klass is a class, so handle default_value
702 703 if args is None and kw is None:
703 704 default_value = None
704 705 else:
705 706 if args is None:
706 707 # kw is not None
707 708 args = ()
708 709 elif kw is None:
709 710 # args is not None
710 711 kw = {}
711 712
712 713 if not isinstance(kw, dict):
713 714 raise TraitError("The 'kw' argument must be a dict or None.")
714 715 if not isinstance(args, tuple):
715 716 raise TraitError("The 'args' argument must be a tuple or None.")
716 717
717 718 default_value = DefaultValueGenerator(*args, **kw)
718 719
719 720 super(Instance, self).__init__(default_value, **metadata)
720 721
721 722 def validate(self, obj, value):
722 723 if value is None:
723 724 if self._allow_none:
724 725 return value
725 726 self.error(obj, value)
726 727
727 728 if isinstance(value, self.klass):
728 729 return value
729 730 else:
730 731 self.error(obj, value)
731 732
732 733 def info(self):
733 734 if isinstance(self.klass, basestring):
734 735 klass = self.klass
735 736 else:
736 737 klass = self.klass.__name__
737 738 result = class_of(klass)
738 739 if self._allow_none:
739 740 return result + ' or None'
740 741
741 742 return result
742 743
743 744 def instance_init(self, obj):
744 745 self._resolve_classes()
745 746 super(Instance, self).instance_init(obj)
746 747
747 748 def _resolve_classes(self):
748 749 if isinstance(self.klass, basestring):
749 750 self.klass = import_item(self.klass)
750 751
751 752 def get_default_value(self):
752 753 """Instantiate a default value instance.
753 754
754 755 This is called when the containing HasTraits classes'
755 756 :meth:`__new__` method is called to ensure that a unique instance
756 757 is created for each HasTraits instance.
757 758 """
758 759 dv = self.default_value
759 760 if isinstance(dv, DefaultValueGenerator):
760 761 return dv.generate(self.klass)
761 762 else:
762 763 return dv
763 764
764 765
765 766 class This(ClassBasedTraitType):
766 767 """A trait for instances of the class containing this trait.
767 768
768 769 Because how how and when class bodies are executed, the ``This``
769 770 trait can only have a default value of None. This, and because we
770 771 always validate default values, ``allow_none`` is *always* true.
771 772 """
772 773
773 774 info_text = 'an instance of the same type as the receiver or None'
774 775
775 776 def __init__(self, **metadata):
776 777 super(This, self).__init__(None, **metadata)
777 778
778 779 def validate(self, obj, value):
779 780 # What if value is a superclass of obj.__class__? This is
780 781 # complicated if it was the superclass that defined the This
781 782 # trait.
782 783 if isinstance(value, self.this_class) or (value is None):
783 784 return value
784 785 else:
785 786 self.error(obj, value)
786 787
787 788
788 789 #-----------------------------------------------------------------------------
789 790 # Basic TraitTypes implementations/subclasses
790 791 #-----------------------------------------------------------------------------
791 792
792 793
793 794 class Any(TraitType):
794 795 default_value = None
795 796 info_text = 'any value'
796 797
797 798
798 799 class Int(TraitType):
799 800 """A integer trait."""
800 801
801 802 evaluate = int
802 803 default_value = 0
803 804 info_text = 'an integer'
804 805
805 806 def validate(self, obj, value):
806 807 if isinstance(value, int):
807 808 return value
808 809 self.error(obj, value)
809 810
810 811 class CInt(Int):
811 812 """A casting version of the int trait."""
812 813
813 814 def validate(self, obj, value):
814 815 try:
815 816 return int(value)
816 817 except:
817 818 self.error(obj, value)
818 819
819 820
820 821 class Long(TraitType):
821 822 """A long integer trait."""
822 823
823 824 evaluate = long
824 825 default_value = 0L
825 826 info_text = 'a long'
826 827
827 828 def validate(self, obj, value):
828 829 if isinstance(value, long):
829 830 return value
830 831 if isinstance(value, int):
831 832 return long(value)
832 833 self.error(obj, value)
833 834
834 835
835 836 class CLong(Long):
836 837 """A casting version of the long integer trait."""
837 838
838 839 def validate(self, obj, value):
839 840 try:
840 841 return long(value)
841 842 except:
842 843 self.error(obj, value)
843 844
844 845
845 846 class Float(TraitType):
846 847 """A float trait."""
847 848
848 849 evaluate = float
849 850 default_value = 0.0
850 851 info_text = 'a float'
851 852
852 853 def validate(self, obj, value):
853 854 if isinstance(value, float):
854 855 return value
855 856 if isinstance(value, int):
856 857 return float(value)
857 858 self.error(obj, value)
858 859
859 860
860 861 class CFloat(Float):
861 862 """A casting version of the float trait."""
862 863
863 864 def validate(self, obj, value):
864 865 try:
865 866 return float(value)
866 867 except:
867 868 self.error(obj, value)
868 869
869 870 class Complex(TraitType):
870 871 """A trait for complex numbers."""
871 872
872 873 evaluate = complex
873 874 default_value = 0.0 + 0.0j
874 875 info_text = 'a complex number'
875 876
876 877 def validate(self, obj, value):
877 878 if isinstance(value, complex):
878 879 return value
879 880 if isinstance(value, (float, int)):
880 881 return complex(value)
881 882 self.error(obj, value)
882 883
883 884
884 885 class CComplex(Complex):
885 886 """A casting version of the complex number trait."""
886 887
887 888 def validate (self, obj, value):
888 889 try:
889 890 return complex(value)
890 891 except:
891 892 self.error(obj, value)
892 893
893 894
894 895 class Str(TraitType):
895 896 """A trait for strings."""
896 897
897 898 evaluate = lambda x: x
898 899 default_value = ''
899 900 info_text = 'a string'
900 901
901 902 def validate(self, obj, value):
902 903 if isinstance(value, str):
903 904 return value
904 905 self.error(obj, value)
905 906
906 907
907 908 class CStr(Str):
908 909 """A casting version of the string trait."""
909 910
910 911 def validate(self, obj, value):
911 912 try:
912 913 return str(value)
913 914 except:
914 915 try:
915 916 return unicode(value)
916 917 except:
917 918 self.error(obj, value)
918 919
919 920
920 921 class Unicode(TraitType):
921 922 """A trait for unicode strings."""
922 923
923 924 evaluate = unicode
924 925 default_value = u''
925 926 info_text = 'a unicode string'
926 927
927 928 def validate(self, obj, value):
928 929 if isinstance(value, unicode):
929 930 return value
930 931 if isinstance(value, str):
931 932 return unicode(value)
932 933 self.error(obj, value)
933 934
934 935
935 936 class CUnicode(Unicode):
936 937 """A casting version of the unicode trait."""
937 938
938 939 def validate(self, obj, value):
939 940 try:
940 941 return unicode(value)
941 942 except:
942 943 self.error(obj, value)
943 944
944 945
945 946 class Bool(TraitType):
946 947 """A boolean (True, False) trait."""
947 948 evaluate = bool
948 949 default_value = False
949 950 info_text = 'a boolean'
950 951
951 952 def validate(self, obj, value):
952 953 if isinstance(value, bool):
953 954 return value
954 955 self.error(obj, value)
955 956
956 957
957 958 class CBool(Bool):
958 959 """A casting version of the boolean trait."""
959 960
960 961 def validate(self, obj, value):
961 962 try:
962 963 return bool(value)
963 964 except:
964 965 self.error(obj, value)
965 966
966 967
967 968 class Enum(TraitType):
968 969 """An enum that whose value must be in a given sequence."""
969 970
970 971 def __init__(self, values, default_value=None, allow_none=True, **metadata):
971 972 self.values = values
972 973 self._allow_none = allow_none
973 974 super(Enum, self).__init__(default_value, **metadata)
974 975
975 976 def validate(self, obj, value):
976 977 if value is None:
977 978 if self._allow_none:
978 979 return value
979 980
980 981 if value in self.values:
981 982 return value
982 983 self.error(obj, value)
983 984
984 985 def info(self):
985 986 """ Returns a description of the trait."""
986 987 result = 'any of ' + repr(self.values)
987 988 if self._allow_none:
988 989 return result + ' or None'
989 990 return result
990 991
991 992 class CaselessStrEnum(Enum):
992 993 """An enum of strings that are caseless in validate."""
993 994
994 995 def validate(self, obj, value):
995 996 if value is None:
996 997 if self._allow_none:
997 998 return value
998 999
999 1000 if not isinstance(value, str):
1000 1001 self.error(obj, value)
1001 1002
1002 1003 for v in self.values:
1003 1004 if v.lower() == value.lower():
1004 1005 return v
1005 1006 self.error(obj, value)
1006 1007
1007 1008
1008 1009 class List(Instance):
1009 1010 """An instance of a Python list."""
1010 1011
1011 1012 def __init__(self, default_value=None, allow_none=True, **metadata):
1012 1013 """Create a list trait type from a list or tuple.
1013 1014
1014 1015 The default value is created by doing ``list(default_value)``,
1015 1016 which creates a copy of the ``default_value``.
1016 1017 """
1017 1018 if default_value is None:
1018 1019 args = ((),)
1019 1020 elif isinstance(default_value, SequenceTypes):
1020 1021 args = (default_value,)
1021 1022 else:
1022 1023 raise TypeError('default value of List was %s' % default_value)
1023 1024
1024 1025 super(List,self).__init__(klass=list, args=args,
1025 1026 allow_none=allow_none, **metadata)
1026 1027
1027 1028
1028 1029 class Dict(Instance):
1029 1030 """An instance of a Python dict."""
1030 1031
1031 1032 def __init__(self, default_value=None, allow_none=True, **metadata):
1032 1033 """Create a dict trait type from a dict.
1033 1034
1034 1035 The default value is created by doing ``dict(default_value)``,
1035 1036 which creates a copy of the ``default_value``.
1036 1037 """
1037 1038 if default_value is None:
1038 1039 args = ((),)
1039 1040 elif isinstance(default_value, dict):
1040 1041 args = (default_value,)
1041 1042 elif isinstance(default_value, SequenceTypes):
1042 1043 args = (default_value,)
1043 1044 else:
1044 1045 raise TypeError('default value of Dict was %s' % default_value)
1045 1046
1046 1047 super(Dict,self).__init__(klass=dict, args=args,
1047 1048 allow_none=allow_none, **metadata)
General Comments 0
You need to be logged in to leave comments. Login now