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