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