##// END OF EJS Templates
Added casting versions of all the basic traitlet types.
Brian Granger -
Show More
@@ -1,752 +1,828
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
19 * A full set of trait types
20 * API compatibility with enthought.traits
20 * API compatibility with enthought.traits
21
21
22 We choose to create this module because we need these capabilities, but
22 We choose to create this module because we need these capabilities, but
23 we need them to be pure Python so they work in all Python implementations,
23 we need them to be pure Python so they work in all Python implementations,
24 including Jython and IronPython.
24 including Jython and IronPython.
25
25
26 Authors:
26 Authors:
27
27
28 * Brian Granger
28 * Brian Granger
29 * Enthought, Inc. Some of the code in this file comes from enthought.traits
29 * Enthought, Inc. Some of the code in this file comes from enthought.traits
30 and is licensed under the BSD license. Also, many of the ideas also come
30 and is licensed under the BSD license. Also, many of the ideas also come
31 from enthought.traits even though our implementation is very different.
31 from enthought.traits even though our implementation is very different.
32 """
32 """
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Copyright (C) 2008-2009 The IPython Development Team
35 # Copyright (C) 2008-2009 The IPython Development Team
36 #
36 #
37 # Distributed under the terms of the BSD License. The full license is in
37 # Distributed under the terms of the BSD License. The full license is in
38 # the file COPYING, distributed as part of this software.
38 # the file COPYING, distributed as part of this software.
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # Imports
42 # Imports
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44
44
45
45
46 import inspect
46 import inspect
47 import sys
47 import sys
48 import types
48 import types
49 from types import InstanceType, ClassType
49 from types import InstanceType, ClassType
50
50
51 ClassTypes = (ClassType, type)
51 ClassTypes = (ClassType, type)
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Basic classes
54 # Basic classes
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57
57
58 class NoDefaultSpecified ( object ): pass
58 class NoDefaultSpecified ( object ): pass
59 NoDefaultSpecified = NoDefaultSpecified()
59 NoDefaultSpecified = NoDefaultSpecified()
60
60
61
61
62 class Undefined ( object ): pass
62 class Undefined ( object ): pass
63 Undefined = Undefined()
63 Undefined = Undefined()
64
64
65
65
66 class TraitletError(Exception):
66 class TraitletError(Exception):
67 pass
67 pass
68
68
69
69
70 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
71 # Utilities
71 # Utilities
72 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
73
73
74
74
75 def class_of ( object ):
75 def class_of ( object ):
76 """ Returns a string containing the class name of an object with the
76 """ Returns a string containing the class name of an object with the
77 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
77 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
78 'a PlotValue').
78 'a PlotValue').
79 """
79 """
80 if isinstance( object, basestring ):
80 if isinstance( object, basestring ):
81 return add_article( object )
81 return add_article( object )
82
82
83 return add_article( object.__class__.__name__ )
83 return add_article( object.__class__.__name__ )
84
84
85
85
86 def add_article ( name ):
86 def add_article ( name ):
87 """ Returns a string containing the correct indefinite article ('a' or 'an')
87 """ Returns a string containing the correct indefinite article ('a' or 'an')
88 prefixed to the specified string.
88 prefixed to the specified string.
89 """
89 """
90 if name[:1].lower() in 'aeiou':
90 if name[:1].lower() in 'aeiou':
91 return 'an ' + name
91 return 'an ' + name
92
92
93 return 'a ' + name
93 return 'a ' + name
94
94
95
95
96 def repr_type(obj):
96 def repr_type(obj):
97 """ Return a string representation of a value and its type for readable
97 """ Return a string representation of a value and its type for readable
98 error messages.
98 error messages.
99 """
99 """
100 the_type = type(obj)
100 the_type = type(obj)
101 if the_type is InstanceType:
101 if the_type is InstanceType:
102 # Old-style class.
102 # Old-style class.
103 the_type = obj.__class__
103 the_type = obj.__class__
104 msg = '%r %r' % (obj, the_type)
104 msg = '%r %r' % (obj, the_type)
105 return msg
105 return msg
106
106
107
107
108 def parse_notifier_name(name):
108 def parse_notifier_name(name):
109 """Convert the name argument to a list of names.
109 """Convert the name argument to a list of names.
110
110
111 Examples
111 Examples
112 --------
112 --------
113
113
114 >>> parse_notifier_name('a')
114 >>> parse_notifier_name('a')
115 ['a']
115 ['a']
116 >>> parse_notifier_name(['a','b'])
116 >>> parse_notifier_name(['a','b'])
117 ['a', 'b']
117 ['a', 'b']
118 >>> parse_notifier_name(None)
118 >>> parse_notifier_name(None)
119 ['anytraitlet']
119 ['anytraitlet']
120 """
120 """
121 if isinstance(name, str):
121 if isinstance(name, str):
122 return [name]
122 return [name]
123 elif name is None:
123 elif name is None:
124 return ['anytraitlet']
124 return ['anytraitlet']
125 elif isinstance(name, (list, tuple)):
125 elif isinstance(name, (list, tuple)):
126 for n in name:
126 for n in name:
127 assert isinstance(n, str), "names must be strings"
127 assert isinstance(n, str), "names must be strings"
128 return name
128 return name
129
129
130
130
131 def get_module_name ( level = 2 ):
131 def get_module_name ( level = 2 ):
132 """ Returns the name of the module that the caller's caller is located in.
132 """ Returns the name of the module that the caller's caller is located in.
133 """
133 """
134 return sys._getframe( level ).f_globals.get( '__name__', '__main__' )
134 return sys._getframe( level ).f_globals.get( '__name__', '__main__' )
135
135
136
136
137 #-----------------------------------------------------------------------------
137 #-----------------------------------------------------------------------------
138 # Base TraitletType for all traitlets
138 # Base TraitletType for all traitlets
139 #-----------------------------------------------------------------------------
139 #-----------------------------------------------------------------------------
140
140
141
141
142 class TraitletType(object):
142 class TraitletType(object):
143
143
144 metadata = {}
144 metadata = {}
145 default_value = Undefined
145 default_value = Undefined
146 info_text = 'any value'
146 info_text = 'any value'
147
147
148 def __init__(self, default_value=NoDefaultSpecified, **metadata):
148 def __init__(self, default_value=NoDefaultSpecified, **metadata):
149 """Create a TraitletType.
149 """Create a TraitletType.
150 """
150 """
151 if default_value is not NoDefaultSpecified:
151 if default_value is not NoDefaultSpecified:
152 self.default_value = default_value
152 self.default_value = default_value
153 self.metadata.update(metadata)
153 self.metadata.update(metadata)
154 self.init()
154 self.init()
155
155
156 def init(self):
156 def init(self):
157 pass
157 pass
158
158
159 def get_default_value(self):
159 def get_default_value(self):
160 """Create a new instance of the default value."""
160 """Create a new instance of the default value."""
161 dv = self.default_value
161 dv = self.default_value
162 return dv
162 return dv
163
163
164 def __get__(self, obj, cls=None):
164 def __get__(self, obj, cls=None):
165 """Get the value of the traitlet by self.name for the instance.
165 """Get the value of the traitlet by self.name for the instance.
166
166
167 The creation of default values is deferred until this is called the
167 The creation of default values is deferred until this is called the
168 first time. This is done so instances of the parent HasTraitlets
168 first time. This is done so instances of the parent HasTraitlets
169 will have their own default value instances.
169 will have their own default value instances.
170 """
170 """
171 if obj is None:
171 if obj is None:
172 return self
172 return self
173 else:
173 else:
174 if not obj._traitlet_values.has_key(self.name):
174 if not obj._traitlet_values.has_key(self.name):
175 dv = self.get_default_value()
175 dv = self.get_default_value()
176 self.__set__(obj, dv, first=True)
176 self.__set__(obj, dv, first=True)
177 return dv
177 return dv
178 else:
178 else:
179 return obj._traitlet_values[self.name]
179 return obj._traitlet_values[self.name]
180
180
181 def __set__(self, obj, value, first=False):
181 def __set__(self, obj, value, first=False):
182 new_value = self._validate(obj, value)
182 new_value = self._validate(obj, value)
183 if not first:
183 if not first:
184 old_value = self.__get__(obj)
184 old_value = self.__get__(obj)
185 if old_value != new_value:
185 if old_value != new_value:
186 obj._traitlet_values[self.name] = new_value
186 obj._traitlet_values[self.name] = new_value
187 obj._notify(self.name, old_value, new_value)
187 obj._notify(self.name, old_value, new_value)
188 else:
188 else:
189 obj._traitlet_values[self.name] = new_value
189 obj._traitlet_values[self.name] = new_value
190
190
191 def _validate(self, obj, value):
191 def _validate(self, obj, value):
192 if hasattr(self, 'validate'):
192 if hasattr(self, 'validate'):
193 return self.validate(obj, value)
193 return self.validate(obj, value)
194 elif hasattr(self, 'is_valid_for'):
194 elif hasattr(self, 'is_valid_for'):
195 valid = self.is_valid_for(value)
195 valid = self.is_valid_for(value)
196 if valid:
196 if valid:
197 return value
197 return value
198 else:
198 else:
199 raise TraitletError('invalid value for type: %r' % value)
199 raise TraitletError('invalid value for type: %r' % value)
200 elif hasattr(self, 'value_for'):
200 elif hasattr(self, 'value_for'):
201 return self.value_for(value)
201 return self.value_for(value)
202 else:
202 else:
203 return value
203 return value
204
204
205 def info(self):
205 def info(self):
206 return self.info_text
206 return self.info_text
207
207
208 def error(self, obj, value):
208 def error(self, obj, value):
209 if obj is not None:
209 if obj is not None:
210 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
210 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
211 % (self.name, class_of(obj),
211 % (self.name, class_of(obj),
212 self.info(), repr_type(value))
212 self.info(), repr_type(value))
213 else:
213 else:
214 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
214 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
215 % (self.name, self.info(), repr_type(value))
215 % (self.name, self.info(), repr_type(value))
216 raise TraitletError(e)
216 raise TraitletError(e)
217
217
218
218
219 #-----------------------------------------------------------------------------
219 #-----------------------------------------------------------------------------
220 # The HasTraitlets implementation
220 # The HasTraitlets implementation
221 #-----------------------------------------------------------------------------
221 #-----------------------------------------------------------------------------
222
222
223
223
224 class MetaHasTraitlets(type):
224 class MetaHasTraitlets(type):
225 """A metaclass for HasTraitlets.
225 """A metaclass for HasTraitlets.
226
226
227 This metaclass makes sure that any TraitletType class attributes are
227 This metaclass makes sure that any TraitletType class attributes are
228 instantiated and sets their name attribute.
228 instantiated and sets their name attribute.
229 """
229 """
230
230
231 def __new__(mcls, name, bases, classdict):
231 def __new__(mcls, name, bases, classdict):
232 for k,v in classdict.iteritems():
232 for k,v in classdict.iteritems():
233 if isinstance(v, TraitletType):
233 if isinstance(v, TraitletType):
234 v.name = k
234 v.name = k
235 elif inspect.isclass(v):
235 elif inspect.isclass(v):
236 if issubclass(v, TraitletType):
236 if issubclass(v, TraitletType):
237 vinst = v()
237 vinst = v()
238 vinst.name = k
238 vinst.name = k
239 classdict[k] = vinst
239 classdict[k] = vinst
240 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
240 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
241
241
242
242
243 class HasTraitlets(object):
243 class HasTraitlets(object):
244
244
245 __metaclass__ = MetaHasTraitlets
245 __metaclass__ = MetaHasTraitlets
246
246
247 def __init__(self):
247 def __init__(self):
248 self._traitlet_values = {}
248 self._traitlet_values = {}
249 self._traitlet_notifiers = {}
249 self._traitlet_notifiers = {}
250
250
251 def _notify(self, name, old_value, new_value):
251 def _notify(self, name, old_value, new_value):
252
252
253 # First dynamic ones
253 # First dynamic ones
254 callables = self._traitlet_notifiers.get(name,[])
254 callables = self._traitlet_notifiers.get(name,[])
255 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
255 more_callables = self._traitlet_notifiers.get('anytraitlet',[])
256 callables.extend(more_callables)
256 callables.extend(more_callables)
257
257
258 # Now static ones
258 # Now static ones
259 try:
259 try:
260 cb = getattr(self, '_%s_changed' % name)
260 cb = getattr(self, '_%s_changed' % name)
261 except:
261 except:
262 pass
262 pass
263 else:
263 else:
264 callables.append(cb)
264 callables.append(cb)
265
265
266 # Call them all now
266 # Call them all now
267 for c in callables:
267 for c in callables:
268 # Traits catches and logs errors here. I allow them to raise
268 # Traits catches and logs errors here. I allow them to raise
269 if callable(c):
269 if callable(c):
270 argspec = inspect.getargspec(c)
270 argspec = inspect.getargspec(c)
271 nargs = len(argspec[0])
271 nargs = len(argspec[0])
272 # Bound methods have an additional 'self' argument
272 # Bound methods have an additional 'self' argument
273 # I don't know how to treat unbound methods, but they
273 # I don't know how to treat unbound methods, but they
274 # can't really be used for callbacks.
274 # can't really be used for callbacks.
275 if isinstance(c, types.MethodType):
275 if isinstance(c, types.MethodType):
276 offset = -1
276 offset = -1
277 else:
277 else:
278 offset = 0
278 offset = 0
279 if nargs + offset == 0:
279 if nargs + offset == 0:
280 c()
280 c()
281 elif nargs + offset == 1:
281 elif nargs + offset == 1:
282 c(name)
282 c(name)
283 elif nargs + offset == 2:
283 elif nargs + offset == 2:
284 c(name, new_value)
284 c(name, new_value)
285 elif nargs + offset == 3:
285 elif nargs + offset == 3:
286 c(name, old_value, new_value)
286 c(name, old_value, new_value)
287 else:
287 else:
288 raise TraitletError('a traitlet changed callback '
288 raise TraitletError('a traitlet changed callback '
289 'must have 0-3 arguments.')
289 'must have 0-3 arguments.')
290 else:
290 else:
291 raise TraitletError('a traitlet changed callback '
291 raise TraitletError('a traitlet changed callback '
292 'must be callable.')
292 'must be callable.')
293
293
294
294
295 def _add_notifiers(self, handler, name):
295 def _add_notifiers(self, handler, name):
296 if not self._traitlet_notifiers.has_key(name):
296 if not self._traitlet_notifiers.has_key(name):
297 nlist = []
297 nlist = []
298 self._traitlet_notifiers[name] = nlist
298 self._traitlet_notifiers[name] = nlist
299 else:
299 else:
300 nlist = self._traitlet_notifiers[name]
300 nlist = self._traitlet_notifiers[name]
301 if handler not in nlist:
301 if handler not in nlist:
302 nlist.append(handler)
302 nlist.append(handler)
303
303
304 def _remove_notifiers(self, handler, name):
304 def _remove_notifiers(self, handler, name):
305 if self._traitlet_notifiers.has_key(name):
305 if self._traitlet_notifiers.has_key(name):
306 nlist = self._traitlet_notifiers[name]
306 nlist = self._traitlet_notifiers[name]
307 try:
307 try:
308 index = nlist.index(handler)
308 index = nlist.index(handler)
309 except ValueError:
309 except ValueError:
310 pass
310 pass
311 else:
311 else:
312 del nlist[index]
312 del nlist[index]
313
313
314 def on_traitlet_change(self, handler, name=None, remove=False):
314 def on_traitlet_change(self, handler, name=None, remove=False):
315 """Setup a handler to be called when a traitlet changes.
315 """Setup a handler to be called when a traitlet changes.
316
316
317 This is used to setup dynamic notifications of traitlet changes.
317 This is used to setup dynamic notifications of traitlet changes.
318
318
319 Static handlers can be created by creating methods on a HasTraitlets
319 Static handlers can be created by creating methods on a HasTraitlets
320 subclass with the naming convention '_[traitletname]_changed'. Thus,
320 subclass with the naming convention '_[traitletname]_changed'. Thus,
321 to create static handler for the traitlet 'a', create the method
321 to create static handler for the traitlet 'a', create the method
322 _a_changed(self, name, old, new) (fewer arguments can be used, see
322 _a_changed(self, name, old, new) (fewer arguments can be used, see
323 below).
323 below).
324
324
325 Parameters
325 Parameters
326 ----------
326 ----------
327 handler : callable
327 handler : callable
328 A callable that is called when a traitlet changes. Its
328 A callable that is called when a traitlet changes. Its
329 signature can be handler(), handler(name), handler(name, new)
329 signature can be handler(), handler(name), handler(name, new)
330 or handler(name, old, new).
330 or handler(name, old, new).
331 name : list, str, None
331 name : list, str, None
332 If None, the handler will apply to all traitlets. If a list
332 If None, the handler will apply to all traitlets. If a list
333 of str, handler will apply to all names in the list. If a
333 of str, handler will apply to all names in the list. If a
334 str, the handler will apply just to that name.
334 str, the handler will apply just to that name.
335 remove : bool
335 remove : bool
336 If False (the default), then install the handler. If True
336 If False (the default), then install the handler. If True
337 then unintall it.
337 then unintall it.
338 """
338 """
339 if remove:
339 if remove:
340 names = parse_notifier_name(name)
340 names = parse_notifier_name(name)
341 for n in names:
341 for n in names:
342 self._remove_notifiers(handler, n)
342 self._remove_notifiers(handler, n)
343 else:
343 else:
344 names = parse_notifier_name(name)
344 names = parse_notifier_name(name)
345 for n in names:
345 for n in names:
346 self._add_notifiers(handler, n)
346 self._add_notifiers(handler, n)
347
347
348 def _add_class_traitlet(self, name, traitlet):
348 def _add_class_traitlet(self, name, traitlet):
349 """Add a class-level traitlet.
349 """Add a class-level traitlet.
350
350
351 This create a new traitlet attached to all instances of this class.
351 This create a new traitlet attached to all instances of this class.
352 But, the value can be different on each instance. But, this behavior
352 But, the value can be different on each instance. But, this behavior
353 is likely to trip up many folks as they would expect the traitlet
353 is likely to trip up many folks as they would expect the traitlet
354 type to be different on each instance.
354 type to be different on each instance.
355
355
356 Parameters
356 Parameters
357 ----------
357 ----------
358 name : str
358 name : str
359 The name of the traitlet.
359 The name of the traitlet.
360 traitlet : TraitletType or an instance of one
360 traitlet : TraitletType or an instance of one
361 The traitlet to assign to the name.
361 The traitlet to assign to the name.
362 """
362 """
363 if inspect.isclass(traitlet):
363 if inspect.isclass(traitlet):
364 inst = traitlet()
364 inst = traitlet()
365 else:
365 else:
366 inst = traitlet
366 inst = traitlet
367 assert isinstance(inst, TraitletType)
367 assert isinstance(inst, TraitletType)
368 inst.name = name
368 inst.name = name
369 setattr(self.__class__, name, inst)
369 setattr(self.__class__, name, inst)
370
370
371
371
372 #-----------------------------------------------------------------------------
372 #-----------------------------------------------------------------------------
373 # Actual TraitletTypes implementations/subclasses
373 # Actual TraitletTypes implementations/subclasses
374 #-----------------------------------------------------------------------------
374 #-----------------------------------------------------------------------------
375
375
376 #-----------------------------------------------------------------------------
376 #-----------------------------------------------------------------------------
377 # TraitletTypes subclasses for handling classes and instances of classes
377 # TraitletTypes subclasses for handling classes and instances of classes
378 #-----------------------------------------------------------------------------
378 #-----------------------------------------------------------------------------
379
379
380
380
381 class BaseClassResolver(TraitletType):
381 class BaseClassResolver(TraitletType):
382 """Mixin class for traitlets that need to resolve classes by strings.
382 """Mixin class for traitlets that need to resolve classes by strings.
383
383
384 This class provides is a mixin that provides its subclasses with the
384 This class provides is a mixin that provides its subclasses with the
385 ability to resolve classes by specifying a string name (for example,
385 ability to resolve classes by specifying a string name (for example,
386 'foo.bar.MyClass'). An actual class can also be resolved.
386 'foo.bar.MyClass'). An actual class can also be resolved.
387
387
388 Any subclass must define instances with 'klass' and 'module' attributes
388 Any subclass must define instances with 'klass' and 'module' attributes
389 that contain the string name of the class (or actual class object) and
389 that contain the string name of the class (or actual class object) and
390 the module name that contained the original trait definition (used for
390 the module name that contained the original trait definition (used for
391 resolving local class names (e.g. 'LocalClass')).
391 resolving local class names (e.g. 'LocalClass')).
392 """
392 """
393
393
394 def resolve_class(self, obj, value):
394 def resolve_class(self, obj, value):
395 klass = self.validate_class(self.find_class(self.klass))
395 klass = self.validate_class(self.find_class(self.klass))
396 if klass is None:
396 if klass is None:
397 self.validate_failed(obj, value)
397 self.validate_failed(obj, value)
398
398
399 self.klass = klass
399 self.klass = klass
400
400
401 def validate_class(self, klass):
401 def validate_class(self, klass):
402 return klass
402 return klass
403
403
404 def find_class(self, klass):
404 def find_class(self, klass):
405 module = self.module
405 module = self.module
406 col = klass.rfind('.')
406 col = klass.rfind('.')
407 if col >= 0:
407 if col >= 0:
408 module = klass[ : col ]
408 module = klass[ : col ]
409 klass = klass[ col + 1: ]
409 klass = klass[ col + 1: ]
410
410
411 theClass = getattr(sys.modules.get(module), klass, None)
411 theClass = getattr(sys.modules.get(module), klass, None)
412 if (theClass is None) and (col >= 0):
412 if (theClass is None) and (col >= 0):
413 try:
413 try:
414 mod = __import__(module)
414 mod = __import__(module)
415 for component in module.split( '.' )[1:]:
415 for component in module.split( '.' )[1:]:
416 mod = getattr(mod, component)
416 mod = getattr(mod, component)
417
417
418 theClass = getattr(mod, klass, None)
418 theClass = getattr(mod, klass, None)
419 except:
419 except:
420 pass
420 pass
421
421
422 return theClass
422 return theClass
423
423
424 def validate_failed (self, obj, value):
424 def validate_failed (self, obj, value):
425 kind = type(value)
425 kind = type(value)
426 if kind is InstanceType:
426 if kind is InstanceType:
427 msg = 'class %s' % value.__class__.__name__
427 msg = 'class %s' % value.__class__.__name__
428 else:
428 else:
429 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
429 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
430
430
431 self.error(obj, msg)
431 self.error(obj, msg)
432
432
433
433
434 class Type(BaseClassResolver):
434 class Type(BaseClassResolver):
435 """A traitlet whose value must be a subclass of a specified class."""
435 """A traitlet whose value must be a subclass of a specified class."""
436
436
437 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
437 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
438 """Construct a Type traitlet
438 """Construct a Type traitlet
439
439
440 A Type traitlet specifies that its values must be subclasses of
440 A Type traitlet specifies that its values must be subclasses of
441 a particular class.
441 a particular class.
442
442
443 Parameters
443 Parameters
444 ----------
444 ----------
445 default_value : class or None
445 default_value : class or None
446 The default value must be a subclass of klass.
446 The default value must be a subclass of klass.
447 klass : class, str, None
447 klass : class, str, None
448 Values of this traitlet must be a subclass of klass. The klass
448 Values of this traitlet must be a subclass of klass. The klass
449 may be specified in a string like: 'foo.bar.MyClass'.
449 may be specified in a string like: 'foo.bar.MyClass'.
450 allow_none : boolean
450 allow_none : boolean
451 Indicates whether None is allowed as an assignable value. Even if
451 Indicates whether None is allowed as an assignable value. Even if
452 ``False``, the default value may be ``None``.
452 ``False``, the default value may be ``None``.
453 """
453 """
454 if default_value is None:
454 if default_value is None:
455 if klass is None:
455 if klass is None:
456 klass = object
456 klass = object
457 elif klass is None:
457 elif klass is None:
458 klass = default_value
458 klass = default_value
459
459
460 if isinstance(klass, basestring):
460 if isinstance(klass, basestring):
461 self.validate = self.resolve
461 self.validate = self.resolve
462 elif not isinstance(klass, ClassTypes):
462 elif not isinstance(klass, ClassTypes):
463 raise TraitletError("A Type traitlet must specify a class.")
463 raise TraitletError("A Type traitlet must specify a class.")
464
464
465 self.klass = klass
465 self.klass = klass
466 self._allow_none = allow_none
466 self._allow_none = allow_none
467 self.module = get_module_name()
467 self.module = get_module_name()
468
468
469 super(Type, self).__init__(default_value, **metadata)
469 super(Type, self).__init__(default_value, **metadata)
470
470
471 def validate(self, obj, value):
471 def validate(self, obj, value):
472 """Validates that the value is a valid object instance."""
472 """Validates that the value is a valid object instance."""
473 try:
473 try:
474 if issubclass(value, self.klass):
474 if issubclass(value, self.klass):
475 return value
475 return value
476 except:
476 except:
477 if (value is None) and (self._allow_none):
477 if (value is None) and (self._allow_none):
478 return value
478 return value
479
479
480 self.error(obj, value)
480 self.error(obj, value)
481
481
482 def resolve(self, obj, name, value):
482 def resolve(self, obj, name, value):
483 """ Resolves a class originally specified as a string into an actual
483 """ Resolves a class originally specified as a string into an actual
484 class, then resets the trait so that future calls will be handled by
484 class, then resets the trait so that future calls will be handled by
485 the normal validate method.
485 the normal validate method.
486 """
486 """
487 if isinstance(self.klass, basestring):
487 if isinstance(self.klass, basestring):
488 self.resolve_class(obj, value)
488 self.resolve_class(obj, value)
489 del self.validate
489 del self.validate
490
490
491 return self.validate(obj, value)
491 return self.validate(obj, value)
492
492
493 def info(self):
493 def info(self):
494 """ Returns a description of the trait."""
494 """ Returns a description of the trait."""
495 klass = self.klass
495 klass = self.klass
496 if not isinstance(klass, basestring):
496 if not isinstance(klass, basestring):
497 klass = klass.__name__
497 klass = klass.__name__
498
498
499 result = 'a subclass of ' + klass
499 result = 'a subclass of ' + klass
500
500
501 if self._allow_none:
501 if self._allow_none:
502 return result + ' or None'
502 return result + ' or None'
503
503
504 return result
504 return result
505
505
506 def get_default_value(self):
506 def get_default_value(self):
507 """ Returns a tuple of the form: ( default_value_type, default_value )
507 """ Returns a tuple of the form: ( default_value_type, default_value )
508 which describes the default value for this trait.
508 which describes the default value for this trait.
509 """
509 """
510 if not isinstance(self.default_value, basestring):
510 if not isinstance(self.default_value, basestring):
511 return super(Type, self).get_default_value()
511 return super(Type, self).get_default_value()
512
512
513 dv = self.resolve_default_value()
513 dv = self.resolve_default_value()
514 dvt = type(dv)
514 dvt = type(dv)
515 return (dvt, dv)
515 return (dvt, dv)
516
516
517 def resolve_default_value(self):
517 def resolve_default_value(self):
518 """ Resolves a class name into a class so that it can be used to
518 """ Resolves a class name into a class so that it can be used to
519 return the class as the default value of the trait.
519 return the class as the default value of the trait.
520 """
520 """
521 if isinstance(self.klass, basestring):
521 if isinstance(self.klass, basestring):
522 try:
522 try:
523 self.resolve_class(None, None)
523 self.resolve_class(None, None)
524 del self.validate
524 del self.validate
525 except:
525 except:
526 raise TraitletError('Could not resolve %s into a valid class' %
526 raise TraitletError('Could not resolve %s into a valid class' %
527 self.klass )
527 self.klass )
528
528
529 return self.klass
529 return self.klass
530
530
531
531
532 class DefaultValueGenerator(object):
532 class DefaultValueGenerator(object):
533 """A class for generating new default value instances."""
533 """A class for generating new default value instances."""
534
534
535 def __init__(self, klass, *args, **kw):
535 def __init__(self, klass, *args, **kw):
536 self.klass = klass
536 self.klass = klass
537 self.args = args
537 self.args = args
538 self.kw = kw
538 self.kw = kw
539
539
540
540
541 class Instance(BaseClassResolver):
541 class Instance(BaseClassResolver):
542 """A trait whose value must be an instance of a specified class.
542 """A trait whose value must be an instance of a specified class.
543
543
544 The value can also be an instance of a subclass of the specified class.
544 The value can also be an instance of a subclass of the specified class.
545 """
545 """
546
546
547 def __init__(self, klass=None, args=None, kw=None, allow_none=True,
547 def __init__(self, klass=None, args=None, kw=None, allow_none=True,
548 module = None, **metadata ):
548 module = None, **metadata ):
549 """Construct an Instance traitlet.
549 """Construct an Instance traitlet.
550
550
551 Parameters
551 Parameters
552 ----------
552 ----------
553 klass : class or instance
553 klass : class or instance
554 The object that forms the basis for the traitlet. If an instance
554 The object that forms the basis for the traitlet. If an instance
555 values must have isinstance(value, type(instance)).
555 values must have isinstance(value, type(instance)).
556 args : tuple
556 args : tuple
557 Positional arguments for generating the default value.
557 Positional arguments for generating the default value.
558 kw : dict
558 kw : dict
559 Keyword arguments for generating the default value.
559 Keyword arguments for generating the default value.
560 allow_none : bool
560 allow_none : bool
561 Indicates whether None is allowed as a value.
561 Indicates whether None is allowed as a value.
562
562
563 Default Value
563 Default Value
564 -------------
564 -------------
565 If klass is an instance, default value is None. If klass is a class
565 If klass is an instance, default value is None. If klass is a class
566 then the default value is obtained by calling ``klass(*args, **kw)``.
566 then the default value is obtained by calling ``klass(*args, **kw)``.
567 If klass is a str, it is first resolved to an actual class and then
567 If klass is a str, it is first resolved to an actual class and then
568 instantiated with ``klass(*args, **kw)``.
568 instantiated with ``klass(*args, **kw)``.
569 """
569 """
570
570
571 self._allow_none = allow_none
571 self._allow_none = allow_none
572 self.module = module or get_module_name()
572 self.module = module or get_module_name()
573
573
574 if klass is None:
574 if klass is None:
575 raise TraitletError('A %s traitlet must have a class specified.' %
575 raise TraitletError('A %s traitlet must have a class specified.' %
576 self.__class__.__name__ )
576 self.__class__.__name__ )
577 elif not isinstance(klass, (basestring,) + ClassTypes ):
577 elif not isinstance(klass, (basestring,) + ClassTypes ):
578 # klass is an instance so default value will be None
578 # klass is an instance so default value will be None
579 self.klass = klass.__class__
579 self.klass = klass.__class__
580 default_value = None
580 default_value = None
581 else:
581 else:
582 # klass is a str or class so we handle args, kw
582 # klass is a str or class so we handle args, kw
583 if args is None:
583 if args is None:
584 args = ()
584 args = ()
585 if kw is None:
585 if kw is None:
586 if isinstance(args, dict):
586 if isinstance(args, dict):
587 kw = args
587 kw = args
588 args = ()
588 args = ()
589 else:
589 else:
590 kw = {}
590 kw = {}
591 if not isinstance(kw, dict):
591 if not isinstance(kw, dict):
592 raise TraitletError("The 'kw' argument must be a dict.")
592 raise TraitletError("The 'kw' argument must be a dict.")
593 if not isinstance(args, tuple):
593 if not isinstance(args, tuple):
594 raise TraitletError("The 'args' argument must be a tuple.")
594 raise TraitletError("The 'args' argument must be a tuple.")
595 self.klass = klass
595 self.klass = klass
596 # This tells my get_default_value that the default value
596 # This tells my get_default_value that the default value
597 # instance needs to be generated when it is called. This
597 # instance needs to be generated when it is called. This
598 # is usually when TraitletType.__get__ is called for the 1st time.
598 # is usually when TraitletType.__get__ is called for the 1st time.
599
599
600 default_value = DefaultValueGenerator(klass, *args, **kw)
600 default_value = DefaultValueGenerator(klass, *args, **kw)
601
601
602 super(Instance, self).__init__(default_value, **metadata)
602 super(Instance, self).__init__(default_value, **metadata)
603
603
604 def validate(self, obj, value):
604 def validate(self, obj, value):
605 if value is None:
605 if value is None:
606 if self._allow_none:
606 if self._allow_none:
607 return value
607 return value
608 self.validate_failed(obj, value)
608 self.validate_failed(obj, value)
609
609
610 # This is where self.klass is turned into a real class if it was
610 # This is where self.klass is turned into a real class if it was
611 # a str initially. This happens the first time TraitletType.__set__
611 # a str initially. This happens the first time TraitletType.__set__
612 # is called. This does happen if a default value is generated by
612 # is called. This does happen if a default value is generated by
613 # TraitletType.__get__.
613 # TraitletType.__get__.
614 if isinstance(self.klass, basestring):
614 if isinstance(self.klass, basestring):
615 self.resolve_class(obj, value)
615 self.resolve_class(obj, value)
616
616
617 if isinstance(value, self.klass):
617 if isinstance(value, self.klass):
618 return value
618 return value
619 else:
619 else:
620 self.validate_failed(obj, value)
620 self.validate_failed(obj, value)
621
621
622 def info ( self ):
622 def info ( self ):
623 klass = self.klass
623 klass = self.klass
624 if not isinstance( klass, basestring ):
624 if not isinstance( klass, basestring ):
625 klass = klass.__name__
625 klass = klass.__name__
626 result = class_of(klass)
626 result = class_of(klass)
627 if self._allow_none:
627 if self._allow_none:
628 return result + ' or None'
628 return result + ' or None'
629
629
630 return result
630 return result
631
631
632 def get_default_value ( self ):
632 def get_default_value ( self ):
633 """Instantiate a default value instance.
633 """Instantiate a default value instance.
634
634
635 When TraitletType.__get__ is called the first time, this is called
635 When TraitletType.__get__ is called the first time, this is called
636 (if no value has been assigned) to get a default value instance.
636 (if no value has been assigned) to get a default value instance.
637 """
637 """
638 dv = self.default_value
638 dv = self.default_value
639 if isinstance(dv, DefaultValueGenerator):
639 if isinstance(dv, DefaultValueGenerator):
640 klass = dv.klass
640 klass = dv.klass
641 args = dv.args
641 args = dv.args
642 kw = dv.kw
642 kw = dv.kw
643 if isinstance(klass, basestring):
643 if isinstance(klass, basestring):
644 klass = self.validate_class(self.find_class(klass))
644 klass = self.validate_class(self.find_class(klass))
645 if klass is None:
645 if klass is None:
646 raise TraitletError('Unable to locate class: ' + dv.klass)
646 raise TraitletError('Unable to locate class: ' + dv.klass)
647 return klass(*args, **kw)
647 return klass(*args, **kw)
648 else:
648 else:
649 return dv
649 return dv
650
650
651
651
652 #-----------------------------------------------------------------------------
652 #-----------------------------------------------------------------------------
653 # Basic TraitletTypes implementations/subclasses
653 # Basic TraitletTypes implementations/subclasses
654 #-----------------------------------------------------------------------------
654 #-----------------------------------------------------------------------------
655
655
656
656
657 class Any(TraitletType):
657 class Any(TraitletType):
658 default_value = None
658 default_value = None
659 info_text = 'any value'
659 info_text = 'any value'
660
660
661
661
662 class Int(TraitletType):
662 class Int(TraitletType):
663 """A integer traitlet."""
663
664
664 evaluate = int
665 evaluate = int
665 default_value = 0
666 default_value = 0
666 info_text = 'an integer'
667 info_text = 'an integer'
667
668
668 def validate(self, obj, value):
669 def validate(self, obj, value):
669 if isinstance(value, int):
670 if isinstance(value, int):
670 return value
671 return value
671 self.error(obj, value)
672 self.error(obj, value)
672
673
674 class CInt(Int):
675 """A casting version of the int traitlet."""
676
677 def validate(self, obj, value):
678 try:
679 return int(value)
680 except:
681 self.error(obj, value)
682
673
683
674 class Long(TraitletType):
684 class Long(TraitletType):
685 """A long integer traitlet."""
675
686
676 evaluate = long
687 evaluate = long
677 default_value = 0L
688 default_value = 0L
678 info_text = 'a long'
689 info_text = 'a long'
679
690
680 def validate(self, obj, value):
691 def validate(self, obj, value):
681 if isinstance(value, long):
692 if isinstance(value, long):
682 return value
693 return value
683 if isinstance(value, int):
694 if isinstance(value, int):
684 return long(value)
695 return long(value)
685 self.error(obj, value)
696 self.error(obj, value)
686
697
687
698
699 class CLong(Long):
700 """A casting version of the long integer traitlet."""
701
702 def validate(self, obj, value):
703 try:
704 return long(value)
705 except:
706 self.error(obj, value)
707
708
688 class Float(TraitletType):
709 class Float(TraitletType):
710 """A float traitlet."""
689
711
690 evaluate = float
712 evaluate = float
691 default_value = 0.0
713 default_value = 0.0
692 info_text = 'a float'
714 info_text = 'a float'
693
715
694 def validate(self, obj, value):
716 def validate(self, obj, value):
695 if isinstance(value, float):
717 if isinstance(value, float):
696 return value
718 return value
697 if isinstance(value, int):
719 if isinstance(value, int):
698 return float(value)
720 return float(value)
699 self.error(obj, value)
721 self.error(obj, value)
700
722
701
723
724 class CFloat(Float):
725 """A casting version of the float traitlet."""
726
727 def validate(self, obj, value):
728 try:
729 return float(value)
730 except:
731 self.error(obj, value)
732
702 class Complex(TraitletType):
733 class Complex(TraitletType):
734 """A traitlet for complex numbers."""
703
735
704 evaluate = complex
736 evaluate = complex
705 default_value = 0.0 + 0.0j
737 default_value = 0.0 + 0.0j
706 info_text = 'a complex number'
738 info_text = 'a complex number'
707
739
708 def validate(self, obj, value):
740 def validate(self, obj, value):
709 if isinstance(value, complex):
741 if isinstance(value, complex):
710 return value
742 return value
711 if isinstance(value, (float, int)):
743 if isinstance(value, (float, int)):
712 return complex(value)
744 return complex(value)
713 self.error(obj, value)
745 self.error(obj, value)
714
746
715
747
748 class CComplex(Complex):
749 """A casting version of the complex number traitlet."""
750
751 def validate (self, obj, value):
752 try:
753 return complex(value)
754 except:
755 self.error(obj, value)
756
757
716 class Str(TraitletType):
758 class Str(TraitletType):
759 """A traitlet for strings."""
717
760
718 evaluate = lambda x: x
761 evaluate = lambda x: x
719 default_value = ''
762 default_value = ''
720 info_text = 'a string'
763 info_text = 'a string'
721
764
722 def validate(self, obj, value):
765 def validate(self, obj, value):
723 if isinstance(value, str):
766 if isinstance(value, str):
724 return value
767 return value
725 self.error(obj, value)
768 self.error(obj, value)
726
769
727
770
771 class CStr(Str):
772 """A casting version of the string traitlet."""
773
774 def validate(self, obj, value):
775 try:
776 return str(value)
777 except:
778 try:
779 return unicode(value)
780 except:
781 self.error(obj, value)
782
783
728 class Unicode(TraitletType):
784 class Unicode(TraitletType):
785 """A traitlet for unicode strings."""
729
786
730 evaluate = unicode
787 evaluate = unicode
731 default_value = u''
788 default_value = u''
732 info_text = 'a unicode string'
789 info_text = 'a unicode string'
733
790
734 def validate(self, obj, value):
791 def validate(self, obj, value):
735 if isinstance(value, unicode):
792 if isinstance(value, unicode):
736 return value
793 return value
737 if isinstance(value, str):
794 if isinstance(value, str):
738 return unicode(value)
795 return unicode(value)
739 self.error(obj, value)
796 self.error(obj, value)
740
797
741
798
742 class Bool(TraitletType):
799 class CUnicode(Unicode):
800 """A casting version of the unicode traitlet."""
743
801
802 def validate(self, obj, value):
803 try:
804 return unicode(value)
805 except:
806 self.error(obj, value)
807
808
809 class Bool(TraitletType):
810 """A boolean (True, False) traitlet."""
744 evaluate = bool
811 evaluate = bool
745 default_value = False
812 default_value = False
746 info_text = 'a boolean'
813 info_text = 'a boolean'
747
814
748 def validate(self, obj, value):
815 def validate(self, obj, value):
749 if isinstance(value, bool):
816 if isinstance(value, bool):
750 return value
817 return value
751 self.error(obj, value)
818 self.error(obj, value)
752
819
820
821 class CBool(Bool):
822 """A casting version of the boolean traitlet."""
823
824 def validate(self, obj, value):
825 try:
826 return bool(value)
827 except:
828 self.error(obj, value) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now