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