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