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