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