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