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