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