##// END OF EJS Templates
Correct description for Bytes traitlet type...
Thomas Kluyver -
Show More
@@ -1,1438 +1,1438 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 Default Value
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 info_text = 'a string'
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 1027 return py3compat.unicode_type(value)
1028 1028 self.error(obj, value)
1029 1029
1030 1030
1031 1031 class CUnicode(Unicode):
1032 1032 """A casting version of the unicode trait."""
1033 1033
1034 1034 def validate(self, obj, value):
1035 1035 try:
1036 1036 return py3compat.unicode_type(value)
1037 1037 except:
1038 1038 self.error(obj, value)
1039 1039
1040 1040
1041 1041 class ObjectName(TraitType):
1042 1042 """A string holding a valid object name in this version of Python.
1043 1043
1044 1044 This does not check that the name exists in any scope."""
1045 1045 info_text = "a valid object identifier in Python"
1046 1046
1047 1047 if py3compat.PY3:
1048 1048 # Python 3:
1049 1049 coerce_str = staticmethod(lambda _,s: s)
1050 1050
1051 1051 else:
1052 1052 # Python 2:
1053 1053 def coerce_str(self, obj, value):
1054 1054 "In Python 2, coerce ascii-only unicode to str"
1055 1055 if isinstance(value, unicode):
1056 1056 try:
1057 1057 return str(value)
1058 1058 except UnicodeEncodeError:
1059 1059 self.error(obj, value)
1060 1060 return value
1061 1061
1062 1062 def validate(self, obj, value):
1063 1063 value = self.coerce_str(obj, value)
1064 1064
1065 1065 if isinstance(value, str) and py3compat.isidentifier(value):
1066 1066 return value
1067 1067 self.error(obj, value)
1068 1068
1069 1069 class DottedObjectName(ObjectName):
1070 1070 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1071 1071 def validate(self, obj, value):
1072 1072 value = self.coerce_str(obj, value)
1073 1073
1074 1074 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1075 1075 return value
1076 1076 self.error(obj, value)
1077 1077
1078 1078
1079 1079 class Bool(TraitType):
1080 1080 """A boolean (True, False) trait."""
1081 1081
1082 1082 default_value = False
1083 1083 info_text = 'a boolean'
1084 1084
1085 1085 def validate(self, obj, value):
1086 1086 if isinstance(value, bool):
1087 1087 return value
1088 1088 self.error(obj, value)
1089 1089
1090 1090
1091 1091 class CBool(Bool):
1092 1092 """A casting version of the boolean trait."""
1093 1093
1094 1094 def validate(self, obj, value):
1095 1095 try:
1096 1096 return bool(value)
1097 1097 except:
1098 1098 self.error(obj, value)
1099 1099
1100 1100
1101 1101 class Enum(TraitType):
1102 1102 """An enum that whose value must be in a given sequence."""
1103 1103
1104 1104 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1105 1105 self.values = values
1106 1106 self._allow_none = allow_none
1107 1107 super(Enum, self).__init__(default_value, **metadata)
1108 1108
1109 1109 def validate(self, obj, value):
1110 1110 if value is None:
1111 1111 if self._allow_none:
1112 1112 return value
1113 1113
1114 1114 if value in self.values:
1115 1115 return value
1116 1116 self.error(obj, value)
1117 1117
1118 1118 def info(self):
1119 1119 """ Returns a description of the trait."""
1120 1120 result = 'any of ' + repr(self.values)
1121 1121 if self._allow_none:
1122 1122 return result + ' or None'
1123 1123 return result
1124 1124
1125 1125 class CaselessStrEnum(Enum):
1126 1126 """An enum of strings that are caseless in validate."""
1127 1127
1128 1128 def validate(self, obj, value):
1129 1129 if value is None:
1130 1130 if self._allow_none:
1131 1131 return value
1132 1132
1133 1133 if not isinstance(value, py3compat.string_types):
1134 1134 self.error(obj, value)
1135 1135
1136 1136 for v in self.values:
1137 1137 if v.lower() == value.lower():
1138 1138 return v
1139 1139 self.error(obj, value)
1140 1140
1141 1141 class Container(Instance):
1142 1142 """An instance of a container (list, set, etc.)
1143 1143
1144 1144 To be subclassed by overriding klass.
1145 1145 """
1146 1146 klass = None
1147 1147 _valid_defaults = SequenceTypes
1148 1148 _trait = None
1149 1149
1150 1150 def __init__(self, trait=None, default_value=None, allow_none=True,
1151 1151 **metadata):
1152 1152 """Create a container trait type from a list, set, or tuple.
1153 1153
1154 1154 The default value is created by doing ``List(default_value)``,
1155 1155 which creates a copy of the ``default_value``.
1156 1156
1157 1157 ``trait`` can be specified, which restricts the type of elements
1158 1158 in the container to that TraitType.
1159 1159
1160 1160 If only one arg is given and it is not a Trait, it is taken as
1161 1161 ``default_value``:
1162 1162
1163 1163 ``c = List([1,2,3])``
1164 1164
1165 1165 Parameters
1166 1166 ----------
1167 1167
1168 1168 trait : TraitType [ optional ]
1169 1169 the type for restricting the contents of the Container. If unspecified,
1170 1170 types are not checked.
1171 1171
1172 1172 default_value : SequenceType [ optional ]
1173 1173 The default value for the Trait. Must be list/tuple/set, and
1174 1174 will be cast to the container type.
1175 1175
1176 1176 allow_none : Bool [ default True ]
1177 1177 Whether to allow the value to be None
1178 1178
1179 1179 **metadata : any
1180 1180 further keys for extensions to the Trait (e.g. config)
1181 1181
1182 1182 """
1183 1183 # allow List([values]):
1184 1184 if default_value is None and not is_trait(trait):
1185 1185 default_value = trait
1186 1186 trait = None
1187 1187
1188 1188 if default_value is None:
1189 1189 args = ()
1190 1190 elif isinstance(default_value, self._valid_defaults):
1191 1191 args = (default_value,)
1192 1192 else:
1193 1193 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1194 1194
1195 1195 if is_trait(trait):
1196 1196 self._trait = trait() if isinstance(trait, type) else trait
1197 1197 self._trait.name = 'element'
1198 1198 elif trait is not None:
1199 1199 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1200 1200
1201 1201 super(Container,self).__init__(klass=self.klass, args=args,
1202 1202 allow_none=allow_none, **metadata)
1203 1203
1204 1204 def element_error(self, obj, element, validator):
1205 1205 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1206 1206 % (self.name, class_of(obj), validator.info(), repr_type(element))
1207 1207 raise TraitError(e)
1208 1208
1209 1209 def validate(self, obj, value):
1210 1210 value = super(Container, self).validate(obj, value)
1211 1211 if value is None:
1212 1212 return value
1213 1213
1214 1214 value = self.validate_elements(obj, value)
1215 1215
1216 1216 return value
1217 1217
1218 1218 def validate_elements(self, obj, value):
1219 1219 validated = []
1220 1220 if self._trait is None or isinstance(self._trait, Any):
1221 1221 return value
1222 1222 for v in value:
1223 1223 try:
1224 1224 v = self._trait.validate(obj, v)
1225 1225 except TraitError:
1226 1226 self.element_error(obj, v, self._trait)
1227 1227 else:
1228 1228 validated.append(v)
1229 1229 return self.klass(validated)
1230 1230
1231 1231
1232 1232 class List(Container):
1233 1233 """An instance of a Python list."""
1234 1234 klass = list
1235 1235
1236 1236 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1237 1237 allow_none=True, **metadata):
1238 1238 """Create a List trait type from a list, set, or tuple.
1239 1239
1240 1240 The default value is created by doing ``List(default_value)``,
1241 1241 which creates a copy of the ``default_value``.
1242 1242
1243 1243 ``trait`` can be specified, which restricts the type of elements
1244 1244 in the container to that TraitType.
1245 1245
1246 1246 If only one arg is given and it is not a Trait, it is taken as
1247 1247 ``default_value``:
1248 1248
1249 1249 ``c = List([1,2,3])``
1250 1250
1251 1251 Parameters
1252 1252 ----------
1253 1253
1254 1254 trait : TraitType [ optional ]
1255 1255 the type for restricting the contents of the Container. If unspecified,
1256 1256 types are not checked.
1257 1257
1258 1258 default_value : SequenceType [ optional ]
1259 1259 The default value for the Trait. Must be list/tuple/set, and
1260 1260 will be cast to the container type.
1261 1261
1262 1262 minlen : Int [ default 0 ]
1263 1263 The minimum length of the input list
1264 1264
1265 1265 maxlen : Int [ default sys.maxsize ]
1266 1266 The maximum length of the input list
1267 1267
1268 1268 allow_none : Bool [ default True ]
1269 1269 Whether to allow the value to be None
1270 1270
1271 1271 **metadata : any
1272 1272 further keys for extensions to the Trait (e.g. config)
1273 1273
1274 1274 """
1275 1275 self._minlen = minlen
1276 1276 self._maxlen = maxlen
1277 1277 super(List, self).__init__(trait=trait, default_value=default_value,
1278 1278 allow_none=allow_none, **metadata)
1279 1279
1280 1280 def length_error(self, obj, value):
1281 1281 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1282 1282 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1283 1283 raise TraitError(e)
1284 1284
1285 1285 def validate_elements(self, obj, value):
1286 1286 length = len(value)
1287 1287 if length < self._minlen or length > self._maxlen:
1288 1288 self.length_error(obj, value)
1289 1289
1290 1290 return super(List, self).validate_elements(obj, value)
1291 1291
1292 1292
1293 1293 class Set(Container):
1294 1294 """An instance of a Python set."""
1295 1295 klass = set
1296 1296
1297 1297 class Tuple(Container):
1298 1298 """An instance of a Python tuple."""
1299 1299 klass = tuple
1300 1300
1301 1301 def __init__(self, *traits, **metadata):
1302 1302 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1303 1303
1304 1304 Create a tuple from a list, set, or tuple.
1305 1305
1306 1306 Create a fixed-type tuple with Traits:
1307 1307
1308 1308 ``t = Tuple(Int, Str, CStr)``
1309 1309
1310 1310 would be length 3, with Int,Str,CStr for each element.
1311 1311
1312 1312 If only one arg is given and it is not a Trait, it is taken as
1313 1313 default_value:
1314 1314
1315 1315 ``t = Tuple((1,2,3))``
1316 1316
1317 1317 Otherwise, ``default_value`` *must* be specified by keyword.
1318 1318
1319 1319 Parameters
1320 1320 ----------
1321 1321
1322 1322 *traits : TraitTypes [ optional ]
1323 1323 the tsype for restricting the contents of the Tuple. If unspecified,
1324 1324 types are not checked. If specified, then each positional argument
1325 1325 corresponds to an element of the tuple. Tuples defined with traits
1326 1326 are of fixed length.
1327 1327
1328 1328 default_value : SequenceType [ optional ]
1329 1329 The default value for the Tuple. Must be list/tuple/set, and
1330 1330 will be cast to a tuple. If `traits` are specified, the
1331 1331 `default_value` must conform to the shape and type they specify.
1332 1332
1333 1333 allow_none : Bool [ default True ]
1334 1334 Whether to allow the value to be None
1335 1335
1336 1336 **metadata : any
1337 1337 further keys for extensions to the Trait (e.g. config)
1338 1338
1339 1339 """
1340 1340 default_value = metadata.pop('default_value', None)
1341 1341 allow_none = metadata.pop('allow_none', True)
1342 1342
1343 1343 # allow Tuple((values,)):
1344 1344 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1345 1345 default_value = traits[0]
1346 1346 traits = ()
1347 1347
1348 1348 if default_value is None:
1349 1349 args = ()
1350 1350 elif isinstance(default_value, self._valid_defaults):
1351 1351 args = (default_value,)
1352 1352 else:
1353 1353 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1354 1354
1355 1355 self._traits = []
1356 1356 for trait in traits:
1357 1357 t = trait() if isinstance(trait, type) else trait
1358 1358 t.name = 'element'
1359 1359 self._traits.append(t)
1360 1360
1361 1361 if self._traits and default_value is None:
1362 1362 # don't allow default to be an empty container if length is specified
1363 1363 args = None
1364 1364 super(Container,self).__init__(klass=self.klass, args=args,
1365 1365 allow_none=allow_none, **metadata)
1366 1366
1367 1367 def validate_elements(self, obj, value):
1368 1368 if not self._traits:
1369 1369 # nothing to validate
1370 1370 return value
1371 1371 if len(value) != len(self._traits):
1372 1372 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1373 1373 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1374 1374 raise TraitError(e)
1375 1375
1376 1376 validated = []
1377 1377 for t,v in zip(self._traits, value):
1378 1378 try:
1379 1379 v = t.validate(obj, v)
1380 1380 except TraitError:
1381 1381 self.element_error(obj, v, t)
1382 1382 else:
1383 1383 validated.append(v)
1384 1384 return tuple(validated)
1385 1385
1386 1386
1387 1387 class Dict(Instance):
1388 1388 """An instance of a Python dict."""
1389 1389
1390 1390 def __init__(self, default_value=None, allow_none=True, **metadata):
1391 1391 """Create a dict trait type from a dict.
1392 1392
1393 1393 The default value is created by doing ``dict(default_value)``,
1394 1394 which creates a copy of the ``default_value``.
1395 1395 """
1396 1396 if default_value is None:
1397 1397 args = ((),)
1398 1398 elif isinstance(default_value, dict):
1399 1399 args = (default_value,)
1400 1400 elif isinstance(default_value, SequenceTypes):
1401 1401 args = (default_value,)
1402 1402 else:
1403 1403 raise TypeError('default value of Dict was %s' % default_value)
1404 1404
1405 1405 super(Dict,self).__init__(klass=dict, args=args,
1406 1406 allow_none=allow_none, **metadata)
1407 1407
1408 1408 class TCPAddress(TraitType):
1409 1409 """A trait for an (ip, port) tuple.
1410 1410
1411 1411 This allows for both IPv4 IP addresses as well as hostnames.
1412 1412 """
1413 1413
1414 1414 default_value = ('127.0.0.1', 0)
1415 1415 info_text = 'an (ip, port) tuple'
1416 1416
1417 1417 def validate(self, obj, value):
1418 1418 if isinstance(value, tuple):
1419 1419 if len(value) == 2:
1420 1420 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1421 1421 port = value[1]
1422 1422 if port >= 0 and port <= 65535:
1423 1423 return value
1424 1424 self.error(obj, value)
1425 1425
1426 1426 class CRegExp(TraitType):
1427 1427 """A casting compiled regular expression trait.
1428 1428
1429 1429 Accepts both strings and compiled regular expressions. The resulting
1430 1430 attribute will be a compiled regular expression."""
1431 1431
1432 1432 info_text = 'a regular expression'
1433 1433
1434 1434 def validate(self, obj, value):
1435 1435 try:
1436 1436 return re.compile(value)
1437 1437 except:
1438 1438 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now