##// END OF EJS Templates
Allow trait values to be set in the keyword arguments of __init__.
Brian Granger -
Show More
@@ -1,1047 +1,1048 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight Traits like module.
4 A lightweight Traits like module.
5
5
6 This is designed to provide a lightweight, simple, pure Python version of
6 This is designed to provide a lightweight, simple, pure Python version of
7 many of the capabilities of enthought.traits. This includes:
7 many of the capabilities of enthought.traits. This includes:
8
8
9 * Validation
9 * Validation
10 * Type specification with defaults
10 * Type specification with defaults
11 * Static and dynamic notification
11 * Static and dynamic notification
12 * Basic predefined types
12 * Basic predefined types
13 * An API that is similar to enthought.traits
13 * An API that is similar to enthought.traits
14
14
15 We don't support:
15 We don't support:
16
16
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types. Most importantly, we don't provide container
19 * A full set of trait types. Most importantly, we don't provide container
20 traits (list, dict, tuple) that can trigger notifications if their
20 traits (list, dict, tuple) that can trigger notifications if their
21 contents change.
21 contents change.
22 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
23
23
24 There are also some important difference in our design:
24 There are also some important difference in our design:
25
25
26 * enthought.traits does not validate default values. We do.
26 * enthought.traits does not validate default values. We do.
27
27
28 We choose to create this module because we need these capabilities, but
28 We choose to create this module because we need these capabilities, but
29 we need them to be pure Python so they work in all Python implementations,
29 we need them to be pure Python so they work in all Python implementations,
30 including Jython and IronPython.
30 including Jython and IronPython.
31
31
32 Authors:
32 Authors:
33
33
34 * Brian Granger
34 * Brian Granger
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
36 and is licensed under the BSD license. Also, many of the ideas also come
36 and is licensed under the BSD license. Also, many of the ideas also come
37 from enthought.traits even though our implementation is very different.
37 from enthought.traits even though our implementation is very different.
38 """
38 """
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Copyright (C) 2008-2009 The IPython Development Team
41 # Copyright (C) 2008-2009 The IPython Development Team
42 #
42 #
43 # Distributed under the terms of the BSD License. The full license is in
43 # Distributed under the terms of the BSD License. The full license is in
44 # the file COPYING, distributed as part of this software.
44 # the file COPYING, distributed as part of this software.
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Imports
48 # Imports
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 import inspect
52 import inspect
53 import sys
53 import sys
54 import types
54 import types
55 from types import (
55 from types import (
56 InstanceType, ClassType, FunctionType,
56 InstanceType, ClassType, FunctionType,
57 ListType, TupleType
57 ListType, TupleType
58 )
58 )
59
59
60 def import_item(name):
60 def import_item(name):
61 """Import and return bar given the string foo.bar."""
61 """Import and return bar given the string foo.bar."""
62 package = '.'.join(name.split('.')[0:-1])
62 package = '.'.join(name.split('.')[0:-1])
63 obj = name.split('.')[-1]
63 obj = name.split('.')[-1]
64 execString = 'from %s import %s' % (package, obj)
64 execString = 'from %s import %s' % (package, obj)
65 try:
65 try:
66 exec execString
66 exec execString
67 except SyntaxError:
67 except SyntaxError:
68 raise ImportError("Invalid class specification: %s" % name)
68 raise ImportError("Invalid class specification: %s" % name)
69 exec 'temp = %s' % obj
69 exec 'temp = %s' % obj
70 return temp
70 return temp
71
71
72
72
73 ClassTypes = (ClassType, type)
73 ClassTypes = (ClassType, type)
74
74
75 SequenceTypes = (ListType, TupleType)
75 SequenceTypes = (ListType, TupleType)
76
76
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78 # Basic classes
78 # Basic classes
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80
80
81
81
82 class NoDefaultSpecified ( object ): pass
82 class NoDefaultSpecified ( object ): pass
83 NoDefaultSpecified = NoDefaultSpecified()
83 NoDefaultSpecified = NoDefaultSpecified()
84
84
85
85
86 class Undefined ( object ): pass
86 class Undefined ( object ): pass
87 Undefined = Undefined()
87 Undefined = Undefined()
88
88
89 class TraitError(Exception):
89 class TraitError(Exception):
90 pass
90 pass
91
91
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93 # Utilities
93 # Utilities
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95
95
96
96
97 def class_of ( object ):
97 def class_of ( object ):
98 """ Returns a string containing the class name of an object with the
98 """ Returns a string containing the class name of an object with the
99 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
99 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
100 'a PlotValue').
100 'a PlotValue').
101 """
101 """
102 if isinstance( object, basestring ):
102 if isinstance( object, basestring ):
103 return add_article( object )
103 return add_article( object )
104
104
105 return add_article( object.__class__.__name__ )
105 return add_article( object.__class__.__name__ )
106
106
107
107
108 def add_article ( name ):
108 def add_article ( name ):
109 """ Returns a string containing the correct indefinite article ('a' or 'an')
109 """ Returns a string containing the correct indefinite article ('a' or 'an')
110 prefixed to the specified string.
110 prefixed to the specified string.
111 """
111 """
112 if name[:1].lower() in 'aeiou':
112 if name[:1].lower() in 'aeiou':
113 return 'an ' + name
113 return 'an ' + name
114
114
115 return 'a ' + name
115 return 'a ' + name
116
116
117
117
118 def repr_type(obj):
118 def repr_type(obj):
119 """ Return a string representation of a value and its type for readable
119 """ Return a string representation of a value and its type for readable
120 error messages.
120 error messages.
121 """
121 """
122 the_type = type(obj)
122 the_type = type(obj)
123 if the_type is InstanceType:
123 if the_type is InstanceType:
124 # Old-style class.
124 # Old-style class.
125 the_type = obj.__class__
125 the_type = obj.__class__
126 msg = '%r %r' % (obj, the_type)
126 msg = '%r %r' % (obj, the_type)
127 return msg
127 return msg
128
128
129
129
130 def parse_notifier_name(name):
130 def parse_notifier_name(name):
131 """Convert the name argument to a list of names.
131 """Convert the name argument to a list of names.
132
132
133 Examples
133 Examples
134 --------
134 --------
135
135
136 >>> parse_notifier_name('a')
136 >>> parse_notifier_name('a')
137 ['a']
137 ['a']
138 >>> parse_notifier_name(['a','b'])
138 >>> parse_notifier_name(['a','b'])
139 ['a', 'b']
139 ['a', 'b']
140 >>> parse_notifier_name(None)
140 >>> parse_notifier_name(None)
141 ['anytrait']
141 ['anytrait']
142 """
142 """
143 if isinstance(name, str):
143 if isinstance(name, str):
144 return [name]
144 return [name]
145 elif name is None:
145 elif name is None:
146 return ['anytrait']
146 return ['anytrait']
147 elif isinstance(name, (list, tuple)):
147 elif isinstance(name, (list, tuple)):
148 for n in name:
148 for n in name:
149 assert isinstance(n, str), "names must be strings"
149 assert isinstance(n, str), "names must be strings"
150 return name
150 return name
151
151
152
152
153 class _SimpleTest:
153 class _SimpleTest:
154 def __init__ ( self, value ): self.value = value
154 def __init__ ( self, value ): self.value = value
155 def __call__ ( self, test ):
155 def __call__ ( self, test ):
156 return test == self.value
156 return test == self.value
157 def __repr__(self):
157 def __repr__(self):
158 return "<SimpleTest(%r)" % self.value
158 return "<SimpleTest(%r)" % self.value
159 def __str__(self):
159 def __str__(self):
160 return self.__repr__()
160 return self.__repr__()
161
161
162
162
163 def getmembers(object, predicate=None):
163 def getmembers(object, predicate=None):
164 """A safe version of inspect.getmembers that handles missing attributes.
164 """A safe version of inspect.getmembers that handles missing attributes.
165
165
166 This is useful when there are descriptor based attributes that for
166 This is useful when there are descriptor based attributes that for
167 some reason raise AttributeError even though they exist. This happens
167 some reason raise AttributeError even though they exist. This happens
168 in zope.inteface with the __provides__ attribute.
168 in zope.inteface with the __provides__ attribute.
169 """
169 """
170 results = []
170 results = []
171 for key in dir(object):
171 for key in dir(object):
172 try:
172 try:
173 value = getattr(object, key)
173 value = getattr(object, key)
174 except AttributeError:
174 except AttributeError:
175 pass
175 pass
176 else:
176 else:
177 if not predicate or predicate(value):
177 if not predicate or predicate(value):
178 results.append((key, value))
178 results.append((key, value))
179 results.sort()
179 results.sort()
180 return results
180 return results
181
181
182
182
183 #-----------------------------------------------------------------------------
183 #-----------------------------------------------------------------------------
184 # Base TraitType for all traits
184 # Base TraitType for all traits
185 #-----------------------------------------------------------------------------
185 #-----------------------------------------------------------------------------
186
186
187
187
188 class TraitType(object):
188 class TraitType(object):
189 """A base class for all trait descriptors.
189 """A base class for all trait descriptors.
190
190
191 Notes
191 Notes
192 -----
192 -----
193 Our implementation of traits is based on Python's descriptor
193 Our implementation of traits is based on Python's descriptor
194 prototol. This class is the base class for all such descriptors. The
194 prototol. This class is the base class for all such descriptors. The
195 only magic we use is a custom metaclass for the main :class:`HasTraits`
195 only magic we use is a custom metaclass for the main :class:`HasTraits`
196 class that does the following:
196 class that does the following:
197
197
198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
198 1. Sets the :attr:`name` attribute of every :class:`TraitType`
199 instance in the class dict to the name of the attribute.
199 instance in the class dict to the name of the attribute.
200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
200 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
201 instance in the class dict to the *class* that declared the trait.
201 instance in the class dict to the *class* that declared the trait.
202 This is used by the :class:`This` trait to allow subclasses to
202 This is used by the :class:`This` trait to allow subclasses to
203 accept superclasses for :class:`This` values.
203 accept superclasses for :class:`This` values.
204 """
204 """
205
205
206
206
207 metadata = {}
207 metadata = {}
208 default_value = Undefined
208 default_value = Undefined
209 info_text = 'any value'
209 info_text = 'any value'
210
210
211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
211 def __init__(self, default_value=NoDefaultSpecified, **metadata):
212 """Create a TraitType.
212 """Create a TraitType.
213 """
213 """
214 if default_value is not NoDefaultSpecified:
214 if default_value is not NoDefaultSpecified:
215 self.default_value = default_value
215 self.default_value = default_value
216
216
217 if len(metadata) > 0:
217 if len(metadata) > 0:
218 if len(self.metadata) > 0:
218 if len(self.metadata) > 0:
219 self._metadata = self.metadata.copy()
219 self._metadata = self.metadata.copy()
220 self._metadata.update(metadata)
220 self._metadata.update(metadata)
221 else:
221 else:
222 self._metadata = metadata
222 self._metadata = metadata
223 else:
223 else:
224 self._metadata = self.metadata
224 self._metadata = self.metadata
225
225
226 self.init()
226 self.init()
227
227
228 def init(self):
228 def init(self):
229 pass
229 pass
230
230
231 def get_default_value(self):
231 def get_default_value(self):
232 """Create a new instance of the default value."""
232 """Create a new instance of the default value."""
233 dv = self.default_value
233 dv = self.default_value
234 return dv
234 return dv
235
235
236 def instance_init(self, obj):
236 def instance_init(self, obj):
237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
237 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
238
238
239 Some stages of initialization must be delayed until the parent
239 Some stages of initialization must be delayed until the parent
240 :class:`HasTraits` instance has been created. This method is
240 :class:`HasTraits` instance has been created. This method is
241 called in :meth:`HasTraits.__new__` after the instance has been
241 called in :meth:`HasTraits.__new__` after the instance has been
242 created.
242 created.
243
243
244 This method trigger the creation and validation of default values
244 This method trigger the creation and validation of default values
245 and also things like the resolution of str given class names in
245 and also things like the resolution of str given class names in
246 :class:`Type` and :class`Instance`.
246 :class:`Type` and :class`Instance`.
247
247
248 Parameters
248 Parameters
249 ----------
249 ----------
250 obj : :class:`HasTraits` instance
250 obj : :class:`HasTraits` instance
251 The parent :class:`HasTraits` instance that has just been
251 The parent :class:`HasTraits` instance that has just been
252 created.
252 created.
253 """
253 """
254 self.set_default_value(obj)
254 self.set_default_value(obj)
255
255
256 def set_default_value(self, obj):
256 def set_default_value(self, obj):
257 """Set the default value on a per instance basis.
257 """Set the default value on a per instance basis.
258
258
259 This method is called by :meth:`instance_init` to create and
259 This method is called by :meth:`instance_init` to create and
260 validate the default value. The creation and validation of
260 validate the default value. The creation and validation of
261 default values must be delayed until the parent :class:`HasTraits`
261 default values must be delayed until the parent :class:`HasTraits`
262 class has been instantiated.
262 class has been instantiated.
263 """
263 """
264 dv = self.get_default_value()
264 dv = self.get_default_value()
265 newdv = self._validate(obj, dv)
265 newdv = self._validate(obj, dv)
266 obj._trait_values[self.name] = newdv
266 obj._trait_values[self.name] = newdv
267
267
268 def __get__(self, obj, cls=None):
268 def __get__(self, obj, cls=None):
269 """Get the value of the trait by self.name for the instance.
269 """Get the value of the trait by self.name for the instance.
270
270
271 Default values are instantiated when :meth:`HasTraits.__new__`
271 Default values are instantiated when :meth:`HasTraits.__new__`
272 is called. Thus by the time this method gets called either the
272 is called. Thus by the time this method gets called either the
273 default value or a user defined value (they called :meth:`__set__`)
273 default value or a user defined value (they called :meth:`__set__`)
274 is in the :class:`HasTraits` instance.
274 is in the :class:`HasTraits` instance.
275 """
275 """
276 if obj is None:
276 if obj is None:
277 return self
277 return self
278 else:
278 else:
279 try:
279 try:
280 value = obj._trait_values[self.name]
280 value = obj._trait_values[self.name]
281 except:
281 except:
282 # HasTraits should call set_default_value to populate
282 # HasTraits should call set_default_value to populate
283 # this. So this should never be reached.
283 # this. So this should never be reached.
284 raise TraitError('Unexpected error in TraitType: '
284 raise TraitError('Unexpected error in TraitType: '
285 'default value not set properly')
285 'default value not set properly')
286 else:
286 else:
287 return value
287 return value
288
288
289 def __set__(self, obj, value):
289 def __set__(self, obj, value):
290 new_value = self._validate(obj, value)
290 new_value = self._validate(obj, value)
291 old_value = self.__get__(obj)
291 old_value = self.__get__(obj)
292 if old_value != new_value:
292 if old_value != new_value:
293 obj._trait_values[self.name] = new_value
293 obj._trait_values[self.name] = new_value
294 obj._notify_trait(self.name, old_value, new_value)
294 obj._notify_trait(self.name, old_value, new_value)
295
295
296 def _validate(self, obj, value):
296 def _validate(self, obj, value):
297 if hasattr(self, 'validate'):
297 if hasattr(self, 'validate'):
298 return self.validate(obj, value)
298 return self.validate(obj, value)
299 elif hasattr(self, 'is_valid_for'):
299 elif hasattr(self, 'is_valid_for'):
300 valid = self.is_valid_for(value)
300 valid = self.is_valid_for(value)
301 if valid:
301 if valid:
302 return value
302 return value
303 else:
303 else:
304 raise TraitError('invalid value for type: %r' % value)
304 raise TraitError('invalid value for type: %r' % value)
305 elif hasattr(self, 'value_for'):
305 elif hasattr(self, 'value_for'):
306 return self.value_for(value)
306 return self.value_for(value)
307 else:
307 else:
308 return value
308 return value
309
309
310 def info(self):
310 def info(self):
311 return self.info_text
311 return self.info_text
312
312
313 def error(self, obj, value):
313 def error(self, obj, value):
314 if obj is not None:
314 if obj is not None:
315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
315 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
316 % (self.name, class_of(obj),
316 % (self.name, class_of(obj),
317 self.info(), repr_type(value))
317 self.info(), repr_type(value))
318 else:
318 else:
319 e = "The '%s' trait must be %s, but a value of %r was specified." \
319 e = "The '%s' trait must be %s, but a value of %r was specified." \
320 % (self.name, self.info(), repr_type(value))
320 % (self.name, self.info(), repr_type(value))
321 raise TraitError(e)
321 raise TraitError(e)
322
322
323 def get_metadata(self, key):
323 def get_metadata(self, key):
324 return getattr(self, '_metadata', {}).get(key, None)
324 return getattr(self, '_metadata', {}).get(key, None)
325
325
326 def set_metadata(self, key, value):
326 def set_metadata(self, key, value):
327 getattr(self, '_metadata', {})[key] = value
327 getattr(self, '_metadata', {})[key] = value
328
328
329
329
330 #-----------------------------------------------------------------------------
330 #-----------------------------------------------------------------------------
331 # The HasTraits implementation
331 # The HasTraits implementation
332 #-----------------------------------------------------------------------------
332 #-----------------------------------------------------------------------------
333
333
334
334
335 class MetaHasTraits(type):
335 class MetaHasTraits(type):
336 """A metaclass for HasTraits.
336 """A metaclass for HasTraits.
337
337
338 This metaclass makes sure that any TraitType class attributes are
338 This metaclass makes sure that any TraitType class attributes are
339 instantiated and sets their name attribute.
339 instantiated and sets their name attribute.
340 """
340 """
341
341
342 def __new__(mcls, name, bases, classdict):
342 def __new__(mcls, name, bases, classdict):
343 """Create the HasTraits class.
343 """Create the HasTraits class.
344
344
345 This instantiates all TraitTypes in the class dict and sets their
345 This instantiates all TraitTypes in the class dict and sets their
346 :attr:`name` attribute.
346 :attr:`name` attribute.
347 """
347 """
348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
348 # print "MetaHasTraitlets (mcls, name): ", mcls, name
349 # print "MetaHasTraitlets (bases): ", bases
349 # print "MetaHasTraitlets (bases): ", bases
350 # print "MetaHasTraitlets (classdict): ", classdict
350 # print "MetaHasTraitlets (classdict): ", classdict
351 for k,v in classdict.iteritems():
351 for k,v in classdict.iteritems():
352 if isinstance(v, TraitType):
352 if isinstance(v, TraitType):
353 v.name = k
353 v.name = k
354 elif inspect.isclass(v):
354 elif inspect.isclass(v):
355 if issubclass(v, TraitType):
355 if issubclass(v, TraitType):
356 vinst = v()
356 vinst = v()
357 vinst.name = k
357 vinst.name = k
358 classdict[k] = vinst
358 classdict[k] = vinst
359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
359 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
360
360
361 def __init__(cls, name, bases, classdict):
361 def __init__(cls, name, bases, classdict):
362 """Finish initializing the HasTraits class.
362 """Finish initializing the HasTraits class.
363
363
364 This sets the :attr:`this_class` attribute of each TraitType in the
364 This sets the :attr:`this_class` attribute of each TraitType in the
365 class dict to the newly created class ``cls``.
365 class dict to the newly created class ``cls``.
366 """
366 """
367 for k, v in classdict.iteritems():
367 for k, v in classdict.iteritems():
368 if isinstance(v, TraitType):
368 if isinstance(v, TraitType):
369 v.this_class = cls
369 v.this_class = cls
370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
370 super(MetaHasTraits, cls).__init__(name, bases, classdict)
371
371
372 class HasTraits(object):
372 class HasTraits(object):
373
373
374 __metaclass__ = MetaHasTraits
374 __metaclass__ = MetaHasTraits
375
375
376 def __new__(cls, *args, **kw):
376 def __new__(cls, **kw):
377 # This is needed because in Python 2.6 object.__new__ only accepts
377 # This is needed because in Python 2.6 object.__new__ only accepts
378 # the cls argument.
378 # the cls argument.
379 new_meth = super(HasTraits, cls).__new__
379 new_meth = super(HasTraits, cls).__new__
380 if new_meth is object.__new__:
380 if new_meth is object.__new__:
381 inst = new_meth(cls)
381 inst = new_meth(cls)
382 else:
382 else:
383 inst = new_meth(cls, *args, **kw)
383 inst = new_meth(cls, **kw)
384 inst._trait_values = {}
384 inst._trait_values = {}
385 inst._trait_notifiers = {}
385 inst._trait_notifiers = {}
386 # Here we tell all the TraitType instances to set their default
386 # Here we tell all the TraitType instances to set their default
387 # values on the instance.
387 # values on the instance.
388 for key in dir(cls):
388 for key in dir(cls):
389 # Some descriptors raise AttributeError like zope.interface's
389 # Some descriptors raise AttributeError like zope.interface's
390 # __provides__ attributes even though they exist. This causes
390 # __provides__ attributes even though they exist. This causes
391 # AttributeErrors even though they are listed in dir(cls).
391 # AttributeErrors even though they are listed in dir(cls).
392 try:
392 try:
393 value = getattr(cls, key)
393 value = getattr(cls, key)
394 except AttributeError:
394 except AttributeError:
395 pass
395 pass
396 else:
396 else:
397 if isinstance(value, TraitType):
397 if isinstance(value, TraitType):
398 value.instance_init(inst)
398 value.instance_init(inst)
399
399
400 return inst
400 return inst
401
401
402 # def __init__(self):
402 def __init__(self, *kw):
403 # self._trait_values = {}
403 # Allow trait values to be set using keywork arguments.
404 # self._trait_notifiers = {}
404 for key, value in kw.iteritems():
405 setattr(self, key, value)
405
406
406 def _notify_trait(self, name, old_value, new_value):
407 def _notify_trait(self, name, old_value, new_value):
407
408
408 # First dynamic ones
409 # First dynamic ones
409 callables = self._trait_notifiers.get(name,[])
410 callables = self._trait_notifiers.get(name,[])
410 more_callables = self._trait_notifiers.get('anytrait',[])
411 more_callables = self._trait_notifiers.get('anytrait',[])
411 callables.extend(more_callables)
412 callables.extend(more_callables)
412
413
413 # Now static ones
414 # Now static ones
414 try:
415 try:
415 cb = getattr(self, '_%s_changed' % name)
416 cb = getattr(self, '_%s_changed' % name)
416 except:
417 except:
417 pass
418 pass
418 else:
419 else:
419 callables.append(cb)
420 callables.append(cb)
420
421
421 # Call them all now
422 # Call them all now
422 for c in callables:
423 for c in callables:
423 # Traits catches and logs errors here. I allow them to raise
424 # Traits catches and logs errors here. I allow them to raise
424 if callable(c):
425 if callable(c):
425 argspec = inspect.getargspec(c)
426 argspec = inspect.getargspec(c)
426 nargs = len(argspec[0])
427 nargs = len(argspec[0])
427 # Bound methods have an additional 'self' argument
428 # Bound methods have an additional 'self' argument
428 # I don't know how to treat unbound methods, but they
429 # I don't know how to treat unbound methods, but they
429 # can't really be used for callbacks.
430 # can't really be used for callbacks.
430 if isinstance(c, types.MethodType):
431 if isinstance(c, types.MethodType):
431 offset = -1
432 offset = -1
432 else:
433 else:
433 offset = 0
434 offset = 0
434 if nargs + offset == 0:
435 if nargs + offset == 0:
435 c()
436 c()
436 elif nargs + offset == 1:
437 elif nargs + offset == 1:
437 c(name)
438 c(name)
438 elif nargs + offset == 2:
439 elif nargs + offset == 2:
439 c(name, new_value)
440 c(name, new_value)
440 elif nargs + offset == 3:
441 elif nargs + offset == 3:
441 c(name, old_value, new_value)
442 c(name, old_value, new_value)
442 else:
443 else:
443 raise TraitError('a trait changed callback '
444 raise TraitError('a trait changed callback '
444 'must have 0-3 arguments.')
445 'must have 0-3 arguments.')
445 else:
446 else:
446 raise TraitError('a trait changed callback '
447 raise TraitError('a trait changed callback '
447 'must be callable.')
448 'must be callable.')
448
449
449
450
450 def _add_notifiers(self, handler, name):
451 def _add_notifiers(self, handler, name):
451 if not self._trait_notifiers.has_key(name):
452 if not self._trait_notifiers.has_key(name):
452 nlist = []
453 nlist = []
453 self._trait_notifiers[name] = nlist
454 self._trait_notifiers[name] = nlist
454 else:
455 else:
455 nlist = self._trait_notifiers[name]
456 nlist = self._trait_notifiers[name]
456 if handler not in nlist:
457 if handler not in nlist:
457 nlist.append(handler)
458 nlist.append(handler)
458
459
459 def _remove_notifiers(self, handler, name):
460 def _remove_notifiers(self, handler, name):
460 if self._trait_notifiers.has_key(name):
461 if self._trait_notifiers.has_key(name):
461 nlist = self._trait_notifiers[name]
462 nlist = self._trait_notifiers[name]
462 try:
463 try:
463 index = nlist.index(handler)
464 index = nlist.index(handler)
464 except ValueError:
465 except ValueError:
465 pass
466 pass
466 else:
467 else:
467 del nlist[index]
468 del nlist[index]
468
469
469 def on_trait_change(self, handler, name=None, remove=False):
470 def on_trait_change(self, handler, name=None, remove=False):
470 """Setup a handler to be called when a trait changes.
471 """Setup a handler to be called when a trait changes.
471
472
472 This is used to setup dynamic notifications of trait changes.
473 This is used to setup dynamic notifications of trait changes.
473
474
474 Static handlers can be created by creating methods on a HasTraits
475 Static handlers can be created by creating methods on a HasTraits
475 subclass with the naming convention '_[traitname]_changed'. Thus,
476 subclass with the naming convention '_[traitname]_changed'. Thus,
476 to create static handler for the trait 'a', create the method
477 to create static handler for the trait 'a', create the method
477 _a_changed(self, name, old, new) (fewer arguments can be used, see
478 _a_changed(self, name, old, new) (fewer arguments can be used, see
478 below).
479 below).
479
480
480 Parameters
481 Parameters
481 ----------
482 ----------
482 handler : callable
483 handler : callable
483 A callable that is called when a trait changes. Its
484 A callable that is called when a trait changes. Its
484 signature can be handler(), handler(name), handler(name, new)
485 signature can be handler(), handler(name), handler(name, new)
485 or handler(name, old, new).
486 or handler(name, old, new).
486 name : list, str, None
487 name : list, str, None
487 If None, the handler will apply to all traits. If a list
488 If None, the handler will apply to all traits. If a list
488 of str, handler will apply to all names in the list. If a
489 of str, handler will apply to all names in the list. If a
489 str, the handler will apply just to that name.
490 str, the handler will apply just to that name.
490 remove : bool
491 remove : bool
491 If False (the default), then install the handler. If True
492 If False (the default), then install the handler. If True
492 then unintall it.
493 then unintall it.
493 """
494 """
494 if remove:
495 if remove:
495 names = parse_notifier_name(name)
496 names = parse_notifier_name(name)
496 for n in names:
497 for n in names:
497 self._remove_notifiers(handler, n)
498 self._remove_notifiers(handler, n)
498 else:
499 else:
499 names = parse_notifier_name(name)
500 names = parse_notifier_name(name)
500 for n in names:
501 for n in names:
501 self._add_notifiers(handler, n)
502 self._add_notifiers(handler, n)
502
503
503 def trait_names(self, **metadata):
504 def trait_names(self, **metadata):
504 """Get a list of all the names of this classes traits."""
505 """Get a list of all the names of this classes traits."""
505 return self.traits(**metadata).keys()
506 return self.traits(**metadata).keys()
506
507
507 def traits(self, **metadata):
508 def traits(self, **metadata):
508 """Get a list of all the traits of this class.
509 """Get a list of all the traits of this class.
509
510
510 The TraitTypes returned don't know anything about the values
511 The TraitTypes returned don't know anything about the values
511 that the various HasTrait's instances are holding.
512 that the various HasTrait's instances are holding.
512
513
513 This follows the same algorithm as traits does and does not allow
514 This follows the same algorithm as traits does and does not allow
514 for any simple way of specifying merely that a metadata name
515 for any simple way of specifying merely that a metadata name
515 exists, but has any value. This is because get_metadata returns
516 exists, but has any value. This is because get_metadata returns
516 None if a metadata key doesn't exist.
517 None if a metadata key doesn't exist.
517 """
518 """
518 traits = dict([memb for memb in getmembers(self.__class__) if \
519 traits = dict([memb for memb in getmembers(self.__class__) if \
519 isinstance(memb[1], TraitType)])
520 isinstance(memb[1], TraitType)])
520
521
521 if len(metadata) == 0:
522 if len(metadata) == 0:
522 return traits
523 return traits
523
524
524 for meta_name, meta_eval in metadata.items():
525 for meta_name, meta_eval in metadata.items():
525 if type(meta_eval) is not FunctionType:
526 if type(meta_eval) is not FunctionType:
526 metadata[meta_name] = _SimpleTest(meta_eval)
527 metadata[meta_name] = _SimpleTest(meta_eval)
527
528
528 result = {}
529 result = {}
529 for name, trait in traits.items():
530 for name, trait in traits.items():
530 for meta_name, meta_eval in metadata.items():
531 for meta_name, meta_eval in metadata.items():
531 if not meta_eval(trait.get_metadata(meta_name)):
532 if not meta_eval(trait.get_metadata(meta_name)):
532 break
533 break
533 else:
534 else:
534 result[name] = trait
535 result[name] = trait
535
536
536 return result
537 return result
537
538
538 def trait_metadata(self, traitname, key):
539 def trait_metadata(self, traitname, key):
539 """Get metadata values for trait by key."""
540 """Get metadata values for trait by key."""
540 try:
541 try:
541 trait = getattr(self.__class__, traitname)
542 trait = getattr(self.__class__, traitname)
542 except AttributeError:
543 except AttributeError:
543 raise TraitError("Class %s does not have a trait named %s" %
544 raise TraitError("Class %s does not have a trait named %s" %
544 (self.__class__.__name__, traitname))
545 (self.__class__.__name__, traitname))
545 else:
546 else:
546 return trait.get_metadata(key)
547 return trait.get_metadata(key)
547
548
548 #-----------------------------------------------------------------------------
549 #-----------------------------------------------------------------------------
549 # Actual TraitTypes implementations/subclasses
550 # Actual TraitTypes implementations/subclasses
550 #-----------------------------------------------------------------------------
551 #-----------------------------------------------------------------------------
551
552
552 #-----------------------------------------------------------------------------
553 #-----------------------------------------------------------------------------
553 # TraitTypes subclasses for handling classes and instances of classes
554 # TraitTypes subclasses for handling classes and instances of classes
554 #-----------------------------------------------------------------------------
555 #-----------------------------------------------------------------------------
555
556
556
557
557 class ClassBasedTraitType(TraitType):
558 class ClassBasedTraitType(TraitType):
558 """A trait with error reporting for Type, Instance and This."""
559 """A trait with error reporting for Type, Instance and This."""
559
560
560 def error(self, obj, value):
561 def error(self, obj, value):
561 kind = type(value)
562 kind = type(value)
562 if kind is InstanceType:
563 if kind is InstanceType:
563 msg = 'class %s' % value.__class__.__name__
564 msg = 'class %s' % value.__class__.__name__
564 else:
565 else:
565 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
566 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
566
567
567 super(ClassBasedTraitType, self).error(obj, msg)
568 super(ClassBasedTraitType, self).error(obj, msg)
568
569
569
570
570 class Type(ClassBasedTraitType):
571 class Type(ClassBasedTraitType):
571 """A trait whose value must be a subclass of a specified class."""
572 """A trait whose value must be a subclass of a specified class."""
572
573
573 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
574 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
574 """Construct a Type trait
575 """Construct a Type trait
575
576
576 A Type trait specifies that its values must be subclasses of
577 A Type trait specifies that its values must be subclasses of
577 a particular class.
578 a particular class.
578
579
579 If only ``default_value`` is given, it is used for the ``klass`` as
580 If only ``default_value`` is given, it is used for the ``klass`` as
580 well.
581 well.
581
582
582 Parameters
583 Parameters
583 ----------
584 ----------
584 default_value : class, str or None
585 default_value : class, str or None
585 The default value must be a subclass of klass. If an str,
586 The default value must be a subclass of klass. If an str,
586 the str must be a fully specified class name, like 'foo.bar.Bah'.
587 the str must be a fully specified class name, like 'foo.bar.Bah'.
587 The string is resolved into real class, when the parent
588 The string is resolved into real class, when the parent
588 :class:`HasTraits` class is instantiated.
589 :class:`HasTraits` class is instantiated.
589 klass : class, str, None
590 klass : class, str, None
590 Values of this trait must be a subclass of klass. The klass
591 Values of this trait must be a subclass of klass. The klass
591 may be specified in a string like: 'foo.bar.MyClass'.
592 may be specified in a string like: 'foo.bar.MyClass'.
592 The string is resolved into real class, when the parent
593 The string is resolved into real class, when the parent
593 :class:`HasTraits` class is instantiated.
594 :class:`HasTraits` class is instantiated.
594 allow_none : boolean
595 allow_none : boolean
595 Indicates whether None is allowed as an assignable value. Even if
596 Indicates whether None is allowed as an assignable value. Even if
596 ``False``, the default value may be ``None``.
597 ``False``, the default value may be ``None``.
597 """
598 """
598 if default_value is None:
599 if default_value is None:
599 if klass is None:
600 if klass is None:
600 klass = object
601 klass = object
601 elif klass is None:
602 elif klass is None:
602 klass = default_value
603 klass = default_value
603
604
604 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
605 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
605 raise TraitError("A Type trait must specify a class.")
606 raise TraitError("A Type trait must specify a class.")
606
607
607 self.klass = klass
608 self.klass = klass
608 self._allow_none = allow_none
609 self._allow_none = allow_none
609
610
610 super(Type, self).__init__(default_value, **metadata)
611 super(Type, self).__init__(default_value, **metadata)
611
612
612 def validate(self, obj, value):
613 def validate(self, obj, value):
613 """Validates that the value is a valid object instance."""
614 """Validates that the value is a valid object instance."""
614 try:
615 try:
615 if issubclass(value, self.klass):
616 if issubclass(value, self.klass):
616 return value
617 return value
617 except:
618 except:
618 if (value is None) and (self._allow_none):
619 if (value is None) and (self._allow_none):
619 return value
620 return value
620
621
621 self.error(obj, value)
622 self.error(obj, value)
622
623
623 def info(self):
624 def info(self):
624 """ Returns a description of the trait."""
625 """ Returns a description of the trait."""
625 if isinstance(self.klass, basestring):
626 if isinstance(self.klass, basestring):
626 klass = self.klass
627 klass = self.klass
627 else:
628 else:
628 klass = self.klass.__name__
629 klass = self.klass.__name__
629 result = 'a subclass of ' + klass
630 result = 'a subclass of ' + klass
630 if self._allow_none:
631 if self._allow_none:
631 return result + ' or None'
632 return result + ' or None'
632 return result
633 return result
633
634
634 def instance_init(self, obj):
635 def instance_init(self, obj):
635 self._resolve_classes()
636 self._resolve_classes()
636 super(Type, self).instance_init(obj)
637 super(Type, self).instance_init(obj)
637
638
638 def _resolve_classes(self):
639 def _resolve_classes(self):
639 if isinstance(self.klass, basestring):
640 if isinstance(self.klass, basestring):
640 self.klass = import_item(self.klass)
641 self.klass = import_item(self.klass)
641 if isinstance(self.default_value, basestring):
642 if isinstance(self.default_value, basestring):
642 self.default_value = import_item(self.default_value)
643 self.default_value = import_item(self.default_value)
643
644
644 def get_default_value(self):
645 def get_default_value(self):
645 return self.default_value
646 return self.default_value
646
647
647
648
648 class DefaultValueGenerator(object):
649 class DefaultValueGenerator(object):
649 """A class for generating new default value instances."""
650 """A class for generating new default value instances."""
650
651
651 def __init__(self, *args, **kw):
652 def __init__(self, *args, **kw):
652 self.args = args
653 self.args = args
653 self.kw = kw
654 self.kw = kw
654
655
655 def generate(self, klass):
656 def generate(self, klass):
656 return klass(*self.args, **self.kw)
657 return klass(*self.args, **self.kw)
657
658
658
659
659 class Instance(ClassBasedTraitType):
660 class Instance(ClassBasedTraitType):
660 """A trait whose value must be an instance of a specified class.
661 """A trait whose value must be an instance of a specified class.
661
662
662 The value can also be an instance of a subclass of the specified class.
663 The value can also be an instance of a subclass of the specified class.
663 """
664 """
664
665
665 def __init__(self, klass=None, args=None, kw=None,
666 def __init__(self, klass=None, args=None, kw=None,
666 allow_none=True, **metadata ):
667 allow_none=True, **metadata ):
667 """Construct an Instance trait.
668 """Construct an Instance trait.
668
669
669 This trait allows values that are instances of a particular
670 This trait allows values that are instances of a particular
670 class or its sublclasses. Our implementation is quite different
671 class or its sublclasses. Our implementation is quite different
671 from that of enthough.traits as we don't allow instances to be used
672 from that of enthough.traits as we don't allow instances to be used
672 for klass and we handle the ``args`` and ``kw`` arguments differently.
673 for klass and we handle the ``args`` and ``kw`` arguments differently.
673
674
674 Parameters
675 Parameters
675 ----------
676 ----------
676 klass : class, str
677 klass : class, str
677 The class that forms the basis for the trait. Class names
678 The class that forms the basis for the trait. Class names
678 can also be specified as strings, like 'foo.bar.Bar'.
679 can also be specified as strings, like 'foo.bar.Bar'.
679 args : tuple
680 args : tuple
680 Positional arguments for generating the default value.
681 Positional arguments for generating the default value.
681 kw : dict
682 kw : dict
682 Keyword arguments for generating the default value.
683 Keyword arguments for generating the default value.
683 allow_none : bool
684 allow_none : bool
684 Indicates whether None is allowed as a value.
685 Indicates whether None is allowed as a value.
685
686
686 Default Value
687 Default Value
687 -------------
688 -------------
688 If both ``args`` and ``kw`` are None, then the default value is None.
689 If both ``args`` and ``kw`` are None, then the default value is None.
689 If ``args`` is a tuple and ``kw`` is a dict, then the default is
690 If ``args`` is a tuple and ``kw`` is a dict, then the default is
690 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
691 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
691 not (but not both), None is replace by ``()`` or ``{}``.
692 not (but not both), None is replace by ``()`` or ``{}``.
692 """
693 """
693
694
694 self._allow_none = allow_none
695 self._allow_none = allow_none
695
696
696 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
697 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
697 raise TraitError('The klass argument must be a class'
698 raise TraitError('The klass argument must be a class'
698 ' you gave: %r' % klass)
699 ' you gave: %r' % klass)
699 self.klass = klass
700 self.klass = klass
700
701
701 # self.klass is a class, so handle default_value
702 # self.klass is a class, so handle default_value
702 if args is None and kw is None:
703 if args is None and kw is None:
703 default_value = None
704 default_value = None
704 else:
705 else:
705 if args is None:
706 if args is None:
706 # kw is not None
707 # kw is not None
707 args = ()
708 args = ()
708 elif kw is None:
709 elif kw is None:
709 # args is not None
710 # args is not None
710 kw = {}
711 kw = {}
711
712
712 if not isinstance(kw, dict):
713 if not isinstance(kw, dict):
713 raise TraitError("The 'kw' argument must be a dict or None.")
714 raise TraitError("The 'kw' argument must be a dict or None.")
714 if not isinstance(args, tuple):
715 if not isinstance(args, tuple):
715 raise TraitError("The 'args' argument must be a tuple or None.")
716 raise TraitError("The 'args' argument must be a tuple or None.")
716
717
717 default_value = DefaultValueGenerator(*args, **kw)
718 default_value = DefaultValueGenerator(*args, **kw)
718
719
719 super(Instance, self).__init__(default_value, **metadata)
720 super(Instance, self).__init__(default_value, **metadata)
720
721
721 def validate(self, obj, value):
722 def validate(self, obj, value):
722 if value is None:
723 if value is None:
723 if self._allow_none:
724 if self._allow_none:
724 return value
725 return value
725 self.error(obj, value)
726 self.error(obj, value)
726
727
727 if isinstance(value, self.klass):
728 if isinstance(value, self.klass):
728 return value
729 return value
729 else:
730 else:
730 self.error(obj, value)
731 self.error(obj, value)
731
732
732 def info(self):
733 def info(self):
733 if isinstance(self.klass, basestring):
734 if isinstance(self.klass, basestring):
734 klass = self.klass
735 klass = self.klass
735 else:
736 else:
736 klass = self.klass.__name__
737 klass = self.klass.__name__
737 result = class_of(klass)
738 result = class_of(klass)
738 if self._allow_none:
739 if self._allow_none:
739 return result + ' or None'
740 return result + ' or None'
740
741
741 return result
742 return result
742
743
743 def instance_init(self, obj):
744 def instance_init(self, obj):
744 self._resolve_classes()
745 self._resolve_classes()
745 super(Instance, self).instance_init(obj)
746 super(Instance, self).instance_init(obj)
746
747
747 def _resolve_classes(self):
748 def _resolve_classes(self):
748 if isinstance(self.klass, basestring):
749 if isinstance(self.klass, basestring):
749 self.klass = import_item(self.klass)
750 self.klass = import_item(self.klass)
750
751
751 def get_default_value(self):
752 def get_default_value(self):
752 """Instantiate a default value instance.
753 """Instantiate a default value instance.
753
754
754 This is called when the containing HasTraits classes'
755 This is called when the containing HasTraits classes'
755 :meth:`__new__` method is called to ensure that a unique instance
756 :meth:`__new__` method is called to ensure that a unique instance
756 is created for each HasTraits instance.
757 is created for each HasTraits instance.
757 """
758 """
758 dv = self.default_value
759 dv = self.default_value
759 if isinstance(dv, DefaultValueGenerator):
760 if isinstance(dv, DefaultValueGenerator):
760 return dv.generate(self.klass)
761 return dv.generate(self.klass)
761 else:
762 else:
762 return dv
763 return dv
763
764
764
765
765 class This(ClassBasedTraitType):
766 class This(ClassBasedTraitType):
766 """A trait for instances of the class containing this trait.
767 """A trait for instances of the class containing this trait.
767
768
768 Because how how and when class bodies are executed, the ``This``
769 Because how how and when class bodies are executed, the ``This``
769 trait can only have a default value of None. This, and because we
770 trait can only have a default value of None. This, and because we
770 always validate default values, ``allow_none`` is *always* true.
771 always validate default values, ``allow_none`` is *always* true.
771 """
772 """
772
773
773 info_text = 'an instance of the same type as the receiver or None'
774 info_text = 'an instance of the same type as the receiver or None'
774
775
775 def __init__(self, **metadata):
776 def __init__(self, **metadata):
776 super(This, self).__init__(None, **metadata)
777 super(This, self).__init__(None, **metadata)
777
778
778 def validate(self, obj, value):
779 def validate(self, obj, value):
779 # What if value is a superclass of obj.__class__? This is
780 # What if value is a superclass of obj.__class__? This is
780 # complicated if it was the superclass that defined the This
781 # complicated if it was the superclass that defined the This
781 # trait.
782 # trait.
782 if isinstance(value, self.this_class) or (value is None):
783 if isinstance(value, self.this_class) or (value is None):
783 return value
784 return value
784 else:
785 else:
785 self.error(obj, value)
786 self.error(obj, value)
786
787
787
788
788 #-----------------------------------------------------------------------------
789 #-----------------------------------------------------------------------------
789 # Basic TraitTypes implementations/subclasses
790 # Basic TraitTypes implementations/subclasses
790 #-----------------------------------------------------------------------------
791 #-----------------------------------------------------------------------------
791
792
792
793
793 class Any(TraitType):
794 class Any(TraitType):
794 default_value = None
795 default_value = None
795 info_text = 'any value'
796 info_text = 'any value'
796
797
797
798
798 class Int(TraitType):
799 class Int(TraitType):
799 """A integer trait."""
800 """A integer trait."""
800
801
801 evaluate = int
802 evaluate = int
802 default_value = 0
803 default_value = 0
803 info_text = 'an integer'
804 info_text = 'an integer'
804
805
805 def validate(self, obj, value):
806 def validate(self, obj, value):
806 if isinstance(value, int):
807 if isinstance(value, int):
807 return value
808 return value
808 self.error(obj, value)
809 self.error(obj, value)
809
810
810 class CInt(Int):
811 class CInt(Int):
811 """A casting version of the int trait."""
812 """A casting version of the int trait."""
812
813
813 def validate(self, obj, value):
814 def validate(self, obj, value):
814 try:
815 try:
815 return int(value)
816 return int(value)
816 except:
817 except:
817 self.error(obj, value)
818 self.error(obj, value)
818
819
819
820
820 class Long(TraitType):
821 class Long(TraitType):
821 """A long integer trait."""
822 """A long integer trait."""
822
823
823 evaluate = long
824 evaluate = long
824 default_value = 0L
825 default_value = 0L
825 info_text = 'a long'
826 info_text = 'a long'
826
827
827 def validate(self, obj, value):
828 def validate(self, obj, value):
828 if isinstance(value, long):
829 if isinstance(value, long):
829 return value
830 return value
830 if isinstance(value, int):
831 if isinstance(value, int):
831 return long(value)
832 return long(value)
832 self.error(obj, value)
833 self.error(obj, value)
833
834
834
835
835 class CLong(Long):
836 class CLong(Long):
836 """A casting version of the long integer trait."""
837 """A casting version of the long integer trait."""
837
838
838 def validate(self, obj, value):
839 def validate(self, obj, value):
839 try:
840 try:
840 return long(value)
841 return long(value)
841 except:
842 except:
842 self.error(obj, value)
843 self.error(obj, value)
843
844
844
845
845 class Float(TraitType):
846 class Float(TraitType):
846 """A float trait."""
847 """A float trait."""
847
848
848 evaluate = float
849 evaluate = float
849 default_value = 0.0
850 default_value = 0.0
850 info_text = 'a float'
851 info_text = 'a float'
851
852
852 def validate(self, obj, value):
853 def validate(self, obj, value):
853 if isinstance(value, float):
854 if isinstance(value, float):
854 return value
855 return value
855 if isinstance(value, int):
856 if isinstance(value, int):
856 return float(value)
857 return float(value)
857 self.error(obj, value)
858 self.error(obj, value)
858
859
859
860
860 class CFloat(Float):
861 class CFloat(Float):
861 """A casting version of the float trait."""
862 """A casting version of the float trait."""
862
863
863 def validate(self, obj, value):
864 def validate(self, obj, value):
864 try:
865 try:
865 return float(value)
866 return float(value)
866 except:
867 except:
867 self.error(obj, value)
868 self.error(obj, value)
868
869
869 class Complex(TraitType):
870 class Complex(TraitType):
870 """A trait for complex numbers."""
871 """A trait for complex numbers."""
871
872
872 evaluate = complex
873 evaluate = complex
873 default_value = 0.0 + 0.0j
874 default_value = 0.0 + 0.0j
874 info_text = 'a complex number'
875 info_text = 'a complex number'
875
876
876 def validate(self, obj, value):
877 def validate(self, obj, value):
877 if isinstance(value, complex):
878 if isinstance(value, complex):
878 return value
879 return value
879 if isinstance(value, (float, int)):
880 if isinstance(value, (float, int)):
880 return complex(value)
881 return complex(value)
881 self.error(obj, value)
882 self.error(obj, value)
882
883
883
884
884 class CComplex(Complex):
885 class CComplex(Complex):
885 """A casting version of the complex number trait."""
886 """A casting version of the complex number trait."""
886
887
887 def validate (self, obj, value):
888 def validate (self, obj, value):
888 try:
889 try:
889 return complex(value)
890 return complex(value)
890 except:
891 except:
891 self.error(obj, value)
892 self.error(obj, value)
892
893
893
894
894 class Str(TraitType):
895 class Str(TraitType):
895 """A trait for strings."""
896 """A trait for strings."""
896
897
897 evaluate = lambda x: x
898 evaluate = lambda x: x
898 default_value = ''
899 default_value = ''
899 info_text = 'a string'
900 info_text = 'a string'
900
901
901 def validate(self, obj, value):
902 def validate(self, obj, value):
902 if isinstance(value, str):
903 if isinstance(value, str):
903 return value
904 return value
904 self.error(obj, value)
905 self.error(obj, value)
905
906
906
907
907 class CStr(Str):
908 class CStr(Str):
908 """A casting version of the string trait."""
909 """A casting version of the string trait."""
909
910
910 def validate(self, obj, value):
911 def validate(self, obj, value):
911 try:
912 try:
912 return str(value)
913 return str(value)
913 except:
914 except:
914 try:
915 try:
915 return unicode(value)
916 return unicode(value)
916 except:
917 except:
917 self.error(obj, value)
918 self.error(obj, value)
918
919
919
920
920 class Unicode(TraitType):
921 class Unicode(TraitType):
921 """A trait for unicode strings."""
922 """A trait for unicode strings."""
922
923
923 evaluate = unicode
924 evaluate = unicode
924 default_value = u''
925 default_value = u''
925 info_text = 'a unicode string'
926 info_text = 'a unicode string'
926
927
927 def validate(self, obj, value):
928 def validate(self, obj, value):
928 if isinstance(value, unicode):
929 if isinstance(value, unicode):
929 return value
930 return value
930 if isinstance(value, str):
931 if isinstance(value, str):
931 return unicode(value)
932 return unicode(value)
932 self.error(obj, value)
933 self.error(obj, value)
933
934
934
935
935 class CUnicode(Unicode):
936 class CUnicode(Unicode):
936 """A casting version of the unicode trait."""
937 """A casting version of the unicode trait."""
937
938
938 def validate(self, obj, value):
939 def validate(self, obj, value):
939 try:
940 try:
940 return unicode(value)
941 return unicode(value)
941 except:
942 except:
942 self.error(obj, value)
943 self.error(obj, value)
943
944
944
945
945 class Bool(TraitType):
946 class Bool(TraitType):
946 """A boolean (True, False) trait."""
947 """A boolean (True, False) trait."""
947 evaluate = bool
948 evaluate = bool
948 default_value = False
949 default_value = False
949 info_text = 'a boolean'
950 info_text = 'a boolean'
950
951
951 def validate(self, obj, value):
952 def validate(self, obj, value):
952 if isinstance(value, bool):
953 if isinstance(value, bool):
953 return value
954 return value
954 self.error(obj, value)
955 self.error(obj, value)
955
956
956
957
957 class CBool(Bool):
958 class CBool(Bool):
958 """A casting version of the boolean trait."""
959 """A casting version of the boolean trait."""
959
960
960 def validate(self, obj, value):
961 def validate(self, obj, value):
961 try:
962 try:
962 return bool(value)
963 return bool(value)
963 except:
964 except:
964 self.error(obj, value)
965 self.error(obj, value)
965
966
966
967
967 class Enum(TraitType):
968 class Enum(TraitType):
968 """An enum that whose value must be in a given sequence."""
969 """An enum that whose value must be in a given sequence."""
969
970
970 def __init__(self, values, default_value=None, allow_none=True, **metadata):
971 def __init__(self, values, default_value=None, allow_none=True, **metadata):
971 self.values = values
972 self.values = values
972 self._allow_none = allow_none
973 self._allow_none = allow_none
973 super(Enum, self).__init__(default_value, **metadata)
974 super(Enum, self).__init__(default_value, **metadata)
974
975
975 def validate(self, obj, value):
976 def validate(self, obj, value):
976 if value is None:
977 if value is None:
977 if self._allow_none:
978 if self._allow_none:
978 return value
979 return value
979
980
980 if value in self.values:
981 if value in self.values:
981 return value
982 return value
982 self.error(obj, value)
983 self.error(obj, value)
983
984
984 def info(self):
985 def info(self):
985 """ Returns a description of the trait."""
986 """ Returns a description of the trait."""
986 result = 'any of ' + repr(self.values)
987 result = 'any of ' + repr(self.values)
987 if self._allow_none:
988 if self._allow_none:
988 return result + ' or None'
989 return result + ' or None'
989 return result
990 return result
990
991
991 class CaselessStrEnum(Enum):
992 class CaselessStrEnum(Enum):
992 """An enum of strings that are caseless in validate."""
993 """An enum of strings that are caseless in validate."""
993
994
994 def validate(self, obj, value):
995 def validate(self, obj, value):
995 if value is None:
996 if value is None:
996 if self._allow_none:
997 if self._allow_none:
997 return value
998 return value
998
999
999 if not isinstance(value, str):
1000 if not isinstance(value, str):
1000 self.error(obj, value)
1001 self.error(obj, value)
1001
1002
1002 for v in self.values:
1003 for v in self.values:
1003 if v.lower() == value.lower():
1004 if v.lower() == value.lower():
1004 return v
1005 return v
1005 self.error(obj, value)
1006 self.error(obj, value)
1006
1007
1007
1008
1008 class List(Instance):
1009 class List(Instance):
1009 """An instance of a Python list."""
1010 """An instance of a Python list."""
1010
1011
1011 def __init__(self, default_value=None, allow_none=True, **metadata):
1012 def __init__(self, default_value=None, allow_none=True, **metadata):
1012 """Create a list trait type from a list or tuple.
1013 """Create a list trait type from a list or tuple.
1013
1014
1014 The default value is created by doing ``list(default_value)``,
1015 The default value is created by doing ``list(default_value)``,
1015 which creates a copy of the ``default_value``.
1016 which creates a copy of the ``default_value``.
1016 """
1017 """
1017 if default_value is None:
1018 if default_value is None:
1018 args = ((),)
1019 args = ((),)
1019 elif isinstance(default_value, SequenceTypes):
1020 elif isinstance(default_value, SequenceTypes):
1020 args = (default_value,)
1021 args = (default_value,)
1021 else:
1022 else:
1022 raise TypeError('default value of List was %s' % default_value)
1023 raise TypeError('default value of List was %s' % default_value)
1023
1024
1024 super(List,self).__init__(klass=list, args=args,
1025 super(List,self).__init__(klass=list, args=args,
1025 allow_none=allow_none, **metadata)
1026 allow_none=allow_none, **metadata)
1026
1027
1027
1028
1028 class Dict(Instance):
1029 class Dict(Instance):
1029 """An instance of a Python dict."""
1030 """An instance of a Python dict."""
1030
1031
1031 def __init__(self, default_value=None, allow_none=True, **metadata):
1032 def __init__(self, default_value=None, allow_none=True, **metadata):
1032 """Create a dict trait type from a dict.
1033 """Create a dict trait type from a dict.
1033
1034
1034 The default value is created by doing ``dict(default_value)``,
1035 The default value is created by doing ``dict(default_value)``,
1035 which creates a copy of the ``default_value``.
1036 which creates a copy of the ``default_value``.
1036 """
1037 """
1037 if default_value is None:
1038 if default_value is None:
1038 args = ((),)
1039 args = ((),)
1039 elif isinstance(default_value, dict):
1040 elif isinstance(default_value, dict):
1040 args = (default_value,)
1041 args = (default_value,)
1041 elif isinstance(default_value, SequenceTypes):
1042 elif isinstance(default_value, SequenceTypes):
1042 args = (default_value,)
1043 args = (default_value,)
1043 else:
1044 else:
1044 raise TypeError('default value of Dict was %s' % default_value)
1045 raise TypeError('default value of Dict was %s' % default_value)
1045
1046
1046 super(Dict,self).__init__(klass=dict, args=args,
1047 super(Dict,self).__init__(klass=dict, args=args,
1047 allow_none=allow_none, **metadata)
1048 allow_none=allow_none, **metadata)
General Comments 0
You need to be logged in to leave comments. Login now