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