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