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