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