##// END OF EJS Templates
Merge pull request #5197 from jasongrout/traitlet-notify...
Min RK -
r15458:d8e4e5e7 merge
parent child Browse files
Show More
@@ -1,1497 +1,1502 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 if old_value != new_value:
374 try:
375 notify = (old_value != new_value)
376 except:
377 # if there is an error in comparing, default to notify
378 notify = True
379 if notify:
375 obj._notify_trait(self.name, old_value, new_value)
380 obj._notify_trait(self.name, old_value, new_value)
376
381
377 def _validate(self, obj, value):
382 def _validate(self, obj, value):
378 if hasattr(self, 'validate'):
383 if hasattr(self, 'validate'):
379 return self.validate(obj, value)
384 return self.validate(obj, value)
380 elif hasattr(self, 'is_valid_for'):
385 elif hasattr(self, 'is_valid_for'):
381 valid = self.is_valid_for(value)
386 valid = self.is_valid_for(value)
382 if valid:
387 if valid:
383 return value
388 return value
384 else:
389 else:
385 raise TraitError('invalid value for type: %r' % value)
390 raise TraitError('invalid value for type: %r' % value)
386 elif hasattr(self, 'value_for'):
391 elif hasattr(self, 'value_for'):
387 return self.value_for(value)
392 return self.value_for(value)
388 else:
393 else:
389 return value
394 return value
390
395
391 def info(self):
396 def info(self):
392 return self.info_text
397 return self.info_text
393
398
394 def error(self, obj, value):
399 def error(self, obj, value):
395 if obj is not None:
400 if obj is not None:
396 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
401 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
397 % (self.name, class_of(obj),
402 % (self.name, class_of(obj),
398 self.info(), repr_type(value))
403 self.info(), repr_type(value))
399 else:
404 else:
400 e = "The '%s' trait must be %s, but a value of %r was specified." \
405 e = "The '%s' trait must be %s, but a value of %r was specified." \
401 % (self.name, self.info(), repr_type(value))
406 % (self.name, self.info(), repr_type(value))
402 raise TraitError(e)
407 raise TraitError(e)
403
408
404 def get_metadata(self, key):
409 def get_metadata(self, key):
405 return getattr(self, '_metadata', {}).get(key, None)
410 return getattr(self, '_metadata', {}).get(key, None)
406
411
407 def set_metadata(self, key, value):
412 def set_metadata(self, key, value):
408 getattr(self, '_metadata', {})[key] = value
413 getattr(self, '_metadata', {})[key] = value
409
414
410
415
411 #-----------------------------------------------------------------------------
416 #-----------------------------------------------------------------------------
412 # The HasTraits implementation
417 # The HasTraits implementation
413 #-----------------------------------------------------------------------------
418 #-----------------------------------------------------------------------------
414
419
415
420
416 class MetaHasTraits(type):
421 class MetaHasTraits(type):
417 """A metaclass for HasTraits.
422 """A metaclass for HasTraits.
418
423
419 This metaclass makes sure that any TraitType class attributes are
424 This metaclass makes sure that any TraitType class attributes are
420 instantiated and sets their name attribute.
425 instantiated and sets their name attribute.
421 """
426 """
422
427
423 def __new__(mcls, name, bases, classdict):
428 def __new__(mcls, name, bases, classdict):
424 """Create the HasTraits class.
429 """Create the HasTraits class.
425
430
426 This instantiates all TraitTypes in the class dict and sets their
431 This instantiates all TraitTypes in the class dict and sets their
427 :attr:`name` attribute.
432 :attr:`name` attribute.
428 """
433 """
429 # print "MetaHasTraitlets (mcls, name): ", mcls, name
434 # print "MetaHasTraitlets (mcls, name): ", mcls, name
430 # print "MetaHasTraitlets (bases): ", bases
435 # print "MetaHasTraitlets (bases): ", bases
431 # print "MetaHasTraitlets (classdict): ", classdict
436 # print "MetaHasTraitlets (classdict): ", classdict
432 for k,v in iteritems(classdict):
437 for k,v in iteritems(classdict):
433 if isinstance(v, TraitType):
438 if isinstance(v, TraitType):
434 v.name = k
439 v.name = k
435 elif inspect.isclass(v):
440 elif inspect.isclass(v):
436 if issubclass(v, TraitType):
441 if issubclass(v, TraitType):
437 vinst = v()
442 vinst = v()
438 vinst.name = k
443 vinst.name = k
439 classdict[k] = vinst
444 classdict[k] = vinst
440 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
445 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
441
446
442 def __init__(cls, name, bases, classdict):
447 def __init__(cls, name, bases, classdict):
443 """Finish initializing the HasTraits class.
448 """Finish initializing the HasTraits class.
444
449
445 This sets the :attr:`this_class` attribute of each TraitType in the
450 This sets the :attr:`this_class` attribute of each TraitType in the
446 class dict to the newly created class ``cls``.
451 class dict to the newly created class ``cls``.
447 """
452 """
448 for k, v in iteritems(classdict):
453 for k, v in iteritems(classdict):
449 if isinstance(v, TraitType):
454 if isinstance(v, TraitType):
450 v.this_class = cls
455 v.this_class = cls
451 super(MetaHasTraits, cls).__init__(name, bases, classdict)
456 super(MetaHasTraits, cls).__init__(name, bases, classdict)
452
457
453 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
458 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
454
459
455 def __new__(cls, *args, **kw):
460 def __new__(cls, *args, **kw):
456 # This is needed because in Python 2.6 object.__new__ only accepts
461 # This is needed because in Python 2.6 object.__new__ only accepts
457 # the cls argument.
462 # the cls argument.
458 new_meth = super(HasTraits, cls).__new__
463 new_meth = super(HasTraits, cls).__new__
459 if new_meth is object.__new__:
464 if new_meth is object.__new__:
460 inst = new_meth(cls)
465 inst = new_meth(cls)
461 else:
466 else:
462 inst = new_meth(cls, **kw)
467 inst = new_meth(cls, **kw)
463 inst._trait_values = {}
468 inst._trait_values = {}
464 inst._trait_notifiers = {}
469 inst._trait_notifiers = {}
465 inst._trait_dyn_inits = {}
470 inst._trait_dyn_inits = {}
466 # Here we tell all the TraitType instances to set their default
471 # Here we tell all the TraitType instances to set their default
467 # values on the instance.
472 # values on the instance.
468 for key in dir(cls):
473 for key in dir(cls):
469 # Some descriptors raise AttributeError like zope.interface's
474 # Some descriptors raise AttributeError like zope.interface's
470 # __provides__ attributes even though they exist. This causes
475 # __provides__ attributes even though they exist. This causes
471 # AttributeErrors even though they are listed in dir(cls).
476 # AttributeErrors even though they are listed in dir(cls).
472 try:
477 try:
473 value = getattr(cls, key)
478 value = getattr(cls, key)
474 except AttributeError:
479 except AttributeError:
475 pass
480 pass
476 else:
481 else:
477 if isinstance(value, TraitType):
482 if isinstance(value, TraitType):
478 value.instance_init(inst)
483 value.instance_init(inst)
479
484
480 return inst
485 return inst
481
486
482 def __init__(self, *args, **kw):
487 def __init__(self, *args, **kw):
483 # Allow trait values to be set using keyword arguments.
488 # Allow trait values to be set using keyword arguments.
484 # We need to use setattr for this to trigger validation and
489 # We need to use setattr for this to trigger validation and
485 # notifications.
490 # notifications.
486 for key, value in iteritems(kw):
491 for key, value in iteritems(kw):
487 setattr(self, key, value)
492 setattr(self, key, value)
488
493
489 def _notify_trait(self, name, old_value, new_value):
494 def _notify_trait(self, name, old_value, new_value):
490
495
491 # First dynamic ones
496 # First dynamic ones
492 callables = []
497 callables = []
493 callables.extend(self._trait_notifiers.get(name,[]))
498 callables.extend(self._trait_notifiers.get(name,[]))
494 callables.extend(self._trait_notifiers.get('anytrait',[]))
499 callables.extend(self._trait_notifiers.get('anytrait',[]))
495
500
496 # Now static ones
501 # Now static ones
497 try:
502 try:
498 cb = getattr(self, '_%s_changed' % name)
503 cb = getattr(self, '_%s_changed' % name)
499 except:
504 except:
500 pass
505 pass
501 else:
506 else:
502 callables.append(cb)
507 callables.append(cb)
503
508
504 # Call them all now
509 # Call them all now
505 for c in callables:
510 for c in callables:
506 # Traits catches and logs errors here. I allow them to raise
511 # Traits catches and logs errors here. I allow them to raise
507 if callable(c):
512 if callable(c):
508 argspec = inspect.getargspec(c)
513 argspec = inspect.getargspec(c)
509 nargs = len(argspec[0])
514 nargs = len(argspec[0])
510 # Bound methods have an additional 'self' argument
515 # Bound methods have an additional 'self' argument
511 # I don't know how to treat unbound methods, but they
516 # I don't know how to treat unbound methods, but they
512 # can't really be used for callbacks.
517 # can't really be used for callbacks.
513 if isinstance(c, types.MethodType):
518 if isinstance(c, types.MethodType):
514 offset = -1
519 offset = -1
515 else:
520 else:
516 offset = 0
521 offset = 0
517 if nargs + offset == 0:
522 if nargs + offset == 0:
518 c()
523 c()
519 elif nargs + offset == 1:
524 elif nargs + offset == 1:
520 c(name)
525 c(name)
521 elif nargs + offset == 2:
526 elif nargs + offset == 2:
522 c(name, new_value)
527 c(name, new_value)
523 elif nargs + offset == 3:
528 elif nargs + offset == 3:
524 c(name, old_value, new_value)
529 c(name, old_value, new_value)
525 else:
530 else:
526 raise TraitError('a trait changed callback '
531 raise TraitError('a trait changed callback '
527 'must have 0-3 arguments.')
532 'must have 0-3 arguments.')
528 else:
533 else:
529 raise TraitError('a trait changed callback '
534 raise TraitError('a trait changed callback '
530 'must be callable.')
535 'must be callable.')
531
536
532
537
533 def _add_notifiers(self, handler, name):
538 def _add_notifiers(self, handler, name):
534 if name not in self._trait_notifiers:
539 if name not in self._trait_notifiers:
535 nlist = []
540 nlist = []
536 self._trait_notifiers[name] = nlist
541 self._trait_notifiers[name] = nlist
537 else:
542 else:
538 nlist = self._trait_notifiers[name]
543 nlist = self._trait_notifiers[name]
539 if handler not in nlist:
544 if handler not in nlist:
540 nlist.append(handler)
545 nlist.append(handler)
541
546
542 def _remove_notifiers(self, handler, name):
547 def _remove_notifiers(self, handler, name):
543 if name in self._trait_notifiers:
548 if name in self._trait_notifiers:
544 nlist = self._trait_notifiers[name]
549 nlist = self._trait_notifiers[name]
545 try:
550 try:
546 index = nlist.index(handler)
551 index = nlist.index(handler)
547 except ValueError:
552 except ValueError:
548 pass
553 pass
549 else:
554 else:
550 del nlist[index]
555 del nlist[index]
551
556
552 def on_trait_change(self, handler, name=None, remove=False):
557 def on_trait_change(self, handler, name=None, remove=False):
553 """Setup a handler to be called when a trait changes.
558 """Setup a handler to be called when a trait changes.
554
559
555 This is used to setup dynamic notifications of trait changes.
560 This is used to setup dynamic notifications of trait changes.
556
561
557 Static handlers can be created by creating methods on a HasTraits
562 Static handlers can be created by creating methods on a HasTraits
558 subclass with the naming convention '_[traitname]_changed'. Thus,
563 subclass with the naming convention '_[traitname]_changed'. Thus,
559 to create static handler for the trait 'a', create the method
564 to create static handler for the trait 'a', create the method
560 _a_changed(self, name, old, new) (fewer arguments can be used, see
565 _a_changed(self, name, old, new) (fewer arguments can be used, see
561 below).
566 below).
562
567
563 Parameters
568 Parameters
564 ----------
569 ----------
565 handler : callable
570 handler : callable
566 A callable that is called when a trait changes. Its
571 A callable that is called when a trait changes. Its
567 signature can be handler(), handler(name), handler(name, new)
572 signature can be handler(), handler(name), handler(name, new)
568 or handler(name, old, new).
573 or handler(name, old, new).
569 name : list, str, None
574 name : list, str, None
570 If None, the handler will apply to all traits. If a list
575 If None, the handler will apply to all traits. If a list
571 of str, handler will apply to all names in the list. If a
576 of str, handler will apply to all names in the list. If a
572 str, the handler will apply just to that name.
577 str, the handler will apply just to that name.
573 remove : bool
578 remove : bool
574 If False (the default), then install the handler. If True
579 If False (the default), then install the handler. If True
575 then unintall it.
580 then unintall it.
576 """
581 """
577 if remove:
582 if remove:
578 names = parse_notifier_name(name)
583 names = parse_notifier_name(name)
579 for n in names:
584 for n in names:
580 self._remove_notifiers(handler, n)
585 self._remove_notifiers(handler, n)
581 else:
586 else:
582 names = parse_notifier_name(name)
587 names = parse_notifier_name(name)
583 for n in names:
588 for n in names:
584 self._add_notifiers(handler, n)
589 self._add_notifiers(handler, n)
585
590
586 @classmethod
591 @classmethod
587 def class_trait_names(cls, **metadata):
592 def class_trait_names(cls, **metadata):
588 """Get a list of all the names of this classes traits.
593 """Get a list of all the names of this classes traits.
589
594
590 This method is just like the :meth:`trait_names` method, but is unbound.
595 This method is just like the :meth:`trait_names` method, but is unbound.
591 """
596 """
592 return cls.class_traits(**metadata).keys()
597 return cls.class_traits(**metadata).keys()
593
598
594 @classmethod
599 @classmethod
595 def class_traits(cls, **metadata):
600 def class_traits(cls, **metadata):
596 """Get a list of all the traits of this class.
601 """Get a list of all the traits of this class.
597
602
598 This method is just like the :meth:`traits` method, but is unbound.
603 This method is just like the :meth:`traits` method, but is unbound.
599
604
600 The TraitTypes returned don't know anything about the values
605 The TraitTypes returned don't know anything about the values
601 that the various HasTrait's instances are holding.
606 that the various HasTrait's instances are holding.
602
607
603 This follows the same algorithm as traits does and does not allow
608 This follows the same algorithm as traits does and does not allow
604 for any simple way of specifying merely that a metadata name
609 for any simple way of specifying merely that a metadata name
605 exists, but has any value. This is because get_metadata returns
610 exists, but has any value. This is because get_metadata returns
606 None if a metadata key doesn't exist.
611 None if a metadata key doesn't exist.
607 """
612 """
608 traits = dict([memb for memb in getmembers(cls) if \
613 traits = dict([memb for memb in getmembers(cls) if \
609 isinstance(memb[1], TraitType)])
614 isinstance(memb[1], TraitType)])
610
615
611 if len(metadata) == 0:
616 if len(metadata) == 0:
612 return traits
617 return traits
613
618
614 for meta_name, meta_eval in metadata.items():
619 for meta_name, meta_eval in metadata.items():
615 if type(meta_eval) is not FunctionType:
620 if type(meta_eval) is not FunctionType:
616 metadata[meta_name] = _SimpleTest(meta_eval)
621 metadata[meta_name] = _SimpleTest(meta_eval)
617
622
618 result = {}
623 result = {}
619 for name, trait in traits.items():
624 for name, trait in traits.items():
620 for meta_name, meta_eval in metadata.items():
625 for meta_name, meta_eval in metadata.items():
621 if not meta_eval(trait.get_metadata(meta_name)):
626 if not meta_eval(trait.get_metadata(meta_name)):
622 break
627 break
623 else:
628 else:
624 result[name] = trait
629 result[name] = trait
625
630
626 return result
631 return result
627
632
628 def trait_names(self, **metadata):
633 def trait_names(self, **metadata):
629 """Get a list of all the names of this classes traits."""
634 """Get a list of all the names of this classes traits."""
630 return self.traits(**metadata).keys()
635 return self.traits(**metadata).keys()
631
636
632 def traits(self, **metadata):
637 def traits(self, **metadata):
633 """Get a list of all the traits of this class.
638 """Get a list of all the traits of this class.
634
639
635 The TraitTypes returned don't know anything about the values
640 The TraitTypes returned don't know anything about the values
636 that the various HasTrait's instances are holding.
641 that the various HasTrait's instances are holding.
637
642
638 This follows the same algorithm as traits does and does not allow
643 This follows the same algorithm as traits does and does not allow
639 for any simple way of specifying merely that a metadata name
644 for any simple way of specifying merely that a metadata name
640 exists, but has any value. This is because get_metadata returns
645 exists, but has any value. This is because get_metadata returns
641 None if a metadata key doesn't exist.
646 None if a metadata key doesn't exist.
642 """
647 """
643 traits = dict([memb for memb in getmembers(self.__class__) if \
648 traits = dict([memb for memb in getmembers(self.__class__) if \
644 isinstance(memb[1], TraitType)])
649 isinstance(memb[1], TraitType)])
645
650
646 if len(metadata) == 0:
651 if len(metadata) == 0:
647 return traits
652 return traits
648
653
649 for meta_name, meta_eval in metadata.items():
654 for meta_name, meta_eval in metadata.items():
650 if type(meta_eval) is not FunctionType:
655 if type(meta_eval) is not FunctionType:
651 metadata[meta_name] = _SimpleTest(meta_eval)
656 metadata[meta_name] = _SimpleTest(meta_eval)
652
657
653 result = {}
658 result = {}
654 for name, trait in traits.items():
659 for name, trait in traits.items():
655 for meta_name, meta_eval in metadata.items():
660 for meta_name, meta_eval in metadata.items():
656 if not meta_eval(trait.get_metadata(meta_name)):
661 if not meta_eval(trait.get_metadata(meta_name)):
657 break
662 break
658 else:
663 else:
659 result[name] = trait
664 result[name] = trait
660
665
661 return result
666 return result
662
667
663 def trait_metadata(self, traitname, key):
668 def trait_metadata(self, traitname, key):
664 """Get metadata values for trait by key."""
669 """Get metadata values for trait by key."""
665 try:
670 try:
666 trait = getattr(self.__class__, traitname)
671 trait = getattr(self.__class__, traitname)
667 except AttributeError:
672 except AttributeError:
668 raise TraitError("Class %s does not have a trait named %s" %
673 raise TraitError("Class %s does not have a trait named %s" %
669 (self.__class__.__name__, traitname))
674 (self.__class__.__name__, traitname))
670 else:
675 else:
671 return trait.get_metadata(key)
676 return trait.get_metadata(key)
672
677
673 #-----------------------------------------------------------------------------
678 #-----------------------------------------------------------------------------
674 # Actual TraitTypes implementations/subclasses
679 # Actual TraitTypes implementations/subclasses
675 #-----------------------------------------------------------------------------
680 #-----------------------------------------------------------------------------
676
681
677 #-----------------------------------------------------------------------------
682 #-----------------------------------------------------------------------------
678 # TraitTypes subclasses for handling classes and instances of classes
683 # TraitTypes subclasses for handling classes and instances of classes
679 #-----------------------------------------------------------------------------
684 #-----------------------------------------------------------------------------
680
685
681
686
682 class ClassBasedTraitType(TraitType):
687 class ClassBasedTraitType(TraitType):
683 """A trait with error reporting for Type, Instance and This."""
688 """A trait with error reporting for Type, Instance and This."""
684
689
685 def error(self, obj, value):
690 def error(self, obj, value):
686 kind = type(value)
691 kind = type(value)
687 if (not py3compat.PY3) and kind is InstanceType:
692 if (not py3compat.PY3) and kind is InstanceType:
688 msg = 'class %s' % value.__class__.__name__
693 msg = 'class %s' % value.__class__.__name__
689 else:
694 else:
690 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
695 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
691
696
692 if obj is not None:
697 if obj is not None:
693 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
698 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
694 % (self.name, class_of(obj),
699 % (self.name, class_of(obj),
695 self.info(), msg)
700 self.info(), msg)
696 else:
701 else:
697 e = "The '%s' trait must be %s, but a value of %r was specified." \
702 e = "The '%s' trait must be %s, but a value of %r was specified." \
698 % (self.name, self.info(), msg)
703 % (self.name, self.info(), msg)
699
704
700 raise TraitError(e)
705 raise TraitError(e)
701
706
702
707
703 class Type(ClassBasedTraitType):
708 class Type(ClassBasedTraitType):
704 """A trait whose value must be a subclass of a specified class."""
709 """A trait whose value must be a subclass of a specified class."""
705
710
706 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
711 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
707 """Construct a Type trait
712 """Construct a Type trait
708
713
709 A Type trait specifies that its values must be subclasses of
714 A Type trait specifies that its values must be subclasses of
710 a particular class.
715 a particular class.
711
716
712 If only ``default_value`` is given, it is used for the ``klass`` as
717 If only ``default_value`` is given, it is used for the ``klass`` as
713 well.
718 well.
714
719
715 Parameters
720 Parameters
716 ----------
721 ----------
717 default_value : class, str or None
722 default_value : class, str or None
718 The default value must be a subclass of klass. If an str,
723 The default value must be a subclass of klass. If an str,
719 the str must be a fully specified class name, like 'foo.bar.Bah'.
724 the str must be a fully specified class name, like 'foo.bar.Bah'.
720 The string is resolved into real class, when the parent
725 The string is resolved into real class, when the parent
721 :class:`HasTraits` class is instantiated.
726 :class:`HasTraits` class is instantiated.
722 klass : class, str, None
727 klass : class, str, None
723 Values of this trait must be a subclass of klass. The klass
728 Values of this trait must be a subclass of klass. The klass
724 may be specified in a string like: 'foo.bar.MyClass'.
729 may be specified in a string like: 'foo.bar.MyClass'.
725 The string is resolved into real class, when the parent
730 The string is resolved into real class, when the parent
726 :class:`HasTraits` class is instantiated.
731 :class:`HasTraits` class is instantiated.
727 allow_none : boolean
732 allow_none : boolean
728 Indicates whether None is allowed as an assignable value. Even if
733 Indicates whether None is allowed as an assignable value. Even if
729 ``False``, the default value may be ``None``.
734 ``False``, the default value may be ``None``.
730 """
735 """
731 if default_value is None:
736 if default_value is None:
732 if klass is None:
737 if klass is None:
733 klass = object
738 klass = object
734 elif klass is None:
739 elif klass is None:
735 klass = default_value
740 klass = default_value
736
741
737 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
742 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
738 raise TraitError("A Type trait must specify a class.")
743 raise TraitError("A Type trait must specify a class.")
739
744
740 self.klass = klass
745 self.klass = klass
741 self._allow_none = allow_none
746 self._allow_none = allow_none
742
747
743 super(Type, self).__init__(default_value, **metadata)
748 super(Type, self).__init__(default_value, **metadata)
744
749
745 def validate(self, obj, value):
750 def validate(self, obj, value):
746 """Validates that the value is a valid object instance."""
751 """Validates that the value is a valid object instance."""
747 try:
752 try:
748 if issubclass(value, self.klass):
753 if issubclass(value, self.klass):
749 return value
754 return value
750 except:
755 except:
751 if (value is None) and (self._allow_none):
756 if (value is None) and (self._allow_none):
752 return value
757 return value
753
758
754 self.error(obj, value)
759 self.error(obj, value)
755
760
756 def info(self):
761 def info(self):
757 """ Returns a description of the trait."""
762 """ Returns a description of the trait."""
758 if isinstance(self.klass, py3compat.string_types):
763 if isinstance(self.klass, py3compat.string_types):
759 klass = self.klass
764 klass = self.klass
760 else:
765 else:
761 klass = self.klass.__name__
766 klass = self.klass.__name__
762 result = 'a subclass of ' + klass
767 result = 'a subclass of ' + klass
763 if self._allow_none:
768 if self._allow_none:
764 return result + ' or None'
769 return result + ' or None'
765 return result
770 return result
766
771
767 def instance_init(self, obj):
772 def instance_init(self, obj):
768 self._resolve_classes()
773 self._resolve_classes()
769 super(Type, self).instance_init(obj)
774 super(Type, self).instance_init(obj)
770
775
771 def _resolve_classes(self):
776 def _resolve_classes(self):
772 if isinstance(self.klass, py3compat.string_types):
777 if isinstance(self.klass, py3compat.string_types):
773 self.klass = import_item(self.klass)
778 self.klass = import_item(self.klass)
774 if isinstance(self.default_value, py3compat.string_types):
779 if isinstance(self.default_value, py3compat.string_types):
775 self.default_value = import_item(self.default_value)
780 self.default_value = import_item(self.default_value)
776
781
777 def get_default_value(self):
782 def get_default_value(self):
778 return self.default_value
783 return self.default_value
779
784
780
785
781 class DefaultValueGenerator(object):
786 class DefaultValueGenerator(object):
782 """A class for generating new default value instances."""
787 """A class for generating new default value instances."""
783
788
784 def __init__(self, *args, **kw):
789 def __init__(self, *args, **kw):
785 self.args = args
790 self.args = args
786 self.kw = kw
791 self.kw = kw
787
792
788 def generate(self, klass):
793 def generate(self, klass):
789 return klass(*self.args, **self.kw)
794 return klass(*self.args, **self.kw)
790
795
791
796
792 class Instance(ClassBasedTraitType):
797 class Instance(ClassBasedTraitType):
793 """A trait whose value must be an instance of a specified class.
798 """A trait whose value must be an instance of a specified class.
794
799
795 The value can also be an instance of a subclass of the specified class.
800 The value can also be an instance of a subclass of the specified class.
796 """
801 """
797
802
798 def __init__(self, klass=None, args=None, kw=None,
803 def __init__(self, klass=None, args=None, kw=None,
799 allow_none=True, **metadata ):
804 allow_none=True, **metadata ):
800 """Construct an Instance trait.
805 """Construct an Instance trait.
801
806
802 This trait allows values that are instances of a particular
807 This trait allows values that are instances of a particular
803 class or its sublclasses. Our implementation is quite different
808 class or its sublclasses. Our implementation is quite different
804 from that of enthough.traits as we don't allow instances to be used
809 from that of enthough.traits as we don't allow instances to be used
805 for klass and we handle the ``args`` and ``kw`` arguments differently.
810 for klass and we handle the ``args`` and ``kw`` arguments differently.
806
811
807 Parameters
812 Parameters
808 ----------
813 ----------
809 klass : class, str
814 klass : class, str
810 The class that forms the basis for the trait. Class names
815 The class that forms the basis for the trait. Class names
811 can also be specified as strings, like 'foo.bar.Bar'.
816 can also be specified as strings, like 'foo.bar.Bar'.
812 args : tuple
817 args : tuple
813 Positional arguments for generating the default value.
818 Positional arguments for generating the default value.
814 kw : dict
819 kw : dict
815 Keyword arguments for generating the default value.
820 Keyword arguments for generating the default value.
816 allow_none : bool
821 allow_none : bool
817 Indicates whether None is allowed as a value.
822 Indicates whether None is allowed as a value.
818
823
819 Notes
824 Notes
820 -----
825 -----
821 If both ``args`` and ``kw`` are None, then the default value is None.
826 If both ``args`` and ``kw`` are None, then the default value is None.
822 If ``args`` is a tuple and ``kw`` is a dict, then the default is
827 If ``args`` is a tuple and ``kw`` is a dict, then the default is
823 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
828 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
824 not (but not both), None is replace by ``()`` or ``{}``.
829 not (but not both), None is replace by ``()`` or ``{}``.
825 """
830 """
826
831
827 self._allow_none = allow_none
832 self._allow_none = allow_none
828
833
829 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types))):
834 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types))):
830 raise TraitError('The klass argument must be a class'
835 raise TraitError('The klass argument must be a class'
831 ' you gave: %r' % klass)
836 ' you gave: %r' % klass)
832 self.klass = klass
837 self.klass = klass
833
838
834 # self.klass is a class, so handle default_value
839 # self.klass is a class, so handle default_value
835 if args is None and kw is None:
840 if args is None and kw is None:
836 default_value = None
841 default_value = None
837 else:
842 else:
838 if args is None:
843 if args is None:
839 # kw is not None
844 # kw is not None
840 args = ()
845 args = ()
841 elif kw is None:
846 elif kw is None:
842 # args is not None
847 # args is not None
843 kw = {}
848 kw = {}
844
849
845 if not isinstance(kw, dict):
850 if not isinstance(kw, dict):
846 raise TraitError("The 'kw' argument must be a dict or None.")
851 raise TraitError("The 'kw' argument must be a dict or None.")
847 if not isinstance(args, tuple):
852 if not isinstance(args, tuple):
848 raise TraitError("The 'args' argument must be a tuple or None.")
853 raise TraitError("The 'args' argument must be a tuple or None.")
849
854
850 default_value = DefaultValueGenerator(*args, **kw)
855 default_value = DefaultValueGenerator(*args, **kw)
851
856
852 super(Instance, self).__init__(default_value, **metadata)
857 super(Instance, self).__init__(default_value, **metadata)
853
858
854 def validate(self, obj, value):
859 def validate(self, obj, value):
855 if value is None:
860 if value is None:
856 if self._allow_none:
861 if self._allow_none:
857 return value
862 return value
858 self.error(obj, value)
863 self.error(obj, value)
859
864
860 if isinstance(value, self.klass):
865 if isinstance(value, self.klass):
861 return value
866 return value
862 else:
867 else:
863 self.error(obj, value)
868 self.error(obj, value)
864
869
865 def info(self):
870 def info(self):
866 if isinstance(self.klass, py3compat.string_types):
871 if isinstance(self.klass, py3compat.string_types):
867 klass = self.klass
872 klass = self.klass
868 else:
873 else:
869 klass = self.klass.__name__
874 klass = self.klass.__name__
870 result = class_of(klass)
875 result = class_of(klass)
871 if self._allow_none:
876 if self._allow_none:
872 return result + ' or None'
877 return result + ' or None'
873
878
874 return result
879 return result
875
880
876 def instance_init(self, obj):
881 def instance_init(self, obj):
877 self._resolve_classes()
882 self._resolve_classes()
878 super(Instance, self).instance_init(obj)
883 super(Instance, self).instance_init(obj)
879
884
880 def _resolve_classes(self):
885 def _resolve_classes(self):
881 if isinstance(self.klass, py3compat.string_types):
886 if isinstance(self.klass, py3compat.string_types):
882 self.klass = import_item(self.klass)
887 self.klass = import_item(self.klass)
883
888
884 def get_default_value(self):
889 def get_default_value(self):
885 """Instantiate a default value instance.
890 """Instantiate a default value instance.
886
891
887 This is called when the containing HasTraits classes'
892 This is called when the containing HasTraits classes'
888 :meth:`__new__` method is called to ensure that a unique instance
893 :meth:`__new__` method is called to ensure that a unique instance
889 is created for each HasTraits instance.
894 is created for each HasTraits instance.
890 """
895 """
891 dv = self.default_value
896 dv = self.default_value
892 if isinstance(dv, DefaultValueGenerator):
897 if isinstance(dv, DefaultValueGenerator):
893 return dv.generate(self.klass)
898 return dv.generate(self.klass)
894 else:
899 else:
895 return dv
900 return dv
896
901
897
902
898 class This(ClassBasedTraitType):
903 class This(ClassBasedTraitType):
899 """A trait for instances of the class containing this trait.
904 """A trait for instances of the class containing this trait.
900
905
901 Because how how and when class bodies are executed, the ``This``
906 Because how how and when class bodies are executed, the ``This``
902 trait can only have a default value of None. This, and because we
907 trait can only have a default value of None. This, and because we
903 always validate default values, ``allow_none`` is *always* true.
908 always validate default values, ``allow_none`` is *always* true.
904 """
909 """
905
910
906 info_text = 'an instance of the same type as the receiver or None'
911 info_text = 'an instance of the same type as the receiver or None'
907
912
908 def __init__(self, **metadata):
913 def __init__(self, **metadata):
909 super(This, self).__init__(None, **metadata)
914 super(This, self).__init__(None, **metadata)
910
915
911 def validate(self, obj, value):
916 def validate(self, obj, value):
912 # What if value is a superclass of obj.__class__? This is
917 # What if value is a superclass of obj.__class__? This is
913 # complicated if it was the superclass that defined the This
918 # complicated if it was the superclass that defined the This
914 # trait.
919 # trait.
915 if isinstance(value, self.this_class) or (value is None):
920 if isinstance(value, self.this_class) or (value is None):
916 return value
921 return value
917 else:
922 else:
918 self.error(obj, value)
923 self.error(obj, value)
919
924
920
925
921 #-----------------------------------------------------------------------------
926 #-----------------------------------------------------------------------------
922 # Basic TraitTypes implementations/subclasses
927 # Basic TraitTypes implementations/subclasses
923 #-----------------------------------------------------------------------------
928 #-----------------------------------------------------------------------------
924
929
925
930
926 class Any(TraitType):
931 class Any(TraitType):
927 default_value = None
932 default_value = None
928 info_text = 'any value'
933 info_text = 'any value'
929
934
930
935
931 class Int(TraitType):
936 class Int(TraitType):
932 """An int trait."""
937 """An int trait."""
933
938
934 default_value = 0
939 default_value = 0
935 info_text = 'an int'
940 info_text = 'an int'
936
941
937 def validate(self, obj, value):
942 def validate(self, obj, value):
938 if isinstance(value, int):
943 if isinstance(value, int):
939 return value
944 return value
940 self.error(obj, value)
945 self.error(obj, value)
941
946
942 class CInt(Int):
947 class CInt(Int):
943 """A casting version of the int trait."""
948 """A casting version of the int trait."""
944
949
945 def validate(self, obj, value):
950 def validate(self, obj, value):
946 try:
951 try:
947 return int(value)
952 return int(value)
948 except:
953 except:
949 self.error(obj, value)
954 self.error(obj, value)
950
955
951 if py3compat.PY3:
956 if py3compat.PY3:
952 Long, CLong = Int, CInt
957 Long, CLong = Int, CInt
953 Integer = Int
958 Integer = Int
954 else:
959 else:
955 class Long(TraitType):
960 class Long(TraitType):
956 """A long integer trait."""
961 """A long integer trait."""
957
962
958 default_value = 0
963 default_value = 0
959 info_text = 'a long'
964 info_text = 'a long'
960
965
961 def validate(self, obj, value):
966 def validate(self, obj, value):
962 if isinstance(value, long):
967 if isinstance(value, long):
963 return value
968 return value
964 if isinstance(value, int):
969 if isinstance(value, int):
965 return long(value)
970 return long(value)
966 self.error(obj, value)
971 self.error(obj, value)
967
972
968
973
969 class CLong(Long):
974 class CLong(Long):
970 """A casting version of the long integer trait."""
975 """A casting version of the long integer trait."""
971
976
972 def validate(self, obj, value):
977 def validate(self, obj, value):
973 try:
978 try:
974 return long(value)
979 return long(value)
975 except:
980 except:
976 self.error(obj, value)
981 self.error(obj, value)
977
982
978 class Integer(TraitType):
983 class Integer(TraitType):
979 """An integer trait.
984 """An integer trait.
980
985
981 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
986 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
982
987
983 default_value = 0
988 default_value = 0
984 info_text = 'an integer'
989 info_text = 'an integer'
985
990
986 def validate(self, obj, value):
991 def validate(self, obj, value):
987 if isinstance(value, int):
992 if isinstance(value, int):
988 return value
993 return value
989 if isinstance(value, long):
994 if isinstance(value, long):
990 # downcast longs that fit in int:
995 # downcast longs that fit in int:
991 # note that int(n > sys.maxint) returns a long, so
996 # note that int(n > sys.maxint) returns a long, so
992 # we don't need a condition on this cast
997 # we don't need a condition on this cast
993 return int(value)
998 return int(value)
994 if sys.platform == "cli":
999 if sys.platform == "cli":
995 from System import Int64
1000 from System import Int64
996 if isinstance(value, Int64):
1001 if isinstance(value, Int64):
997 return int(value)
1002 return int(value)
998 self.error(obj, value)
1003 self.error(obj, value)
999
1004
1000
1005
1001 class Float(TraitType):
1006 class Float(TraitType):
1002 """A float trait."""
1007 """A float trait."""
1003
1008
1004 default_value = 0.0
1009 default_value = 0.0
1005 info_text = 'a float'
1010 info_text = 'a float'
1006
1011
1007 def validate(self, obj, value):
1012 def validate(self, obj, value):
1008 if isinstance(value, float):
1013 if isinstance(value, float):
1009 return value
1014 return value
1010 if isinstance(value, int):
1015 if isinstance(value, int):
1011 return float(value)
1016 return float(value)
1012 self.error(obj, value)
1017 self.error(obj, value)
1013
1018
1014
1019
1015 class CFloat(Float):
1020 class CFloat(Float):
1016 """A casting version of the float trait."""
1021 """A casting version of the float trait."""
1017
1022
1018 def validate(self, obj, value):
1023 def validate(self, obj, value):
1019 try:
1024 try:
1020 return float(value)
1025 return float(value)
1021 except:
1026 except:
1022 self.error(obj, value)
1027 self.error(obj, value)
1023
1028
1024 class Complex(TraitType):
1029 class Complex(TraitType):
1025 """A trait for complex numbers."""
1030 """A trait for complex numbers."""
1026
1031
1027 default_value = 0.0 + 0.0j
1032 default_value = 0.0 + 0.0j
1028 info_text = 'a complex number'
1033 info_text = 'a complex number'
1029
1034
1030 def validate(self, obj, value):
1035 def validate(self, obj, value):
1031 if isinstance(value, complex):
1036 if isinstance(value, complex):
1032 return value
1037 return value
1033 if isinstance(value, (float, int)):
1038 if isinstance(value, (float, int)):
1034 return complex(value)
1039 return complex(value)
1035 self.error(obj, value)
1040 self.error(obj, value)
1036
1041
1037
1042
1038 class CComplex(Complex):
1043 class CComplex(Complex):
1039 """A casting version of the complex number trait."""
1044 """A casting version of the complex number trait."""
1040
1045
1041 def validate (self, obj, value):
1046 def validate (self, obj, value):
1042 try:
1047 try:
1043 return complex(value)
1048 return complex(value)
1044 except:
1049 except:
1045 self.error(obj, value)
1050 self.error(obj, value)
1046
1051
1047 # We should always be explicit about whether we're using bytes or unicode, both
1052 # We should always be explicit about whether we're using bytes or unicode, both
1048 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1053 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1049 # we don't have a Str type.
1054 # we don't have a Str type.
1050 class Bytes(TraitType):
1055 class Bytes(TraitType):
1051 """A trait for byte strings."""
1056 """A trait for byte strings."""
1052
1057
1053 default_value = b''
1058 default_value = b''
1054 info_text = 'a bytes object'
1059 info_text = 'a bytes object'
1055
1060
1056 def validate(self, obj, value):
1061 def validate(self, obj, value):
1057 if isinstance(value, bytes):
1062 if isinstance(value, bytes):
1058 return value
1063 return value
1059 self.error(obj, value)
1064 self.error(obj, value)
1060
1065
1061
1066
1062 class CBytes(Bytes):
1067 class CBytes(Bytes):
1063 """A casting version of the byte string trait."""
1068 """A casting version of the byte string trait."""
1064
1069
1065 def validate(self, obj, value):
1070 def validate(self, obj, value):
1066 try:
1071 try:
1067 return bytes(value)
1072 return bytes(value)
1068 except:
1073 except:
1069 self.error(obj, value)
1074 self.error(obj, value)
1070
1075
1071
1076
1072 class Unicode(TraitType):
1077 class Unicode(TraitType):
1073 """A trait for unicode strings."""
1078 """A trait for unicode strings."""
1074
1079
1075 default_value = u''
1080 default_value = u''
1076 info_text = 'a unicode string'
1081 info_text = 'a unicode string'
1077
1082
1078 def validate(self, obj, value):
1083 def validate(self, obj, value):
1079 if isinstance(value, py3compat.unicode_type):
1084 if isinstance(value, py3compat.unicode_type):
1080 return value
1085 return value
1081 if isinstance(value, bytes):
1086 if isinstance(value, bytes):
1082 try:
1087 try:
1083 return value.decode('ascii', 'strict')
1088 return value.decode('ascii', 'strict')
1084 except UnicodeDecodeError:
1089 except UnicodeDecodeError:
1085 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1090 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1086 raise TraitError(msg.format(value, self.name, class_of(obj)))
1091 raise TraitError(msg.format(value, self.name, class_of(obj)))
1087 self.error(obj, value)
1092 self.error(obj, value)
1088
1093
1089
1094
1090 class CUnicode(Unicode):
1095 class CUnicode(Unicode):
1091 """A casting version of the unicode trait."""
1096 """A casting version of the unicode trait."""
1092
1097
1093 def validate(self, obj, value):
1098 def validate(self, obj, value):
1094 try:
1099 try:
1095 return py3compat.unicode_type(value)
1100 return py3compat.unicode_type(value)
1096 except:
1101 except:
1097 self.error(obj, value)
1102 self.error(obj, value)
1098
1103
1099
1104
1100 class ObjectName(TraitType):
1105 class ObjectName(TraitType):
1101 """A string holding a valid object name in this version of Python.
1106 """A string holding a valid object name in this version of Python.
1102
1107
1103 This does not check that the name exists in any scope."""
1108 This does not check that the name exists in any scope."""
1104 info_text = "a valid object identifier in Python"
1109 info_text = "a valid object identifier in Python"
1105
1110
1106 if py3compat.PY3:
1111 if py3compat.PY3:
1107 # Python 3:
1112 # Python 3:
1108 coerce_str = staticmethod(lambda _,s: s)
1113 coerce_str = staticmethod(lambda _,s: s)
1109
1114
1110 else:
1115 else:
1111 # Python 2:
1116 # Python 2:
1112 def coerce_str(self, obj, value):
1117 def coerce_str(self, obj, value):
1113 "In Python 2, coerce ascii-only unicode to str"
1118 "In Python 2, coerce ascii-only unicode to str"
1114 if isinstance(value, unicode):
1119 if isinstance(value, unicode):
1115 try:
1120 try:
1116 return str(value)
1121 return str(value)
1117 except UnicodeEncodeError:
1122 except UnicodeEncodeError:
1118 self.error(obj, value)
1123 self.error(obj, value)
1119 return value
1124 return value
1120
1125
1121 def validate(self, obj, value):
1126 def validate(self, obj, value):
1122 value = self.coerce_str(obj, value)
1127 value = self.coerce_str(obj, value)
1123
1128
1124 if isinstance(value, str) and py3compat.isidentifier(value):
1129 if isinstance(value, str) and py3compat.isidentifier(value):
1125 return value
1130 return value
1126 self.error(obj, value)
1131 self.error(obj, value)
1127
1132
1128 class DottedObjectName(ObjectName):
1133 class DottedObjectName(ObjectName):
1129 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1134 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1130 def validate(self, obj, value):
1135 def validate(self, obj, value):
1131 value = self.coerce_str(obj, value)
1136 value = self.coerce_str(obj, value)
1132
1137
1133 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1138 if isinstance(value, str) and py3compat.isidentifier(value, dotted=True):
1134 return value
1139 return value
1135 self.error(obj, value)
1140 self.error(obj, value)
1136
1141
1137
1142
1138 class Bool(TraitType):
1143 class Bool(TraitType):
1139 """A boolean (True, False) trait."""
1144 """A boolean (True, False) trait."""
1140
1145
1141 default_value = False
1146 default_value = False
1142 info_text = 'a boolean'
1147 info_text = 'a boolean'
1143
1148
1144 def validate(self, obj, value):
1149 def validate(self, obj, value):
1145 if isinstance(value, bool):
1150 if isinstance(value, bool):
1146 return value
1151 return value
1147 self.error(obj, value)
1152 self.error(obj, value)
1148
1153
1149
1154
1150 class CBool(Bool):
1155 class CBool(Bool):
1151 """A casting version of the boolean trait."""
1156 """A casting version of the boolean trait."""
1152
1157
1153 def validate(self, obj, value):
1158 def validate(self, obj, value):
1154 try:
1159 try:
1155 return bool(value)
1160 return bool(value)
1156 except:
1161 except:
1157 self.error(obj, value)
1162 self.error(obj, value)
1158
1163
1159
1164
1160 class Enum(TraitType):
1165 class Enum(TraitType):
1161 """An enum that whose value must be in a given sequence."""
1166 """An enum that whose value must be in a given sequence."""
1162
1167
1163 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1168 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1164 self.values = values
1169 self.values = values
1165 self._allow_none = allow_none
1170 self._allow_none = allow_none
1166 super(Enum, self).__init__(default_value, **metadata)
1171 super(Enum, self).__init__(default_value, **metadata)
1167
1172
1168 def validate(self, obj, value):
1173 def validate(self, obj, value):
1169 if value is None:
1174 if value is None:
1170 if self._allow_none:
1175 if self._allow_none:
1171 return value
1176 return value
1172
1177
1173 if value in self.values:
1178 if value in self.values:
1174 return value
1179 return value
1175 self.error(obj, value)
1180 self.error(obj, value)
1176
1181
1177 def info(self):
1182 def info(self):
1178 """ Returns a description of the trait."""
1183 """ Returns a description of the trait."""
1179 result = 'any of ' + repr(self.values)
1184 result = 'any of ' + repr(self.values)
1180 if self._allow_none:
1185 if self._allow_none:
1181 return result + ' or None'
1186 return result + ' or None'
1182 return result
1187 return result
1183
1188
1184 class CaselessStrEnum(Enum):
1189 class CaselessStrEnum(Enum):
1185 """An enum of strings that are caseless in validate."""
1190 """An enum of strings that are caseless in validate."""
1186
1191
1187 def validate(self, obj, value):
1192 def validate(self, obj, value):
1188 if value is None:
1193 if value is None:
1189 if self._allow_none:
1194 if self._allow_none:
1190 return value
1195 return value
1191
1196
1192 if not isinstance(value, py3compat.string_types):
1197 if not isinstance(value, py3compat.string_types):
1193 self.error(obj, value)
1198 self.error(obj, value)
1194
1199
1195 for v in self.values:
1200 for v in self.values:
1196 if v.lower() == value.lower():
1201 if v.lower() == value.lower():
1197 return v
1202 return v
1198 self.error(obj, value)
1203 self.error(obj, value)
1199
1204
1200 class Container(Instance):
1205 class Container(Instance):
1201 """An instance of a container (list, set, etc.)
1206 """An instance of a container (list, set, etc.)
1202
1207
1203 To be subclassed by overriding klass.
1208 To be subclassed by overriding klass.
1204 """
1209 """
1205 klass = None
1210 klass = None
1206 _valid_defaults = SequenceTypes
1211 _valid_defaults = SequenceTypes
1207 _trait = None
1212 _trait = None
1208
1213
1209 def __init__(self, trait=None, default_value=None, allow_none=True,
1214 def __init__(self, trait=None, default_value=None, allow_none=True,
1210 **metadata):
1215 **metadata):
1211 """Create a container trait type from a list, set, or tuple.
1216 """Create a container trait type from a list, set, or tuple.
1212
1217
1213 The default value is created by doing ``List(default_value)``,
1218 The default value is created by doing ``List(default_value)``,
1214 which creates a copy of the ``default_value``.
1219 which creates a copy of the ``default_value``.
1215
1220
1216 ``trait`` can be specified, which restricts the type of elements
1221 ``trait`` can be specified, which restricts the type of elements
1217 in the container to that TraitType.
1222 in the container to that TraitType.
1218
1223
1219 If only one arg is given and it is not a Trait, it is taken as
1224 If only one arg is given and it is not a Trait, it is taken as
1220 ``default_value``:
1225 ``default_value``:
1221
1226
1222 ``c = List([1,2,3])``
1227 ``c = List([1,2,3])``
1223
1228
1224 Parameters
1229 Parameters
1225 ----------
1230 ----------
1226
1231
1227 trait : TraitType [ optional ]
1232 trait : TraitType [ optional ]
1228 the type for restricting the contents of the Container. If unspecified,
1233 the type for restricting the contents of the Container. If unspecified,
1229 types are not checked.
1234 types are not checked.
1230
1235
1231 default_value : SequenceType [ optional ]
1236 default_value : SequenceType [ optional ]
1232 The default value for the Trait. Must be list/tuple/set, and
1237 The default value for the Trait. Must be list/tuple/set, and
1233 will be cast to the container type.
1238 will be cast to the container type.
1234
1239
1235 allow_none : Bool [ default True ]
1240 allow_none : Bool [ default True ]
1236 Whether to allow the value to be None
1241 Whether to allow the value to be None
1237
1242
1238 **metadata : any
1243 **metadata : any
1239 further keys for extensions to the Trait (e.g. config)
1244 further keys for extensions to the Trait (e.g. config)
1240
1245
1241 """
1246 """
1242 # allow List([values]):
1247 # allow List([values]):
1243 if default_value is None and not is_trait(trait):
1248 if default_value is None and not is_trait(trait):
1244 default_value = trait
1249 default_value = trait
1245 trait = None
1250 trait = None
1246
1251
1247 if default_value is None:
1252 if default_value is None:
1248 args = ()
1253 args = ()
1249 elif isinstance(default_value, self._valid_defaults):
1254 elif isinstance(default_value, self._valid_defaults):
1250 args = (default_value,)
1255 args = (default_value,)
1251 else:
1256 else:
1252 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1257 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1253
1258
1254 if is_trait(trait):
1259 if is_trait(trait):
1255 self._trait = trait() if isinstance(trait, type) else trait
1260 self._trait = trait() if isinstance(trait, type) else trait
1256 self._trait.name = 'element'
1261 self._trait.name = 'element'
1257 elif trait is not None:
1262 elif trait is not None:
1258 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1263 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1259
1264
1260 super(Container,self).__init__(klass=self.klass, args=args,
1265 super(Container,self).__init__(klass=self.klass, args=args,
1261 allow_none=allow_none, **metadata)
1266 allow_none=allow_none, **metadata)
1262
1267
1263 def element_error(self, obj, element, validator):
1268 def element_error(self, obj, element, validator):
1264 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1269 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1265 % (self.name, class_of(obj), validator.info(), repr_type(element))
1270 % (self.name, class_of(obj), validator.info(), repr_type(element))
1266 raise TraitError(e)
1271 raise TraitError(e)
1267
1272
1268 def validate(self, obj, value):
1273 def validate(self, obj, value):
1269 value = super(Container, self).validate(obj, value)
1274 value = super(Container, self).validate(obj, value)
1270 if value is None:
1275 if value is None:
1271 return value
1276 return value
1272
1277
1273 value = self.validate_elements(obj, value)
1278 value = self.validate_elements(obj, value)
1274
1279
1275 return value
1280 return value
1276
1281
1277 def validate_elements(self, obj, value):
1282 def validate_elements(self, obj, value):
1278 validated = []
1283 validated = []
1279 if self._trait is None or isinstance(self._trait, Any):
1284 if self._trait is None or isinstance(self._trait, Any):
1280 return value
1285 return value
1281 for v in value:
1286 for v in value:
1282 try:
1287 try:
1283 v = self._trait.validate(obj, v)
1288 v = self._trait.validate(obj, v)
1284 except TraitError:
1289 except TraitError:
1285 self.element_error(obj, v, self._trait)
1290 self.element_error(obj, v, self._trait)
1286 else:
1291 else:
1287 validated.append(v)
1292 validated.append(v)
1288 return self.klass(validated)
1293 return self.klass(validated)
1289
1294
1290
1295
1291 class List(Container):
1296 class List(Container):
1292 """An instance of a Python list."""
1297 """An instance of a Python list."""
1293 klass = list
1298 klass = list
1294
1299
1295 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1300 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize,
1296 allow_none=True, **metadata):
1301 allow_none=True, **metadata):
1297 """Create a List trait type from a list, set, or tuple.
1302 """Create a List trait type from a list, set, or tuple.
1298
1303
1299 The default value is created by doing ``List(default_value)``,
1304 The default value is created by doing ``List(default_value)``,
1300 which creates a copy of the ``default_value``.
1305 which creates a copy of the ``default_value``.
1301
1306
1302 ``trait`` can be specified, which restricts the type of elements
1307 ``trait`` can be specified, which restricts the type of elements
1303 in the container to that TraitType.
1308 in the container to that TraitType.
1304
1309
1305 If only one arg is given and it is not a Trait, it is taken as
1310 If only one arg is given and it is not a Trait, it is taken as
1306 ``default_value``:
1311 ``default_value``:
1307
1312
1308 ``c = List([1,2,3])``
1313 ``c = List([1,2,3])``
1309
1314
1310 Parameters
1315 Parameters
1311 ----------
1316 ----------
1312
1317
1313 trait : TraitType [ optional ]
1318 trait : TraitType [ optional ]
1314 the type for restricting the contents of the Container. If unspecified,
1319 the type for restricting the contents of the Container. If unspecified,
1315 types are not checked.
1320 types are not checked.
1316
1321
1317 default_value : SequenceType [ optional ]
1322 default_value : SequenceType [ optional ]
1318 The default value for the Trait. Must be list/tuple/set, and
1323 The default value for the Trait. Must be list/tuple/set, and
1319 will be cast to the container type.
1324 will be cast to the container type.
1320
1325
1321 minlen : Int [ default 0 ]
1326 minlen : Int [ default 0 ]
1322 The minimum length of the input list
1327 The minimum length of the input list
1323
1328
1324 maxlen : Int [ default sys.maxsize ]
1329 maxlen : Int [ default sys.maxsize ]
1325 The maximum length of the input list
1330 The maximum length of the input list
1326
1331
1327 allow_none : Bool [ default True ]
1332 allow_none : Bool [ default True ]
1328 Whether to allow the value to be None
1333 Whether to allow the value to be None
1329
1334
1330 **metadata : any
1335 **metadata : any
1331 further keys for extensions to the Trait (e.g. config)
1336 further keys for extensions to the Trait (e.g. config)
1332
1337
1333 """
1338 """
1334 self._minlen = minlen
1339 self._minlen = minlen
1335 self._maxlen = maxlen
1340 self._maxlen = maxlen
1336 super(List, self).__init__(trait=trait, default_value=default_value,
1341 super(List, self).__init__(trait=trait, default_value=default_value,
1337 allow_none=allow_none, **metadata)
1342 allow_none=allow_none, **metadata)
1338
1343
1339 def length_error(self, obj, value):
1344 def length_error(self, obj, value):
1340 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1345 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1341 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1346 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1342 raise TraitError(e)
1347 raise TraitError(e)
1343
1348
1344 def validate_elements(self, obj, value):
1349 def validate_elements(self, obj, value):
1345 length = len(value)
1350 length = len(value)
1346 if length < self._minlen or length > self._maxlen:
1351 if length < self._minlen or length > self._maxlen:
1347 self.length_error(obj, value)
1352 self.length_error(obj, value)
1348
1353
1349 return super(List, self).validate_elements(obj, value)
1354 return super(List, self).validate_elements(obj, value)
1350
1355
1351
1356
1352 class Set(Container):
1357 class Set(Container):
1353 """An instance of a Python set."""
1358 """An instance of a Python set."""
1354 klass = set
1359 klass = set
1355
1360
1356 class Tuple(Container):
1361 class Tuple(Container):
1357 """An instance of a Python tuple."""
1362 """An instance of a Python tuple."""
1358 klass = tuple
1363 klass = tuple
1359
1364
1360 def __init__(self, *traits, **metadata):
1365 def __init__(self, *traits, **metadata):
1361 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1366 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1362
1367
1363 Create a tuple from a list, set, or tuple.
1368 Create a tuple from a list, set, or tuple.
1364
1369
1365 Create a fixed-type tuple with Traits:
1370 Create a fixed-type tuple with Traits:
1366
1371
1367 ``t = Tuple(Int, Str, CStr)``
1372 ``t = Tuple(Int, Str, CStr)``
1368
1373
1369 would be length 3, with Int,Str,CStr for each element.
1374 would be length 3, with Int,Str,CStr for each element.
1370
1375
1371 If only one arg is given and it is not a Trait, it is taken as
1376 If only one arg is given and it is not a Trait, it is taken as
1372 default_value:
1377 default_value:
1373
1378
1374 ``t = Tuple((1,2,3))``
1379 ``t = Tuple((1,2,3))``
1375
1380
1376 Otherwise, ``default_value`` *must* be specified by keyword.
1381 Otherwise, ``default_value`` *must* be specified by keyword.
1377
1382
1378 Parameters
1383 Parameters
1379 ----------
1384 ----------
1380
1385
1381 *traits : TraitTypes [ optional ]
1386 *traits : TraitTypes [ optional ]
1382 the tsype for restricting the contents of the Tuple. If unspecified,
1387 the tsype for restricting the contents of the Tuple. If unspecified,
1383 types are not checked. If specified, then each positional argument
1388 types are not checked. If specified, then each positional argument
1384 corresponds to an element of the tuple. Tuples defined with traits
1389 corresponds to an element of the tuple. Tuples defined with traits
1385 are of fixed length.
1390 are of fixed length.
1386
1391
1387 default_value : SequenceType [ optional ]
1392 default_value : SequenceType [ optional ]
1388 The default value for the Tuple. Must be list/tuple/set, and
1393 The default value for the Tuple. Must be list/tuple/set, and
1389 will be cast to a tuple. If `traits` are specified, the
1394 will be cast to a tuple. If `traits` are specified, the
1390 `default_value` must conform to the shape and type they specify.
1395 `default_value` must conform to the shape and type they specify.
1391
1396
1392 allow_none : Bool [ default True ]
1397 allow_none : Bool [ default True ]
1393 Whether to allow the value to be None
1398 Whether to allow the value to be None
1394
1399
1395 **metadata : any
1400 **metadata : any
1396 further keys for extensions to the Trait (e.g. config)
1401 further keys for extensions to the Trait (e.g. config)
1397
1402
1398 """
1403 """
1399 default_value = metadata.pop('default_value', None)
1404 default_value = metadata.pop('default_value', None)
1400 allow_none = metadata.pop('allow_none', True)
1405 allow_none = metadata.pop('allow_none', True)
1401
1406
1402 # allow Tuple((values,)):
1407 # allow Tuple((values,)):
1403 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1408 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1404 default_value = traits[0]
1409 default_value = traits[0]
1405 traits = ()
1410 traits = ()
1406
1411
1407 if default_value is None:
1412 if default_value is None:
1408 args = ()
1413 args = ()
1409 elif isinstance(default_value, self._valid_defaults):
1414 elif isinstance(default_value, self._valid_defaults):
1410 args = (default_value,)
1415 args = (default_value,)
1411 else:
1416 else:
1412 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1417 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1413
1418
1414 self._traits = []
1419 self._traits = []
1415 for trait in traits:
1420 for trait in traits:
1416 t = trait() if isinstance(trait, type) else trait
1421 t = trait() if isinstance(trait, type) else trait
1417 t.name = 'element'
1422 t.name = 'element'
1418 self._traits.append(t)
1423 self._traits.append(t)
1419
1424
1420 if self._traits and default_value is None:
1425 if self._traits and default_value is None:
1421 # don't allow default to be an empty container if length is specified
1426 # don't allow default to be an empty container if length is specified
1422 args = None
1427 args = None
1423 super(Container,self).__init__(klass=self.klass, args=args,
1428 super(Container,self).__init__(klass=self.klass, args=args,
1424 allow_none=allow_none, **metadata)
1429 allow_none=allow_none, **metadata)
1425
1430
1426 def validate_elements(self, obj, value):
1431 def validate_elements(self, obj, value):
1427 if not self._traits:
1432 if not self._traits:
1428 # nothing to validate
1433 # nothing to validate
1429 return value
1434 return value
1430 if len(value) != len(self._traits):
1435 if len(value) != len(self._traits):
1431 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1436 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1432 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1437 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1433 raise TraitError(e)
1438 raise TraitError(e)
1434
1439
1435 validated = []
1440 validated = []
1436 for t,v in zip(self._traits, value):
1441 for t,v in zip(self._traits, value):
1437 try:
1442 try:
1438 v = t.validate(obj, v)
1443 v = t.validate(obj, v)
1439 except TraitError:
1444 except TraitError:
1440 self.element_error(obj, v, t)
1445 self.element_error(obj, v, t)
1441 else:
1446 else:
1442 validated.append(v)
1447 validated.append(v)
1443 return tuple(validated)
1448 return tuple(validated)
1444
1449
1445
1450
1446 class Dict(Instance):
1451 class Dict(Instance):
1447 """An instance of a Python dict."""
1452 """An instance of a Python dict."""
1448
1453
1449 def __init__(self, default_value=None, allow_none=True, **metadata):
1454 def __init__(self, default_value=None, allow_none=True, **metadata):
1450 """Create a dict trait type from a dict.
1455 """Create a dict trait type from a dict.
1451
1456
1452 The default value is created by doing ``dict(default_value)``,
1457 The default value is created by doing ``dict(default_value)``,
1453 which creates a copy of the ``default_value``.
1458 which creates a copy of the ``default_value``.
1454 """
1459 """
1455 if default_value is None:
1460 if default_value is None:
1456 args = ((),)
1461 args = ((),)
1457 elif isinstance(default_value, dict):
1462 elif isinstance(default_value, dict):
1458 args = (default_value,)
1463 args = (default_value,)
1459 elif isinstance(default_value, SequenceTypes):
1464 elif isinstance(default_value, SequenceTypes):
1460 args = (default_value,)
1465 args = (default_value,)
1461 else:
1466 else:
1462 raise TypeError('default value of Dict was %s' % default_value)
1467 raise TypeError('default value of Dict was %s' % default_value)
1463
1468
1464 super(Dict,self).__init__(klass=dict, args=args,
1469 super(Dict,self).__init__(klass=dict, args=args,
1465 allow_none=allow_none, **metadata)
1470 allow_none=allow_none, **metadata)
1466
1471
1467 class TCPAddress(TraitType):
1472 class TCPAddress(TraitType):
1468 """A trait for an (ip, port) tuple.
1473 """A trait for an (ip, port) tuple.
1469
1474
1470 This allows for both IPv4 IP addresses as well as hostnames.
1475 This allows for both IPv4 IP addresses as well as hostnames.
1471 """
1476 """
1472
1477
1473 default_value = ('127.0.0.1', 0)
1478 default_value = ('127.0.0.1', 0)
1474 info_text = 'an (ip, port) tuple'
1479 info_text = 'an (ip, port) tuple'
1475
1480
1476 def validate(self, obj, value):
1481 def validate(self, obj, value):
1477 if isinstance(value, tuple):
1482 if isinstance(value, tuple):
1478 if len(value) == 2:
1483 if len(value) == 2:
1479 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1484 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1480 port = value[1]
1485 port = value[1]
1481 if port >= 0 and port <= 65535:
1486 if port >= 0 and port <= 65535:
1482 return value
1487 return value
1483 self.error(obj, value)
1488 self.error(obj, value)
1484
1489
1485 class CRegExp(TraitType):
1490 class CRegExp(TraitType):
1486 """A casting compiled regular expression trait.
1491 """A casting compiled regular expression trait.
1487
1492
1488 Accepts both strings and compiled regular expressions. The resulting
1493 Accepts both strings and compiled regular expressions. The resulting
1489 attribute will be a compiled regular expression."""
1494 attribute will be a compiled regular expression."""
1490
1495
1491 info_text = 'a regular expression'
1496 info_text = 'a regular expression'
1492
1497
1493 def validate(self, obj, value):
1498 def validate(self, obj, value):
1494 try:
1499 try:
1495 return re.compile(value)
1500 return re.compile(value)
1496 except:
1501 except:
1497 self.error(obj, value)
1502 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now