##// END OF EJS Templates
Add ObjectName and DottedObjectName trait types for referring to Python identifiers.
Thomas Kluyver -
Show More
@@ -1,593 +1,593 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Display formatters.
2 """Display formatters.
3
3
4
4
5 Authors:
5 Authors:
6
6
7 * Robert Kern
7 * Robert Kern
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (c) 2010, IPython Development Team.
11 # Copyright (c) 2010, IPython Development Team.
12 #
12 #
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14 #
14 #
15 # The full license is in the file COPYING.txt, distributed with this software.
15 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # Stdlib imports
22 # Stdlib imports
23 import abc
23 import abc
24 import sys
24 import sys
25 # We must use StringIO, as cStringIO doesn't handle unicode properly.
25 # We must use StringIO, as cStringIO doesn't handle unicode properly.
26 from StringIO import StringIO
26 from StringIO import StringIO
27
27
28 # Our own imports
28 # Our own imports
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.lib import pretty
30 from IPython.lib import pretty
31 from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode
31 from IPython.utils.traitlets import Bool, Dict, Int, Unicode, CUnicode, ObjectName
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # The main DisplayFormatter class
35 # The main DisplayFormatter class
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38
38
39 class DisplayFormatter(Configurable):
39 class DisplayFormatter(Configurable):
40
40
41 # When set to true only the default plain text formatter will be used.
41 # When set to true only the default plain text formatter will be used.
42 plain_text_only = Bool(False, config=True)
42 plain_text_only = Bool(False, config=True)
43
43
44 # A dict of formatter whose keys are format types (MIME types) and whose
44 # A dict of formatter whose keys are format types (MIME types) and whose
45 # values are subclasses of BaseFormatter.
45 # values are subclasses of BaseFormatter.
46 formatters = Dict(config=True)
46 formatters = Dict(config=True)
47 def _formatters_default(self):
47 def _formatters_default(self):
48 """Activate the default formatters."""
48 """Activate the default formatters."""
49 formatter_classes = [
49 formatter_classes = [
50 PlainTextFormatter,
50 PlainTextFormatter,
51 HTMLFormatter,
51 HTMLFormatter,
52 SVGFormatter,
52 SVGFormatter,
53 PNGFormatter,
53 PNGFormatter,
54 LatexFormatter,
54 LatexFormatter,
55 JSONFormatter,
55 JSONFormatter,
56 JavascriptFormatter
56 JavascriptFormatter
57 ]
57 ]
58 d = {}
58 d = {}
59 for cls in formatter_classes:
59 for cls in formatter_classes:
60 f = cls(config=self.config)
60 f = cls(config=self.config)
61 d[f.format_type] = f
61 d[f.format_type] = f
62 return d
62 return d
63
63
64 def format(self, obj, include=None, exclude=None):
64 def format(self, obj, include=None, exclude=None):
65 """Return a format data dict for an object.
65 """Return a format data dict for an object.
66
66
67 By default all format types will be computed.
67 By default all format types will be computed.
68
68
69 The following MIME types are currently implemented:
69 The following MIME types are currently implemented:
70
70
71 * text/plain
71 * text/plain
72 * text/html
72 * text/html
73 * text/latex
73 * text/latex
74 * application/json
74 * application/json
75 * image/png
75 * image/png
76 * immage/svg+xml
76 * immage/svg+xml
77
77
78 Parameters
78 Parameters
79 ----------
79 ----------
80 obj : object
80 obj : object
81 The Python object whose format data will be computed.
81 The Python object whose format data will be computed.
82 include : list or tuple, optional
82 include : list or tuple, optional
83 A list of format type strings (MIME types) to include in the
83 A list of format type strings (MIME types) to include in the
84 format data dict. If this is set *only* the format types included
84 format data dict. If this is set *only* the format types included
85 in this list will be computed.
85 in this list will be computed.
86 exclude : list or tuple, optional
86 exclude : list or tuple, optional
87 A list of format type string (MIME types) to exclue in the format
87 A list of format type string (MIME types) to exclue in the format
88 data dict. If this is set all format types will be computed,
88 data dict. If this is set all format types will be computed,
89 except for those included in this argument.
89 except for those included in this argument.
90
90
91 Returns
91 Returns
92 -------
92 -------
93 format_dict : dict
93 format_dict : dict
94 A dictionary of key/value pairs, one or each format that was
94 A dictionary of key/value pairs, one or each format that was
95 generated for the object. The keys are the format types, which
95 generated for the object. The keys are the format types, which
96 will usually be MIME type strings and the values and JSON'able
96 will usually be MIME type strings and the values and JSON'able
97 data structure containing the raw data for the representation in
97 data structure containing the raw data for the representation in
98 that format.
98 that format.
99 """
99 """
100 format_dict = {}
100 format_dict = {}
101
101
102 # If plain text only is active
102 # If plain text only is active
103 if self.plain_text_only:
103 if self.plain_text_only:
104 formatter = self.formatters['text/plain']
104 formatter = self.formatters['text/plain']
105 try:
105 try:
106 data = formatter(obj)
106 data = formatter(obj)
107 except:
107 except:
108 # FIXME: log the exception
108 # FIXME: log the exception
109 raise
109 raise
110 if data is not None:
110 if data is not None:
111 format_dict['text/plain'] = data
111 format_dict['text/plain'] = data
112 return format_dict
112 return format_dict
113
113
114 for format_type, formatter in self.formatters.items():
114 for format_type, formatter in self.formatters.items():
115 if include is not None:
115 if include is not None:
116 if format_type not in include:
116 if format_type not in include:
117 continue
117 continue
118 if exclude is not None:
118 if exclude is not None:
119 if format_type in exclude:
119 if format_type in exclude:
120 continue
120 continue
121 try:
121 try:
122 data = formatter(obj)
122 data = formatter(obj)
123 except:
123 except:
124 # FIXME: log the exception
124 # FIXME: log the exception
125 raise
125 raise
126 if data is not None:
126 if data is not None:
127 format_dict[format_type] = data
127 format_dict[format_type] = data
128 return format_dict
128 return format_dict
129
129
130 @property
130 @property
131 def format_types(self):
131 def format_types(self):
132 """Return the format types (MIME types) of the active formatters."""
132 """Return the format types (MIME types) of the active formatters."""
133 return self.formatters.keys()
133 return self.formatters.keys()
134
134
135
135
136 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137 # Formatters for specific format types (text, html, svg, etc.)
137 # Formatters for specific format types (text, html, svg, etc.)
138 #-----------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
139
139
140
140
141 class FormatterABC(object):
141 class FormatterABC(object):
142 """ Abstract base class for Formatters.
142 """ Abstract base class for Formatters.
143
143
144 A formatter is a callable class that is responsible for computing the
144 A formatter is a callable class that is responsible for computing the
145 raw format data for a particular format type (MIME type). For example,
145 raw format data for a particular format type (MIME type). For example,
146 an HTML formatter would have a format type of `text/html` and would return
146 an HTML formatter would have a format type of `text/html` and would return
147 the HTML representation of the object when called.
147 the HTML representation of the object when called.
148 """
148 """
149 __metaclass__ = abc.ABCMeta
149 __metaclass__ = abc.ABCMeta
150
150
151 # The format type of the data returned, usually a MIME type.
151 # The format type of the data returned, usually a MIME type.
152 format_type = 'text/plain'
152 format_type = 'text/plain'
153
153
154 # Is the formatter enabled...
154 # Is the formatter enabled...
155 enabled = True
155 enabled = True
156
156
157 @abc.abstractmethod
157 @abc.abstractmethod
158 def __call__(self, obj):
158 def __call__(self, obj):
159 """Return a JSON'able representation of the object.
159 """Return a JSON'able representation of the object.
160
160
161 If the object cannot be formatted by this formatter, then return None
161 If the object cannot be formatted by this formatter, then return None
162 """
162 """
163 try:
163 try:
164 return repr(obj)
164 return repr(obj)
165 except TypeError:
165 except TypeError:
166 return None
166 return None
167
167
168
168
169 class BaseFormatter(Configurable):
169 class BaseFormatter(Configurable):
170 """A base formatter class that is configurable.
170 """A base formatter class that is configurable.
171
171
172 This formatter should usually be used as the base class of all formatters.
172 This formatter should usually be used as the base class of all formatters.
173 It is a traited :class:`Configurable` class and includes an extensible
173 It is a traited :class:`Configurable` class and includes an extensible
174 API for users to determine how their objects are formatted. The following
174 API for users to determine how their objects are formatted. The following
175 logic is used to find a function to format an given object.
175 logic is used to find a function to format an given object.
176
176
177 1. The object is introspected to see if it has a method with the name
177 1. The object is introspected to see if it has a method with the name
178 :attr:`print_method`. If is does, that object is passed to that method
178 :attr:`print_method`. If is does, that object is passed to that method
179 for formatting.
179 for formatting.
180 2. If no print method is found, three internal dictionaries are consulted
180 2. If no print method is found, three internal dictionaries are consulted
181 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
181 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
182 and :attr:`deferred_printers`.
182 and :attr:`deferred_printers`.
183
183
184 Users should use these dictionaries to register functions that will be
184 Users should use these dictionaries to register functions that will be
185 used to compute the format data for their objects (if those objects don't
185 used to compute the format data for their objects (if those objects don't
186 have the special print methods). The easiest way of using these
186 have the special print methods). The easiest way of using these
187 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
187 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
188 methods.
188 methods.
189
189
190 If no function/callable is found to compute the format data, ``None`` is
190 If no function/callable is found to compute the format data, ``None`` is
191 returned and this format type is not used.
191 returned and this format type is not used.
192 """
192 """
193
193
194 format_type = Unicode('text/plain')
194 format_type = Unicode('text/plain')
195
195
196 enabled = Bool(True, config=True)
196 enabled = Bool(True, config=True)
197
197
198 print_method = Unicode('__repr__')
198 print_method = ObjectName('__repr__')
199
199
200 # The singleton printers.
200 # The singleton printers.
201 # Maps the IDs of the builtin singleton objects to the format functions.
201 # Maps the IDs of the builtin singleton objects to the format functions.
202 singleton_printers = Dict(config=True)
202 singleton_printers = Dict(config=True)
203 def _singleton_printers_default(self):
203 def _singleton_printers_default(self):
204 return {}
204 return {}
205
205
206 # The type-specific printers.
206 # The type-specific printers.
207 # Map type objects to the format functions.
207 # Map type objects to the format functions.
208 type_printers = Dict(config=True)
208 type_printers = Dict(config=True)
209 def _type_printers_default(self):
209 def _type_printers_default(self):
210 return {}
210 return {}
211
211
212 # The deferred-import type-specific printers.
212 # The deferred-import type-specific printers.
213 # Map (modulename, classname) pairs to the format functions.
213 # Map (modulename, classname) pairs to the format functions.
214 deferred_printers = Dict(config=True)
214 deferred_printers = Dict(config=True)
215 def _deferred_printers_default(self):
215 def _deferred_printers_default(self):
216 return {}
216 return {}
217
217
218 def __call__(self, obj):
218 def __call__(self, obj):
219 """Compute the format for an object."""
219 """Compute the format for an object."""
220 if self.enabled:
220 if self.enabled:
221 obj_id = id(obj)
221 obj_id = id(obj)
222 try:
222 try:
223 obj_class = getattr(obj, '__class__', None) or type(obj)
223 obj_class = getattr(obj, '__class__', None) or type(obj)
224 # First try to find registered singleton printers for the type.
224 # First try to find registered singleton printers for the type.
225 try:
225 try:
226 printer = self.singleton_printers[obj_id]
226 printer = self.singleton_printers[obj_id]
227 except (TypeError, KeyError):
227 except (TypeError, KeyError):
228 pass
228 pass
229 else:
229 else:
230 return printer(obj)
230 return printer(obj)
231 # Next look for type_printers.
231 # Next look for type_printers.
232 for cls in pretty._get_mro(obj_class):
232 for cls in pretty._get_mro(obj_class):
233 if cls in self.type_printers:
233 if cls in self.type_printers:
234 return self.type_printers[cls](obj)
234 return self.type_printers[cls](obj)
235 else:
235 else:
236 printer = self._in_deferred_types(cls)
236 printer = self._in_deferred_types(cls)
237 if printer is not None:
237 if printer is not None:
238 return printer(obj)
238 return printer(obj)
239 # Finally look for special method names.
239 # Finally look for special method names.
240 if hasattr(obj_class, self.print_method):
240 if hasattr(obj_class, self.print_method):
241 printer = getattr(obj_class, self.print_method)
241 printer = getattr(obj_class, self.print_method)
242 return printer(obj)
242 return printer(obj)
243 return None
243 return None
244 except Exception:
244 except Exception:
245 pass
245 pass
246 else:
246 else:
247 return None
247 return None
248
248
249 def for_type(self, typ, func):
249 def for_type(self, typ, func):
250 """Add a format function for a given type.
250 """Add a format function for a given type.
251
251
252 Parameters
252 Parameters
253 -----------
253 -----------
254 typ : class
254 typ : class
255 The class of the object that will be formatted using `func`.
255 The class of the object that will be formatted using `func`.
256 func : callable
256 func : callable
257 The callable that will be called to compute the format data. The
257 The callable that will be called to compute the format data. The
258 call signature of this function is simple, it must take the
258 call signature of this function is simple, it must take the
259 object to be formatted and return the raw data for the given
259 object to be formatted and return the raw data for the given
260 format. Subclasses may use a different call signature for the
260 format. Subclasses may use a different call signature for the
261 `func` argument.
261 `func` argument.
262 """
262 """
263 oldfunc = self.type_printers.get(typ, None)
263 oldfunc = self.type_printers.get(typ, None)
264 if func is not None:
264 if func is not None:
265 # To support easy restoration of old printers, we need to ignore
265 # To support easy restoration of old printers, we need to ignore
266 # Nones.
266 # Nones.
267 self.type_printers[typ] = func
267 self.type_printers[typ] = func
268 return oldfunc
268 return oldfunc
269
269
270 def for_type_by_name(self, type_module, type_name, func):
270 def for_type_by_name(self, type_module, type_name, func):
271 """Add a format function for a type specified by the full dotted
271 """Add a format function for a type specified by the full dotted
272 module and name of the type, rather than the type of the object.
272 module and name of the type, rather than the type of the object.
273
273
274 Parameters
274 Parameters
275 ----------
275 ----------
276 type_module : str
276 type_module : str
277 The full dotted name of the module the type is defined in, like
277 The full dotted name of the module the type is defined in, like
278 ``numpy``.
278 ``numpy``.
279 type_name : str
279 type_name : str
280 The name of the type (the class name), like ``dtype``
280 The name of the type (the class name), like ``dtype``
281 func : callable
281 func : callable
282 The callable that will be called to compute the format data. The
282 The callable that will be called to compute the format data. The
283 call signature of this function is simple, it must take the
283 call signature of this function is simple, it must take the
284 object to be formatted and return the raw data for the given
284 object to be formatted and return the raw data for the given
285 format. Subclasses may use a different call signature for the
285 format. Subclasses may use a different call signature for the
286 `func` argument.
286 `func` argument.
287 """
287 """
288 key = (type_module, type_name)
288 key = (type_module, type_name)
289 oldfunc = self.deferred_printers.get(key, None)
289 oldfunc = self.deferred_printers.get(key, None)
290 if func is not None:
290 if func is not None:
291 # To support easy restoration of old printers, we need to ignore
291 # To support easy restoration of old printers, we need to ignore
292 # Nones.
292 # Nones.
293 self.deferred_printers[key] = func
293 self.deferred_printers[key] = func
294 return oldfunc
294 return oldfunc
295
295
296 def _in_deferred_types(self, cls):
296 def _in_deferred_types(self, cls):
297 """
297 """
298 Check if the given class is specified in the deferred type registry.
298 Check if the given class is specified in the deferred type registry.
299
299
300 Returns the printer from the registry if it exists, and None if the
300 Returns the printer from the registry if it exists, and None if the
301 class is not in the registry. Successful matches will be moved to the
301 class is not in the registry. Successful matches will be moved to the
302 regular type registry for future use.
302 regular type registry for future use.
303 """
303 """
304 mod = getattr(cls, '__module__', None)
304 mod = getattr(cls, '__module__', None)
305 name = getattr(cls, '__name__', None)
305 name = getattr(cls, '__name__', None)
306 key = (mod, name)
306 key = (mod, name)
307 printer = None
307 printer = None
308 if key in self.deferred_printers:
308 if key in self.deferred_printers:
309 # Move the printer over to the regular registry.
309 # Move the printer over to the regular registry.
310 printer = self.deferred_printers.pop(key)
310 printer = self.deferred_printers.pop(key)
311 self.type_printers[cls] = printer
311 self.type_printers[cls] = printer
312 return printer
312 return printer
313
313
314
314
315 class PlainTextFormatter(BaseFormatter):
315 class PlainTextFormatter(BaseFormatter):
316 """The default pretty-printer.
316 """The default pretty-printer.
317
317
318 This uses :mod:`IPython.external.pretty` to compute the format data of
318 This uses :mod:`IPython.external.pretty` to compute the format data of
319 the object. If the object cannot be pretty printed, :func:`repr` is used.
319 the object. If the object cannot be pretty printed, :func:`repr` is used.
320 See the documentation of :mod:`IPython.external.pretty` for details on
320 See the documentation of :mod:`IPython.external.pretty` for details on
321 how to write pretty printers. Here is a simple example::
321 how to write pretty printers. Here is a simple example::
322
322
323 def dtype_pprinter(obj, p, cycle):
323 def dtype_pprinter(obj, p, cycle):
324 if cycle:
324 if cycle:
325 return p.text('dtype(...)')
325 return p.text('dtype(...)')
326 if hasattr(obj, 'fields'):
326 if hasattr(obj, 'fields'):
327 if obj.fields is None:
327 if obj.fields is None:
328 p.text(repr(obj))
328 p.text(repr(obj))
329 else:
329 else:
330 p.begin_group(7, 'dtype([')
330 p.begin_group(7, 'dtype([')
331 for i, field in enumerate(obj.descr):
331 for i, field in enumerate(obj.descr):
332 if i > 0:
332 if i > 0:
333 p.text(',')
333 p.text(',')
334 p.breakable()
334 p.breakable()
335 p.pretty(field)
335 p.pretty(field)
336 p.end_group(7, '])')
336 p.end_group(7, '])')
337 """
337 """
338
338
339 # The format type of data returned.
339 # The format type of data returned.
340 format_type = Unicode('text/plain')
340 format_type = Unicode('text/plain')
341
341
342 # This subclass ignores this attribute as it always need to return
342 # This subclass ignores this attribute as it always need to return
343 # something.
343 # something.
344 enabled = Bool(True, config=False)
344 enabled = Bool(True, config=False)
345
345
346 # Look for a _repr_pretty_ methods to use for pretty printing.
346 # Look for a _repr_pretty_ methods to use for pretty printing.
347 print_method = Unicode('_repr_pretty_')
347 print_method = ObjectName('_repr_pretty_')
348
348
349 # Whether to pretty-print or not.
349 # Whether to pretty-print or not.
350 pprint = Bool(True, config=True)
350 pprint = Bool(True, config=True)
351
351
352 # Whether to be verbose or not.
352 # Whether to be verbose or not.
353 verbose = Bool(False, config=True)
353 verbose = Bool(False, config=True)
354
354
355 # The maximum width.
355 # The maximum width.
356 max_width = Int(79, config=True)
356 max_width = Int(79, config=True)
357
357
358 # The newline character.
358 # The newline character.
359 newline = Unicode('\n', config=True)
359 newline = Unicode('\n', config=True)
360
360
361 # format-string for pprinting floats
361 # format-string for pprinting floats
362 float_format = Unicode('%r')
362 float_format = Unicode('%r')
363 # setter for float precision, either int or direct format-string
363 # setter for float precision, either int or direct format-string
364 float_precision = CUnicode('', config=True)
364 float_precision = CUnicode('', config=True)
365
365
366 def _float_precision_changed(self, name, old, new):
366 def _float_precision_changed(self, name, old, new):
367 """float_precision changed, set float_format accordingly.
367 """float_precision changed, set float_format accordingly.
368
368
369 float_precision can be set by int or str.
369 float_precision can be set by int or str.
370 This will set float_format, after interpreting input.
370 This will set float_format, after interpreting input.
371 If numpy has been imported, numpy print precision will also be set.
371 If numpy has been imported, numpy print precision will also be set.
372
372
373 integer `n` sets format to '%.nf', otherwise, format set directly.
373 integer `n` sets format to '%.nf', otherwise, format set directly.
374
374
375 An empty string returns to defaults (repr for float, 8 for numpy).
375 An empty string returns to defaults (repr for float, 8 for numpy).
376
376
377 This parameter can be set via the '%precision' magic.
377 This parameter can be set via the '%precision' magic.
378 """
378 """
379
379
380 if '%' in new:
380 if '%' in new:
381 # got explicit format string
381 # got explicit format string
382 fmt = new
382 fmt = new
383 try:
383 try:
384 fmt%3.14159
384 fmt%3.14159
385 except Exception:
385 except Exception:
386 raise ValueError("Precision must be int or format string, not %r"%new)
386 raise ValueError("Precision must be int or format string, not %r"%new)
387 elif new:
387 elif new:
388 # otherwise, should be an int
388 # otherwise, should be an int
389 try:
389 try:
390 i = int(new)
390 i = int(new)
391 assert i >= 0
391 assert i >= 0
392 except ValueError:
392 except ValueError:
393 raise ValueError("Precision must be int or format string, not %r"%new)
393 raise ValueError("Precision must be int or format string, not %r"%new)
394 except AssertionError:
394 except AssertionError:
395 raise ValueError("int precision must be non-negative, not %r"%i)
395 raise ValueError("int precision must be non-negative, not %r"%i)
396
396
397 fmt = '%%.%if'%i
397 fmt = '%%.%if'%i
398 if 'numpy' in sys.modules:
398 if 'numpy' in sys.modules:
399 # set numpy precision if it has been imported
399 # set numpy precision if it has been imported
400 import numpy
400 import numpy
401 numpy.set_printoptions(precision=i)
401 numpy.set_printoptions(precision=i)
402 else:
402 else:
403 # default back to repr
403 # default back to repr
404 fmt = '%r'
404 fmt = '%r'
405 if 'numpy' in sys.modules:
405 if 'numpy' in sys.modules:
406 import numpy
406 import numpy
407 # numpy default is 8
407 # numpy default is 8
408 numpy.set_printoptions(precision=8)
408 numpy.set_printoptions(precision=8)
409 self.float_format = fmt
409 self.float_format = fmt
410
410
411 # Use the default pretty printers from IPython.external.pretty.
411 # Use the default pretty printers from IPython.external.pretty.
412 def _singleton_printers_default(self):
412 def _singleton_printers_default(self):
413 return pretty._singleton_pprinters.copy()
413 return pretty._singleton_pprinters.copy()
414
414
415 def _type_printers_default(self):
415 def _type_printers_default(self):
416 d = pretty._type_pprinters.copy()
416 d = pretty._type_pprinters.copy()
417 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
417 d[float] = lambda obj,p,cycle: p.text(self.float_format%obj)
418 return d
418 return d
419
419
420 def _deferred_printers_default(self):
420 def _deferred_printers_default(self):
421 return pretty._deferred_type_pprinters.copy()
421 return pretty._deferred_type_pprinters.copy()
422
422
423 #### FormatterABC interface ####
423 #### FormatterABC interface ####
424
424
425 def __call__(self, obj):
425 def __call__(self, obj):
426 """Compute the pretty representation of the object."""
426 """Compute the pretty representation of the object."""
427 if not self.pprint:
427 if not self.pprint:
428 try:
428 try:
429 return repr(obj)
429 return repr(obj)
430 except TypeError:
430 except TypeError:
431 return ''
431 return ''
432 else:
432 else:
433 # This uses use StringIO, as cStringIO doesn't handle unicode.
433 # This uses use StringIO, as cStringIO doesn't handle unicode.
434 stream = StringIO()
434 stream = StringIO()
435 printer = pretty.RepresentationPrinter(stream, self.verbose,
435 printer = pretty.RepresentationPrinter(stream, self.verbose,
436 self.max_width, self.newline,
436 self.max_width, self.newline,
437 singleton_pprinters=self.singleton_printers,
437 singleton_pprinters=self.singleton_printers,
438 type_pprinters=self.type_printers,
438 type_pprinters=self.type_printers,
439 deferred_pprinters=self.deferred_printers)
439 deferred_pprinters=self.deferred_printers)
440 printer.pretty(obj)
440 printer.pretty(obj)
441 printer.flush()
441 printer.flush()
442 return stream.getvalue()
442 return stream.getvalue()
443
443
444
444
445 class HTMLFormatter(BaseFormatter):
445 class HTMLFormatter(BaseFormatter):
446 """An HTML formatter.
446 """An HTML formatter.
447
447
448 To define the callables that compute the HTML representation of your
448 To define the callables that compute the HTML representation of your
449 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
449 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
450 or :meth:`for_type_by_name` methods to register functions that handle
450 or :meth:`for_type_by_name` methods to register functions that handle
451 this.
451 this.
452
452
453 The return value of this formatter should be a valid HTML snippet that
453 The return value of this formatter should be a valid HTML snippet that
454 could be injected into an existing DOM. It should *not* include the
454 could be injected into an existing DOM. It should *not* include the
455 ```<html>`` or ```<body>`` tags.
455 ```<html>`` or ```<body>`` tags.
456 """
456 """
457 format_type = Unicode('text/html')
457 format_type = Unicode('text/html')
458
458
459 print_method = Unicode('_repr_html_')
459 print_method = ObjectName('_repr_html_')
460
460
461
461
462 class SVGFormatter(BaseFormatter):
462 class SVGFormatter(BaseFormatter):
463 """An SVG formatter.
463 """An SVG formatter.
464
464
465 To define the callables that compute the SVG representation of your
465 To define the callables that compute the SVG representation of your
466 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
466 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
467 or :meth:`for_type_by_name` methods to register functions that handle
467 or :meth:`for_type_by_name` methods to register functions that handle
468 this.
468 this.
469
469
470 The return value of this formatter should be valid SVG enclosed in
470 The return value of this formatter should be valid SVG enclosed in
471 ```<svg>``` tags, that could be injected into an existing DOM. It should
471 ```<svg>``` tags, that could be injected into an existing DOM. It should
472 *not* include the ```<html>`` or ```<body>`` tags.
472 *not* include the ```<html>`` or ```<body>`` tags.
473 """
473 """
474 format_type = Unicode('image/svg+xml')
474 format_type = Unicode('image/svg+xml')
475
475
476 print_method = Unicode('_repr_svg_')
476 print_method = ObjectName('_repr_svg_')
477
477
478
478
479 class PNGFormatter(BaseFormatter):
479 class PNGFormatter(BaseFormatter):
480 """A PNG formatter.
480 """A PNG formatter.
481
481
482 To define the callables that compute the PNG representation of your
482 To define the callables that compute the PNG representation of your
483 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
483 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
484 or :meth:`for_type_by_name` methods to register functions that handle
484 or :meth:`for_type_by_name` methods to register functions that handle
485 this.
485 this.
486
486
487 The return value of this formatter should be raw PNG data, *not*
487 The return value of this formatter should be raw PNG data, *not*
488 base64 encoded.
488 base64 encoded.
489 """
489 """
490 format_type = Unicode('image/png')
490 format_type = Unicode('image/png')
491
491
492 print_method = Unicode('_repr_png_')
492 print_method = ObjectName('_repr_png_')
493
493
494
494
495 class LatexFormatter(BaseFormatter):
495 class LatexFormatter(BaseFormatter):
496 """A LaTeX formatter.
496 """A LaTeX formatter.
497
497
498 To define the callables that compute the LaTeX representation of your
498 To define the callables that compute the LaTeX representation of your
499 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
499 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
500 or :meth:`for_type_by_name` methods to register functions that handle
500 or :meth:`for_type_by_name` methods to register functions that handle
501 this.
501 this.
502
502
503 The return value of this formatter should be a valid LaTeX equation,
503 The return value of this formatter should be a valid LaTeX equation,
504 enclosed in either ```$``` or ```$$```.
504 enclosed in either ```$``` or ```$$```.
505 """
505 """
506 format_type = Unicode('text/latex')
506 format_type = Unicode('text/latex')
507
507
508 print_method = Unicode('_repr_latex_')
508 print_method = ObjectName('_repr_latex_')
509
509
510
510
511 class JSONFormatter(BaseFormatter):
511 class JSONFormatter(BaseFormatter):
512 """A JSON string formatter.
512 """A JSON string formatter.
513
513
514 To define the callables that compute the JSON string representation of
514 To define the callables that compute the JSON string representation of
515 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
515 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
516 or :meth:`for_type_by_name` methods to register functions that handle
516 or :meth:`for_type_by_name` methods to register functions that handle
517 this.
517 this.
518
518
519 The return value of this formatter should be a valid JSON string.
519 The return value of this formatter should be a valid JSON string.
520 """
520 """
521 format_type = Unicode('application/json')
521 format_type = Unicode('application/json')
522
522
523 print_method = Unicode('_repr_json_')
523 print_method = ObjectName('_repr_json_')
524
524
525
525
526 class JavascriptFormatter(BaseFormatter):
526 class JavascriptFormatter(BaseFormatter):
527 """A Javascript formatter.
527 """A Javascript formatter.
528
528
529 To define the callables that compute the Javascript representation of
529 To define the callables that compute the Javascript representation of
530 your objects, define a :meth:`_repr_javascript_` method or use the
530 your objects, define a :meth:`_repr_javascript_` method or use the
531 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
531 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
532 that handle this.
532 that handle this.
533
533
534 The return value of this formatter should be valid Javascript code and
534 The return value of this formatter should be valid Javascript code and
535 should *not* be enclosed in ```<script>``` tags.
535 should *not* be enclosed in ```<script>``` tags.
536 """
536 """
537 format_type = Unicode('application/javascript')
537 format_type = Unicode('application/javascript')
538
538
539 print_method = Unicode('_repr_javascript_')
539 print_method = ObjectName('_repr_javascript_')
540
540
541 FormatterABC.register(BaseFormatter)
541 FormatterABC.register(BaseFormatter)
542 FormatterABC.register(PlainTextFormatter)
542 FormatterABC.register(PlainTextFormatter)
543 FormatterABC.register(HTMLFormatter)
543 FormatterABC.register(HTMLFormatter)
544 FormatterABC.register(SVGFormatter)
544 FormatterABC.register(SVGFormatter)
545 FormatterABC.register(PNGFormatter)
545 FormatterABC.register(PNGFormatter)
546 FormatterABC.register(LatexFormatter)
546 FormatterABC.register(LatexFormatter)
547 FormatterABC.register(JSONFormatter)
547 FormatterABC.register(JSONFormatter)
548 FormatterABC.register(JavascriptFormatter)
548 FormatterABC.register(JavascriptFormatter)
549
549
550
550
551 def format_display_data(obj, include=None, exclude=None):
551 def format_display_data(obj, include=None, exclude=None):
552 """Return a format data dict for an object.
552 """Return a format data dict for an object.
553
553
554 By default all format types will be computed.
554 By default all format types will be computed.
555
555
556 The following MIME types are currently implemented:
556 The following MIME types are currently implemented:
557
557
558 * text/plain
558 * text/plain
559 * text/html
559 * text/html
560 * text/latex
560 * text/latex
561 * application/json
561 * application/json
562 * image/png
562 * image/png
563 * immage/svg+xml
563 * immage/svg+xml
564
564
565 Parameters
565 Parameters
566 ----------
566 ----------
567 obj : object
567 obj : object
568 The Python object whose format data will be computed.
568 The Python object whose format data will be computed.
569
569
570 Returns
570 Returns
571 -------
571 -------
572 format_dict : dict
572 format_dict : dict
573 A dictionary of key/value pairs, one or each format that was
573 A dictionary of key/value pairs, one or each format that was
574 generated for the object. The keys are the format types, which
574 generated for the object. The keys are the format types, which
575 will usually be MIME type strings and the values and JSON'able
575 will usually be MIME type strings and the values and JSON'able
576 data structure containing the raw data for the representation in
576 data structure containing the raw data for the representation in
577 that format.
577 that format.
578 include : list or tuple, optional
578 include : list or tuple, optional
579 A list of format type strings (MIME types) to include in the
579 A list of format type strings (MIME types) to include in the
580 format data dict. If this is set *only* the format types included
580 format data dict. If this is set *only* the format types included
581 in this list will be computed.
581 in this list will be computed.
582 exclude : list or tuple, optional
582 exclude : list or tuple, optional
583 A list of format type string (MIME types) to exclue in the format
583 A list of format type string (MIME types) to exclue in the format
584 data dict. If this is set all format types will be computed,
584 data dict. If this is set all format types will be computed,
585 except for those included in this argument.
585 except for those included in this argument.
586 """
586 """
587 from IPython.core.interactiveshell import InteractiveShell
587 from IPython.core.interactiveshell import InteractiveShell
588
588
589 InteractiveShell.instance().display_formatter.format(
589 InteractiveShell.instance().display_formatter.format(
590 obj,
590 obj,
591 include,
591 include,
592 exclude
592 exclude
593 )
593 )
@@ -1,814 +1,847 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Tests for IPython.utils.traitlets.
4 Tests for IPython.utils.traitlets.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
10 and is licensed under the BSD license. Also, many of the ideas also come
10 and is licensed under the BSD license. Also, many of the ideas also come
11 from enthought.traits even though our implementation is very different.
11 from enthought.traits even though our implementation is very different.
12 """
12 """
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2008-2009 The IPython Development Team
15 # Copyright (C) 2008-2009 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 import sys
25 from unittest import TestCase
26 from unittest import TestCase
26
27
27 from IPython.utils.traitlets import (
28 from IPython.utils.traitlets import (
28 HasTraits, MetaHasTraits, TraitType, Any, CBytes,
29 HasTraits, MetaHasTraits, TraitType, Any, CBytes,
29 Int, Long, Float, Complex, Bytes, Unicode, TraitError,
30 Int, Long, Float, Complex, Bytes, Unicode, TraitError,
30 Undefined, Type, This, Instance, TCPAddress, List, Tuple
31 Undefined, Type, This, Instance, TCPAddress, List, Tuple,
32 ObjectName, DottedObjectName
31 )
33 )
32
34
33
35
34 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
35 # Helper classes for testing
37 # Helper classes for testing
36 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
37
39
38
40
39 class HasTraitsStub(HasTraits):
41 class HasTraitsStub(HasTraits):
40
42
41 def _notify_trait(self, name, old, new):
43 def _notify_trait(self, name, old, new):
42 self._notify_name = name
44 self._notify_name = name
43 self._notify_old = old
45 self._notify_old = old
44 self._notify_new = new
46 self._notify_new = new
45
47
46
48
47 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
48 # Test classes
50 # Test classes
49 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
50
52
51
53
52 class TestTraitType(TestCase):
54 class TestTraitType(TestCase):
53
55
54 def test_get_undefined(self):
56 def test_get_undefined(self):
55 class A(HasTraits):
57 class A(HasTraits):
56 a = TraitType
58 a = TraitType
57 a = A()
59 a = A()
58 self.assertEquals(a.a, Undefined)
60 self.assertEquals(a.a, Undefined)
59
61
60 def test_set(self):
62 def test_set(self):
61 class A(HasTraitsStub):
63 class A(HasTraitsStub):
62 a = TraitType
64 a = TraitType
63
65
64 a = A()
66 a = A()
65 a.a = 10
67 a.a = 10
66 self.assertEquals(a.a, 10)
68 self.assertEquals(a.a, 10)
67 self.assertEquals(a._notify_name, 'a')
69 self.assertEquals(a._notify_name, 'a')
68 self.assertEquals(a._notify_old, Undefined)
70 self.assertEquals(a._notify_old, Undefined)
69 self.assertEquals(a._notify_new, 10)
71 self.assertEquals(a._notify_new, 10)
70
72
71 def test_validate(self):
73 def test_validate(self):
72 class MyTT(TraitType):
74 class MyTT(TraitType):
73 def validate(self, inst, value):
75 def validate(self, inst, value):
74 return -1
76 return -1
75 class A(HasTraitsStub):
77 class A(HasTraitsStub):
76 tt = MyTT
78 tt = MyTT
77
79
78 a = A()
80 a = A()
79 a.tt = 10
81 a.tt = 10
80 self.assertEquals(a.tt, -1)
82 self.assertEquals(a.tt, -1)
81
83
82 def test_default_validate(self):
84 def test_default_validate(self):
83 class MyIntTT(TraitType):
85 class MyIntTT(TraitType):
84 def validate(self, obj, value):
86 def validate(self, obj, value):
85 if isinstance(value, int):
87 if isinstance(value, int):
86 return value
88 return value
87 self.error(obj, value)
89 self.error(obj, value)
88 class A(HasTraits):
90 class A(HasTraits):
89 tt = MyIntTT(10)
91 tt = MyIntTT(10)
90 a = A()
92 a = A()
91 self.assertEquals(a.tt, 10)
93 self.assertEquals(a.tt, 10)
92
94
93 # Defaults are validated when the HasTraits is instantiated
95 # Defaults are validated when the HasTraits is instantiated
94 class B(HasTraits):
96 class B(HasTraits):
95 tt = MyIntTT('bad default')
97 tt = MyIntTT('bad default')
96 self.assertRaises(TraitError, B)
98 self.assertRaises(TraitError, B)
97
99
98 def test_is_valid_for(self):
100 def test_is_valid_for(self):
99 class MyTT(TraitType):
101 class MyTT(TraitType):
100 def is_valid_for(self, value):
102 def is_valid_for(self, value):
101 return True
103 return True
102 class A(HasTraits):
104 class A(HasTraits):
103 tt = MyTT
105 tt = MyTT
104
106
105 a = A()
107 a = A()
106 a.tt = 10
108 a.tt = 10
107 self.assertEquals(a.tt, 10)
109 self.assertEquals(a.tt, 10)
108
110
109 def test_value_for(self):
111 def test_value_for(self):
110 class MyTT(TraitType):
112 class MyTT(TraitType):
111 def value_for(self, value):
113 def value_for(self, value):
112 return 20
114 return 20
113 class A(HasTraits):
115 class A(HasTraits):
114 tt = MyTT
116 tt = MyTT
115
117
116 a = A()
118 a = A()
117 a.tt = 10
119 a.tt = 10
118 self.assertEquals(a.tt, 20)
120 self.assertEquals(a.tt, 20)
119
121
120 def test_info(self):
122 def test_info(self):
121 class A(HasTraits):
123 class A(HasTraits):
122 tt = TraitType
124 tt = TraitType
123 a = A()
125 a = A()
124 self.assertEquals(A.tt.info(), 'any value')
126 self.assertEquals(A.tt.info(), 'any value')
125
127
126 def test_error(self):
128 def test_error(self):
127 class A(HasTraits):
129 class A(HasTraits):
128 tt = TraitType
130 tt = TraitType
129 a = A()
131 a = A()
130 self.assertRaises(TraitError, A.tt.error, a, 10)
132 self.assertRaises(TraitError, A.tt.error, a, 10)
131
133
132 def test_dynamic_initializer(self):
134 def test_dynamic_initializer(self):
133 class A(HasTraits):
135 class A(HasTraits):
134 x = Int(10)
136 x = Int(10)
135 def _x_default(self):
137 def _x_default(self):
136 return 11
138 return 11
137 class B(A):
139 class B(A):
138 x = Int(20)
140 x = Int(20)
139 class C(A):
141 class C(A):
140 def _x_default(self):
142 def _x_default(self):
141 return 21
143 return 21
142
144
143 a = A()
145 a = A()
144 self.assertEquals(a._trait_values, {})
146 self.assertEquals(a._trait_values, {})
145 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
147 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
146 self.assertEquals(a.x, 11)
148 self.assertEquals(a.x, 11)
147 self.assertEquals(a._trait_values, {'x': 11})
149 self.assertEquals(a._trait_values, {'x': 11})
148 b = B()
150 b = B()
149 self.assertEquals(b._trait_values, {'x': 20})
151 self.assertEquals(b._trait_values, {'x': 20})
150 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
152 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
151 self.assertEquals(b.x, 20)
153 self.assertEquals(b.x, 20)
152 c = C()
154 c = C()
153 self.assertEquals(c._trait_values, {})
155 self.assertEquals(c._trait_values, {})
154 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
156 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
155 self.assertEquals(c.x, 21)
157 self.assertEquals(c.x, 21)
156 self.assertEquals(c._trait_values, {'x': 21})
158 self.assertEquals(c._trait_values, {'x': 21})
157 # Ensure that the base class remains unmolested when the _default
159 # Ensure that the base class remains unmolested when the _default
158 # initializer gets overridden in a subclass.
160 # initializer gets overridden in a subclass.
159 a = A()
161 a = A()
160 c = C()
162 c = C()
161 self.assertEquals(a._trait_values, {})
163 self.assertEquals(a._trait_values, {})
162 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
164 self.assertEquals(a._trait_dyn_inits.keys(), ['x'])
163 self.assertEquals(a.x, 11)
165 self.assertEquals(a.x, 11)
164 self.assertEquals(a._trait_values, {'x': 11})
166 self.assertEquals(a._trait_values, {'x': 11})
165
167
166
168
167
169
168 class TestHasTraitsMeta(TestCase):
170 class TestHasTraitsMeta(TestCase):
169
171
170 def test_metaclass(self):
172 def test_metaclass(self):
171 self.assertEquals(type(HasTraits), MetaHasTraits)
173 self.assertEquals(type(HasTraits), MetaHasTraits)
172
174
173 class A(HasTraits):
175 class A(HasTraits):
174 a = Int
176 a = Int
175
177
176 a = A()
178 a = A()
177 self.assertEquals(type(a.__class__), MetaHasTraits)
179 self.assertEquals(type(a.__class__), MetaHasTraits)
178 self.assertEquals(a.a,0)
180 self.assertEquals(a.a,0)
179 a.a = 10
181 a.a = 10
180 self.assertEquals(a.a,10)
182 self.assertEquals(a.a,10)
181
183
182 class B(HasTraits):
184 class B(HasTraits):
183 b = Int()
185 b = Int()
184
186
185 b = B()
187 b = B()
186 self.assertEquals(b.b,0)
188 self.assertEquals(b.b,0)
187 b.b = 10
189 b.b = 10
188 self.assertEquals(b.b,10)
190 self.assertEquals(b.b,10)
189
191
190 class C(HasTraits):
192 class C(HasTraits):
191 c = Int(30)
193 c = Int(30)
192
194
193 c = C()
195 c = C()
194 self.assertEquals(c.c,30)
196 self.assertEquals(c.c,30)
195 c.c = 10
197 c.c = 10
196 self.assertEquals(c.c,10)
198 self.assertEquals(c.c,10)
197
199
198 def test_this_class(self):
200 def test_this_class(self):
199 class A(HasTraits):
201 class A(HasTraits):
200 t = This()
202 t = This()
201 tt = This()
203 tt = This()
202 class B(A):
204 class B(A):
203 tt = This()
205 tt = This()
204 ttt = This()
206 ttt = This()
205 self.assertEquals(A.t.this_class, A)
207 self.assertEquals(A.t.this_class, A)
206 self.assertEquals(B.t.this_class, A)
208 self.assertEquals(B.t.this_class, A)
207 self.assertEquals(B.tt.this_class, B)
209 self.assertEquals(B.tt.this_class, B)
208 self.assertEquals(B.ttt.this_class, B)
210 self.assertEquals(B.ttt.this_class, B)
209
211
210 class TestHasTraitsNotify(TestCase):
212 class TestHasTraitsNotify(TestCase):
211
213
212 def setUp(self):
214 def setUp(self):
213 self._notify1 = []
215 self._notify1 = []
214 self._notify2 = []
216 self._notify2 = []
215
217
216 def notify1(self, name, old, new):
218 def notify1(self, name, old, new):
217 self._notify1.append((name, old, new))
219 self._notify1.append((name, old, new))
218
220
219 def notify2(self, name, old, new):
221 def notify2(self, name, old, new):
220 self._notify2.append((name, old, new))
222 self._notify2.append((name, old, new))
221
223
222 def test_notify_all(self):
224 def test_notify_all(self):
223
225
224 class A(HasTraits):
226 class A(HasTraits):
225 a = Int
227 a = Int
226 b = Float
228 b = Float
227
229
228 a = A()
230 a = A()
229 a.on_trait_change(self.notify1)
231 a.on_trait_change(self.notify1)
230 a.a = 0
232 a.a = 0
231 self.assertEquals(len(self._notify1),0)
233 self.assertEquals(len(self._notify1),0)
232 a.b = 0.0
234 a.b = 0.0
233 self.assertEquals(len(self._notify1),0)
235 self.assertEquals(len(self._notify1),0)
234 a.a = 10
236 a.a = 10
235 self.assert_(('a',0,10) in self._notify1)
237 self.assert_(('a',0,10) in self._notify1)
236 a.b = 10.0
238 a.b = 10.0
237 self.assert_(('b',0.0,10.0) in self._notify1)
239 self.assert_(('b',0.0,10.0) in self._notify1)
238 self.assertRaises(TraitError,setattr,a,'a','bad string')
240 self.assertRaises(TraitError,setattr,a,'a','bad string')
239 self.assertRaises(TraitError,setattr,a,'b','bad string')
241 self.assertRaises(TraitError,setattr,a,'b','bad string')
240 self._notify1 = []
242 self._notify1 = []
241 a.on_trait_change(self.notify1,remove=True)
243 a.on_trait_change(self.notify1,remove=True)
242 a.a = 20
244 a.a = 20
243 a.b = 20.0
245 a.b = 20.0
244 self.assertEquals(len(self._notify1),0)
246 self.assertEquals(len(self._notify1),0)
245
247
246 def test_notify_one(self):
248 def test_notify_one(self):
247
249
248 class A(HasTraits):
250 class A(HasTraits):
249 a = Int
251 a = Int
250 b = Float
252 b = Float
251
253
252 a = A()
254 a = A()
253 a.on_trait_change(self.notify1, 'a')
255 a.on_trait_change(self.notify1, 'a')
254 a.a = 0
256 a.a = 0
255 self.assertEquals(len(self._notify1),0)
257 self.assertEquals(len(self._notify1),0)
256 a.a = 10
258 a.a = 10
257 self.assert_(('a',0,10) in self._notify1)
259 self.assert_(('a',0,10) in self._notify1)
258 self.assertRaises(TraitError,setattr,a,'a','bad string')
260 self.assertRaises(TraitError,setattr,a,'a','bad string')
259
261
260 def test_subclass(self):
262 def test_subclass(self):
261
263
262 class A(HasTraits):
264 class A(HasTraits):
263 a = Int
265 a = Int
264
266
265 class B(A):
267 class B(A):
266 b = Float
268 b = Float
267
269
268 b = B()
270 b = B()
269 self.assertEquals(b.a,0)
271 self.assertEquals(b.a,0)
270 self.assertEquals(b.b,0.0)
272 self.assertEquals(b.b,0.0)
271 b.a = 100
273 b.a = 100
272 b.b = 100.0
274 b.b = 100.0
273 self.assertEquals(b.a,100)
275 self.assertEquals(b.a,100)
274 self.assertEquals(b.b,100.0)
276 self.assertEquals(b.b,100.0)
275
277
276 def test_notify_subclass(self):
278 def test_notify_subclass(self):
277
279
278 class A(HasTraits):
280 class A(HasTraits):
279 a = Int
281 a = Int
280
282
281 class B(A):
283 class B(A):
282 b = Float
284 b = Float
283
285
284 b = B()
286 b = B()
285 b.on_trait_change(self.notify1, 'a')
287 b.on_trait_change(self.notify1, 'a')
286 b.on_trait_change(self.notify2, 'b')
288 b.on_trait_change(self.notify2, 'b')
287 b.a = 0
289 b.a = 0
288 b.b = 0.0
290 b.b = 0.0
289 self.assertEquals(len(self._notify1),0)
291 self.assertEquals(len(self._notify1),0)
290 self.assertEquals(len(self._notify2),0)
292 self.assertEquals(len(self._notify2),0)
291 b.a = 10
293 b.a = 10
292 b.b = 10.0
294 b.b = 10.0
293 self.assert_(('a',0,10) in self._notify1)
295 self.assert_(('a',0,10) in self._notify1)
294 self.assert_(('b',0.0,10.0) in self._notify2)
296 self.assert_(('b',0.0,10.0) in self._notify2)
295
297
296 def test_static_notify(self):
298 def test_static_notify(self):
297
299
298 class A(HasTraits):
300 class A(HasTraits):
299 a = Int
301 a = Int
300 _notify1 = []
302 _notify1 = []
301 def _a_changed(self, name, old, new):
303 def _a_changed(self, name, old, new):
302 self._notify1.append((name, old, new))
304 self._notify1.append((name, old, new))
303
305
304 a = A()
306 a = A()
305 a.a = 0
307 a.a = 0
306 # This is broken!!!
308 # This is broken!!!
307 self.assertEquals(len(a._notify1),0)
309 self.assertEquals(len(a._notify1),0)
308 a.a = 10
310 a.a = 10
309 self.assert_(('a',0,10) in a._notify1)
311 self.assert_(('a',0,10) in a._notify1)
310
312
311 class B(A):
313 class B(A):
312 b = Float
314 b = Float
313 _notify2 = []
315 _notify2 = []
314 def _b_changed(self, name, old, new):
316 def _b_changed(self, name, old, new):
315 self._notify2.append((name, old, new))
317 self._notify2.append((name, old, new))
316
318
317 b = B()
319 b = B()
318 b.a = 10
320 b.a = 10
319 b.b = 10.0
321 b.b = 10.0
320 self.assert_(('a',0,10) in b._notify1)
322 self.assert_(('a',0,10) in b._notify1)
321 self.assert_(('b',0.0,10.0) in b._notify2)
323 self.assert_(('b',0.0,10.0) in b._notify2)
322
324
323 def test_notify_args(self):
325 def test_notify_args(self):
324
326
325 def callback0():
327 def callback0():
326 self.cb = ()
328 self.cb = ()
327 def callback1(name):
329 def callback1(name):
328 self.cb = (name,)
330 self.cb = (name,)
329 def callback2(name, new):
331 def callback2(name, new):
330 self.cb = (name, new)
332 self.cb = (name, new)
331 def callback3(name, old, new):
333 def callback3(name, old, new):
332 self.cb = (name, old, new)
334 self.cb = (name, old, new)
333
335
334 class A(HasTraits):
336 class A(HasTraits):
335 a = Int
337 a = Int
336
338
337 a = A()
339 a = A()
338 a.on_trait_change(callback0, 'a')
340 a.on_trait_change(callback0, 'a')
339 a.a = 10
341 a.a = 10
340 self.assertEquals(self.cb,())
342 self.assertEquals(self.cb,())
341 a.on_trait_change(callback0, 'a', remove=True)
343 a.on_trait_change(callback0, 'a', remove=True)
342
344
343 a.on_trait_change(callback1, 'a')
345 a.on_trait_change(callback1, 'a')
344 a.a = 100
346 a.a = 100
345 self.assertEquals(self.cb,('a',))
347 self.assertEquals(self.cb,('a',))
346 a.on_trait_change(callback1, 'a', remove=True)
348 a.on_trait_change(callback1, 'a', remove=True)
347
349
348 a.on_trait_change(callback2, 'a')
350 a.on_trait_change(callback2, 'a')
349 a.a = 1000
351 a.a = 1000
350 self.assertEquals(self.cb,('a',1000))
352 self.assertEquals(self.cb,('a',1000))
351 a.on_trait_change(callback2, 'a', remove=True)
353 a.on_trait_change(callback2, 'a', remove=True)
352
354
353 a.on_trait_change(callback3, 'a')
355 a.on_trait_change(callback3, 'a')
354 a.a = 10000
356 a.a = 10000
355 self.assertEquals(self.cb,('a',1000,10000))
357 self.assertEquals(self.cb,('a',1000,10000))
356 a.on_trait_change(callback3, 'a', remove=True)
358 a.on_trait_change(callback3, 'a', remove=True)
357
359
358 self.assertEquals(len(a._trait_notifiers['a']),0)
360 self.assertEquals(len(a._trait_notifiers['a']),0)
359
361
360
362
361 class TestHasTraits(TestCase):
363 class TestHasTraits(TestCase):
362
364
363 def test_trait_names(self):
365 def test_trait_names(self):
364 class A(HasTraits):
366 class A(HasTraits):
365 i = Int
367 i = Int
366 f = Float
368 f = Float
367 a = A()
369 a = A()
368 self.assertEquals(a.trait_names(),['i','f'])
370 self.assertEquals(a.trait_names(),['i','f'])
369 self.assertEquals(A.class_trait_names(),['i','f'])
371 self.assertEquals(A.class_trait_names(),['i','f'])
370
372
371 def test_trait_metadata(self):
373 def test_trait_metadata(self):
372 class A(HasTraits):
374 class A(HasTraits):
373 i = Int(config_key='MY_VALUE')
375 i = Int(config_key='MY_VALUE')
374 a = A()
376 a = A()
375 self.assertEquals(a.trait_metadata('i','config_key'), 'MY_VALUE')
377 self.assertEquals(a.trait_metadata('i','config_key'), 'MY_VALUE')
376
378
377 def test_traits(self):
379 def test_traits(self):
378 class A(HasTraits):
380 class A(HasTraits):
379 i = Int
381 i = Int
380 f = Float
382 f = Float
381 a = A()
383 a = A()
382 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
384 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
383 self.assertEquals(A.class_traits(), dict(i=A.i, f=A.f))
385 self.assertEquals(A.class_traits(), dict(i=A.i, f=A.f))
384
386
385 def test_traits_metadata(self):
387 def test_traits_metadata(self):
386 class A(HasTraits):
388 class A(HasTraits):
387 i = Int(config_key='VALUE1', other_thing='VALUE2')
389 i = Int(config_key='VALUE1', other_thing='VALUE2')
388 f = Float(config_key='VALUE3', other_thing='VALUE2')
390 f = Float(config_key='VALUE3', other_thing='VALUE2')
389 j = Int(0)
391 j = Int(0)
390 a = A()
392 a = A()
391 self.assertEquals(a.traits(), dict(i=A.i, f=A.f, j=A.j))
393 self.assertEquals(a.traits(), dict(i=A.i, f=A.f, j=A.j))
392 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
394 traits = a.traits(config_key='VALUE1', other_thing='VALUE2')
393 self.assertEquals(traits, dict(i=A.i))
395 self.assertEquals(traits, dict(i=A.i))
394
396
395 # This passes, but it shouldn't because I am replicating a bug in
397 # This passes, but it shouldn't because I am replicating a bug in
396 # traits.
398 # traits.
397 traits = a.traits(config_key=lambda v: True)
399 traits = a.traits(config_key=lambda v: True)
398 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
400 self.assertEquals(traits, dict(i=A.i, f=A.f, j=A.j))
399
401
400 def test_init(self):
402 def test_init(self):
401 class A(HasTraits):
403 class A(HasTraits):
402 i = Int()
404 i = Int()
403 x = Float()
405 x = Float()
404 a = A(i=1, x=10.0)
406 a = A(i=1, x=10.0)
405 self.assertEquals(a.i, 1)
407 self.assertEquals(a.i, 1)
406 self.assertEquals(a.x, 10.0)
408 self.assertEquals(a.x, 10.0)
407
409
408 #-----------------------------------------------------------------------------
410 #-----------------------------------------------------------------------------
409 # Tests for specific trait types
411 # Tests for specific trait types
410 #-----------------------------------------------------------------------------
412 #-----------------------------------------------------------------------------
411
413
412
414
413 class TestType(TestCase):
415 class TestType(TestCase):
414
416
415 def test_default(self):
417 def test_default(self):
416
418
417 class B(object): pass
419 class B(object): pass
418 class A(HasTraits):
420 class A(HasTraits):
419 klass = Type
421 klass = Type
420
422
421 a = A()
423 a = A()
422 self.assertEquals(a.klass, None)
424 self.assertEquals(a.klass, None)
423
425
424 a.klass = B
426 a.klass = B
425 self.assertEquals(a.klass, B)
427 self.assertEquals(a.klass, B)
426 self.assertRaises(TraitError, setattr, a, 'klass', 10)
428 self.assertRaises(TraitError, setattr, a, 'klass', 10)
427
429
428 def test_value(self):
430 def test_value(self):
429
431
430 class B(object): pass
432 class B(object): pass
431 class C(object): pass
433 class C(object): pass
432 class A(HasTraits):
434 class A(HasTraits):
433 klass = Type(B)
435 klass = Type(B)
434
436
435 a = A()
437 a = A()
436 self.assertEquals(a.klass, B)
438 self.assertEquals(a.klass, B)
437 self.assertRaises(TraitError, setattr, a, 'klass', C)
439 self.assertRaises(TraitError, setattr, a, 'klass', C)
438 self.assertRaises(TraitError, setattr, a, 'klass', object)
440 self.assertRaises(TraitError, setattr, a, 'klass', object)
439 a.klass = B
441 a.klass = B
440
442
441 def test_allow_none(self):
443 def test_allow_none(self):
442
444
443 class B(object): pass
445 class B(object): pass
444 class C(B): pass
446 class C(B): pass
445 class A(HasTraits):
447 class A(HasTraits):
446 klass = Type(B, allow_none=False)
448 klass = Type(B, allow_none=False)
447
449
448 a = A()
450 a = A()
449 self.assertEquals(a.klass, B)
451 self.assertEquals(a.klass, B)
450 self.assertRaises(TraitError, setattr, a, 'klass', None)
452 self.assertRaises(TraitError, setattr, a, 'klass', None)
451 a.klass = C
453 a.klass = C
452 self.assertEquals(a.klass, C)
454 self.assertEquals(a.klass, C)
453
455
454 def test_validate_klass(self):
456 def test_validate_klass(self):
455
457
456 class A(HasTraits):
458 class A(HasTraits):
457 klass = Type('no strings allowed')
459 klass = Type('no strings allowed')
458
460
459 self.assertRaises(ImportError, A)
461 self.assertRaises(ImportError, A)
460
462
461 class A(HasTraits):
463 class A(HasTraits):
462 klass = Type('rub.adub.Duck')
464 klass = Type('rub.adub.Duck')
463
465
464 self.assertRaises(ImportError, A)
466 self.assertRaises(ImportError, A)
465
467
466 def test_validate_default(self):
468 def test_validate_default(self):
467
469
468 class B(object): pass
470 class B(object): pass
469 class A(HasTraits):
471 class A(HasTraits):
470 klass = Type('bad default', B)
472 klass = Type('bad default', B)
471
473
472 self.assertRaises(ImportError, A)
474 self.assertRaises(ImportError, A)
473
475
474 class C(HasTraits):
476 class C(HasTraits):
475 klass = Type(None, B, allow_none=False)
477 klass = Type(None, B, allow_none=False)
476
478
477 self.assertRaises(TraitError, C)
479 self.assertRaises(TraitError, C)
478
480
479 def test_str_klass(self):
481 def test_str_klass(self):
480
482
481 class A(HasTraits):
483 class A(HasTraits):
482 klass = Type('IPython.utils.ipstruct.Struct')
484 klass = Type('IPython.utils.ipstruct.Struct')
483
485
484 from IPython.utils.ipstruct import Struct
486 from IPython.utils.ipstruct import Struct
485 a = A()
487 a = A()
486 a.klass = Struct
488 a.klass = Struct
487 self.assertEquals(a.klass, Struct)
489 self.assertEquals(a.klass, Struct)
488
490
489 self.assertRaises(TraitError, setattr, a, 'klass', 10)
491 self.assertRaises(TraitError, setattr, a, 'klass', 10)
490
492
491 class TestInstance(TestCase):
493 class TestInstance(TestCase):
492
494
493 def test_basic(self):
495 def test_basic(self):
494 class Foo(object): pass
496 class Foo(object): pass
495 class Bar(Foo): pass
497 class Bar(Foo): pass
496 class Bah(object): pass
498 class Bah(object): pass
497
499
498 class A(HasTraits):
500 class A(HasTraits):
499 inst = Instance(Foo)
501 inst = Instance(Foo)
500
502
501 a = A()
503 a = A()
502 self.assert_(a.inst is None)
504 self.assert_(a.inst is None)
503 a.inst = Foo()
505 a.inst = Foo()
504 self.assert_(isinstance(a.inst, Foo))
506 self.assert_(isinstance(a.inst, Foo))
505 a.inst = Bar()
507 a.inst = Bar()
506 self.assert_(isinstance(a.inst, Foo))
508 self.assert_(isinstance(a.inst, Foo))
507 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
509 self.assertRaises(TraitError, setattr, a, 'inst', Foo)
508 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
510 self.assertRaises(TraitError, setattr, a, 'inst', Bar)
509 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
511 self.assertRaises(TraitError, setattr, a, 'inst', Bah())
510
512
511 def test_unique_default_value(self):
513 def test_unique_default_value(self):
512 class Foo(object): pass
514 class Foo(object): pass
513 class A(HasTraits):
515 class A(HasTraits):
514 inst = Instance(Foo,(),{})
516 inst = Instance(Foo,(),{})
515
517
516 a = A()
518 a = A()
517 b = A()
519 b = A()
518 self.assert_(a.inst is not b.inst)
520 self.assert_(a.inst is not b.inst)
519
521
520 def test_args_kw(self):
522 def test_args_kw(self):
521 class Foo(object):
523 class Foo(object):
522 def __init__(self, c): self.c = c
524 def __init__(self, c): self.c = c
523 class Bar(object): pass
525 class Bar(object): pass
524 class Bah(object):
526 class Bah(object):
525 def __init__(self, c, d):
527 def __init__(self, c, d):
526 self.c = c; self.d = d
528 self.c = c; self.d = d
527
529
528 class A(HasTraits):
530 class A(HasTraits):
529 inst = Instance(Foo, (10,))
531 inst = Instance(Foo, (10,))
530 a = A()
532 a = A()
531 self.assertEquals(a.inst.c, 10)
533 self.assertEquals(a.inst.c, 10)
532
534
533 class B(HasTraits):
535 class B(HasTraits):
534 inst = Instance(Bah, args=(10,), kw=dict(d=20))
536 inst = Instance(Bah, args=(10,), kw=dict(d=20))
535 b = B()
537 b = B()
536 self.assertEquals(b.inst.c, 10)
538 self.assertEquals(b.inst.c, 10)
537 self.assertEquals(b.inst.d, 20)
539 self.assertEquals(b.inst.d, 20)
538
540
539 class C(HasTraits):
541 class C(HasTraits):
540 inst = Instance(Foo)
542 inst = Instance(Foo)
541 c = C()
543 c = C()
542 self.assert_(c.inst is None)
544 self.assert_(c.inst is None)
543
545
544 def test_bad_default(self):
546 def test_bad_default(self):
545 class Foo(object): pass
547 class Foo(object): pass
546
548
547 class A(HasTraits):
549 class A(HasTraits):
548 inst = Instance(Foo, allow_none=False)
550 inst = Instance(Foo, allow_none=False)
549
551
550 self.assertRaises(TraitError, A)
552 self.assertRaises(TraitError, A)
551
553
552 def test_instance(self):
554 def test_instance(self):
553 class Foo(object): pass
555 class Foo(object): pass
554
556
555 def inner():
557 def inner():
556 class A(HasTraits):
558 class A(HasTraits):
557 inst = Instance(Foo())
559 inst = Instance(Foo())
558
560
559 self.assertRaises(TraitError, inner)
561 self.assertRaises(TraitError, inner)
560
562
561
563
562 class TestThis(TestCase):
564 class TestThis(TestCase):
563
565
564 def test_this_class(self):
566 def test_this_class(self):
565 class Foo(HasTraits):
567 class Foo(HasTraits):
566 this = This
568 this = This
567
569
568 f = Foo()
570 f = Foo()
569 self.assertEquals(f.this, None)
571 self.assertEquals(f.this, None)
570 g = Foo()
572 g = Foo()
571 f.this = g
573 f.this = g
572 self.assertEquals(f.this, g)
574 self.assertEquals(f.this, g)
573 self.assertRaises(TraitError, setattr, f, 'this', 10)
575 self.assertRaises(TraitError, setattr, f, 'this', 10)
574
576
575 def test_this_inst(self):
577 def test_this_inst(self):
576 class Foo(HasTraits):
578 class Foo(HasTraits):
577 this = This()
579 this = This()
578
580
579 f = Foo()
581 f = Foo()
580 f.this = Foo()
582 f.this = Foo()
581 self.assert_(isinstance(f.this, Foo))
583 self.assert_(isinstance(f.this, Foo))
582
584
583 def test_subclass(self):
585 def test_subclass(self):
584 class Foo(HasTraits):
586 class Foo(HasTraits):
585 t = This()
587 t = This()
586 class Bar(Foo):
588 class Bar(Foo):
587 pass
589 pass
588 f = Foo()
590 f = Foo()
589 b = Bar()
591 b = Bar()
590 f.t = b
592 f.t = b
591 b.t = f
593 b.t = f
592 self.assertEquals(f.t, b)
594 self.assertEquals(f.t, b)
593 self.assertEquals(b.t, f)
595 self.assertEquals(b.t, f)
594
596
595 def test_subclass_override(self):
597 def test_subclass_override(self):
596 class Foo(HasTraits):
598 class Foo(HasTraits):
597 t = This()
599 t = This()
598 class Bar(Foo):
600 class Bar(Foo):
599 t = This()
601 t = This()
600 f = Foo()
602 f = Foo()
601 b = Bar()
603 b = Bar()
602 f.t = b
604 f.t = b
603 self.assertEquals(f.t, b)
605 self.assertEquals(f.t, b)
604 self.assertRaises(TraitError, setattr, b, 't', f)
606 self.assertRaises(TraitError, setattr, b, 't', f)
605
607
606 class TraitTestBase(TestCase):
608 class TraitTestBase(TestCase):
607 """A best testing class for basic trait types."""
609 """A best testing class for basic trait types."""
608
610
609 def assign(self, value):
611 def assign(self, value):
610 self.obj.value = value
612 self.obj.value = value
611
613
612 def coerce(self, value):
614 def coerce(self, value):
613 return value
615 return value
614
616
615 def test_good_values(self):
617 def test_good_values(self):
616 if hasattr(self, '_good_values'):
618 if hasattr(self, '_good_values'):
617 for value in self._good_values:
619 for value in self._good_values:
618 self.assign(value)
620 self.assign(value)
619 self.assertEquals(self.obj.value, self.coerce(value))
621 self.assertEquals(self.obj.value, self.coerce(value))
620
622
621 def test_bad_values(self):
623 def test_bad_values(self):
622 if hasattr(self, '_bad_values'):
624 if hasattr(self, '_bad_values'):
623 for value in self._bad_values:
625 for value in self._bad_values:
624 self.assertRaises(TraitError, self.assign, value)
626 self.assertRaises(TraitError, self.assign, value)
625
627
626 def test_default_value(self):
628 def test_default_value(self):
627 if hasattr(self, '_default_value'):
629 if hasattr(self, '_default_value'):
628 self.assertEquals(self._default_value, self.obj.value)
630 self.assertEquals(self._default_value, self.obj.value)
629
631
630
632
631 class AnyTrait(HasTraits):
633 class AnyTrait(HasTraits):
632
634
633 value = Any
635 value = Any
634
636
635 class AnyTraitTest(TraitTestBase):
637 class AnyTraitTest(TraitTestBase):
636
638
637 obj = AnyTrait()
639 obj = AnyTrait()
638
640
639 _default_value = None
641 _default_value = None
640 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
642 _good_values = [10.0, 'ten', u'ten', [10], {'ten': 10},(10,), None, 1j]
641 _bad_values = []
643 _bad_values = []
642
644
643
645
644 class IntTrait(HasTraits):
646 class IntTrait(HasTraits):
645
647
646 value = Int(99)
648 value = Int(99)
647
649
648 class TestInt(TraitTestBase):
650 class TestInt(TraitTestBase):
649
651
650 obj = IntTrait()
652 obj = IntTrait()
651 _default_value = 99
653 _default_value = 99
652 _good_values = [10, -10]
654 _good_values = [10, -10]
653 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
655 _bad_values = ['ten', u'ten', [10], {'ten': 10},(10,), None, 1j, 10L,
654 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
656 -10L, 10.1, -10.1, '10L', '-10L', '10.1', '-10.1', u'10L',
655 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
657 u'-10L', u'10.1', u'-10.1', '10', '-10', u'10', u'-10']
656
658
657
659
658 class LongTrait(HasTraits):
660 class LongTrait(HasTraits):
659
661
660 value = Long(99L)
662 value = Long(99L)
661
663
662 class TestLong(TraitTestBase):
664 class TestLong(TraitTestBase):
663
665
664 obj = LongTrait()
666 obj = LongTrait()
665
667
666 _default_value = 99L
668 _default_value = 99L
667 _good_values = [10, -10, 10L, -10L]
669 _good_values = [10, -10, 10L, -10L]
668 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
670 _bad_values = ['ten', u'ten', [10], [10l], {'ten': 10},(10,),(10L,),
669 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
671 None, 1j, 10.1, -10.1, '10', '-10', '10L', '-10L', '10.1',
670 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
672 '-10.1', u'10', u'-10', u'10L', u'-10L', u'10.1',
671 u'-10.1']
673 u'-10.1']
672
674
673
675
674 class FloatTrait(HasTraits):
676 class FloatTrait(HasTraits):
675
677
676 value = Float(99.0)
678 value = Float(99.0)
677
679
678 class TestFloat(TraitTestBase):
680 class TestFloat(TraitTestBase):
679
681
680 obj = FloatTrait()
682 obj = FloatTrait()
681
683
682 _default_value = 99.0
684 _default_value = 99.0
683 _good_values = [10, -10, 10.1, -10.1]
685 _good_values = [10, -10, 10.1, -10.1]
684 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
686 _bad_values = [10L, -10L, 'ten', u'ten', [10], {'ten': 10},(10,), None,
685 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
687 1j, '10', '-10', '10L', '-10L', '10.1', '-10.1', u'10',
686 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
688 u'-10', u'10L', u'-10L', u'10.1', u'-10.1']
687
689
688
690
689 class ComplexTrait(HasTraits):
691 class ComplexTrait(HasTraits):
690
692
691 value = Complex(99.0-99.0j)
693 value = Complex(99.0-99.0j)
692
694
693 class TestComplex(TraitTestBase):
695 class TestComplex(TraitTestBase):
694
696
695 obj = ComplexTrait()
697 obj = ComplexTrait()
696
698
697 _default_value = 99.0-99.0j
699 _default_value = 99.0-99.0j
698 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
700 _good_values = [10, -10, 10.1, -10.1, 10j, 10+10j, 10-10j,
699 10.1j, 10.1+10.1j, 10.1-10.1j]
701 10.1j, 10.1+10.1j, 10.1-10.1j]
700 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
702 _bad_values = [10L, -10L, u'10L', u'-10L', 'ten', [10], {'ten': 10},(10,), None]
701
703
702
704
703 class BytesTrait(HasTraits):
705 class BytesTrait(HasTraits):
704
706
705 value = Bytes('string')
707 value = Bytes('string')
706
708
707 class TestBytes(TraitTestBase):
709 class TestBytes(TraitTestBase):
708
710
709 obj = BytesTrait()
711 obj = BytesTrait()
710
712
711 _default_value = 'string'
713 _default_value = 'string'
712 _good_values = ['10', '-10', '10L',
714 _good_values = ['10', '-10', '10L',
713 '-10L', '10.1', '-10.1', 'string']
715 '-10L', '10.1', '-10.1', 'string']
714 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
716 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j, [10],
715 ['ten'],{'ten': 10},(10,), None, u'string']
717 ['ten'],{'ten': 10},(10,), None, u'string']
716
718
717
719
718 class UnicodeTrait(HasTraits):
720 class UnicodeTrait(HasTraits):
719
721
720 value = Unicode(u'unicode')
722 value = Unicode(u'unicode')
721
723
722 class TestUnicode(TraitTestBase):
724 class TestUnicode(TraitTestBase):
723
725
724 obj = UnicodeTrait()
726 obj = UnicodeTrait()
725
727
726 _default_value = u'unicode'
728 _default_value = u'unicode'
727 _good_values = ['10', '-10', '10L', '-10L', '10.1',
729 _good_values = ['10', '-10', '10L', '-10L', '10.1',
728 '-10.1', '', u'', 'string', u'string', ]
730 '-10.1', '', u'', 'string', u'string', u"€"]
729 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
731 _bad_values = [10, -10, 10L, -10L, 10.1, -10.1, 1j,
730 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
732 [10], ['ten'], [u'ten'], {'ten': 10},(10,), None]
731
733
732
734
735 class ObjectNameTrait(HasTraits):
736 value = ObjectName("abc")
737
738 class TestObjectName(TraitTestBase):
739 obj = ObjectNameTrait()
740
741 _default_value = "abc"
742 _good_values = ["a", "gh", "g9", "g_", "_G", u"a345_"]
743 _bad_values = [1, "", u"€", "9g", "!", "#abc", "aj@", "a.b", "a()", "a[0]",
744 object(), object]
745 if sys.version_info[0] < 3:
746 _bad_values.append(u"ΓΎ")
747 else:
748 _good_values.append(u"ΓΎ") # ΓΎ=1 is valid in Python 3 (PEP 3131).
749
750
751 class DottedObjectNameTrait(HasTraits):
752 value = DottedObjectName("a.b")
753
754 class TestDottedObjectName(TraitTestBase):
755 obj = DottedObjectNameTrait()
756
757 _default_value = "a.b"
758 _good_values = ["A", "y.t", "y765.__repr__", "os.path.join", u"os.path.join"]
759 _bad_values = [1, u"abc.€", "_.@", ".", ".abc", "abc.", ".abc."]
760 if sys.version_info[0] < 3:
761 _bad_values.append(u"t.ΓΎ")
762 else:
763 _good_values.append(u"t.ΓΎ")
764
765
733 class TCPAddressTrait(HasTraits):
766 class TCPAddressTrait(HasTraits):
734
767
735 value = TCPAddress()
768 value = TCPAddress()
736
769
737 class TestTCPAddress(TraitTestBase):
770 class TestTCPAddress(TraitTestBase):
738
771
739 obj = TCPAddressTrait()
772 obj = TCPAddressTrait()
740
773
741 _default_value = ('127.0.0.1',0)
774 _default_value = ('127.0.0.1',0)
742 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
775 _good_values = [('localhost',0),('192.168.0.1',1000),('www.google.com',80)]
743 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
776 _bad_values = [(0,0),('localhost',10.0),('localhost',-1)]
744
777
745 class ListTrait(HasTraits):
778 class ListTrait(HasTraits):
746
779
747 value = List(Int)
780 value = List(Int)
748
781
749 class TestList(TraitTestBase):
782 class TestList(TraitTestBase):
750
783
751 obj = ListTrait()
784 obj = ListTrait()
752
785
753 _default_value = []
786 _default_value = []
754 _good_values = [[], [1], range(10)]
787 _good_values = [[], [1], range(10)]
755 _bad_values = [10, [1,'a'], 'a', (1,2)]
788 _bad_values = [10, [1,'a'], 'a', (1,2)]
756
789
757 class LenListTrait(HasTraits):
790 class LenListTrait(HasTraits):
758
791
759 value = List(Int, [0], minlen=1, maxlen=2)
792 value = List(Int, [0], minlen=1, maxlen=2)
760
793
761 class TestLenList(TraitTestBase):
794 class TestLenList(TraitTestBase):
762
795
763 obj = LenListTrait()
796 obj = LenListTrait()
764
797
765 _default_value = [0]
798 _default_value = [0]
766 _good_values = [[1], range(2)]
799 _good_values = [[1], range(2)]
767 _bad_values = [10, [1,'a'], 'a', (1,2), [], range(3)]
800 _bad_values = [10, [1,'a'], 'a', (1,2), [], range(3)]
768
801
769 class TupleTrait(HasTraits):
802 class TupleTrait(HasTraits):
770
803
771 value = Tuple(Int)
804 value = Tuple(Int)
772
805
773 class TestTupleTrait(TraitTestBase):
806 class TestTupleTrait(TraitTestBase):
774
807
775 obj = TupleTrait()
808 obj = TupleTrait()
776
809
777 _default_value = None
810 _default_value = None
778 _good_values = [(1,), None,(0,)]
811 _good_values = [(1,), None,(0,)]
779 _bad_values = [10, (1,2), [1],('a'), ()]
812 _bad_values = [10, (1,2), [1],('a'), ()]
780
813
781 def test_invalid_args(self):
814 def test_invalid_args(self):
782 self.assertRaises(TypeError, Tuple, 5)
815 self.assertRaises(TypeError, Tuple, 5)
783 self.assertRaises(TypeError, Tuple, default_value='hello')
816 self.assertRaises(TypeError, Tuple, default_value='hello')
784 t = Tuple(Int, CBytes, default_value=(1,5))
817 t = Tuple(Int, CBytes, default_value=(1,5))
785
818
786 class LooseTupleTrait(HasTraits):
819 class LooseTupleTrait(HasTraits):
787
820
788 value = Tuple((1,2,3))
821 value = Tuple((1,2,3))
789
822
790 class TestLooseTupleTrait(TraitTestBase):
823 class TestLooseTupleTrait(TraitTestBase):
791
824
792 obj = LooseTupleTrait()
825 obj = LooseTupleTrait()
793
826
794 _default_value = (1,2,3)
827 _default_value = (1,2,3)
795 _good_values = [(1,), None, (0,), tuple(range(5)), tuple('hello'), ('a',5), ()]
828 _good_values = [(1,), None, (0,), tuple(range(5)), tuple('hello'), ('a',5), ()]
796 _bad_values = [10, 'hello', [1], []]
829 _bad_values = [10, 'hello', [1], []]
797
830
798 def test_invalid_args(self):
831 def test_invalid_args(self):
799 self.assertRaises(TypeError, Tuple, 5)
832 self.assertRaises(TypeError, Tuple, 5)
800 self.assertRaises(TypeError, Tuple, default_value='hello')
833 self.assertRaises(TypeError, Tuple, default_value='hello')
801 t = Tuple(Int, CBytes, default_value=(1,5))
834 t = Tuple(Int, CBytes, default_value=(1,5))
802
835
803
836
804 class MultiTupleTrait(HasTraits):
837 class MultiTupleTrait(HasTraits):
805
838
806 value = Tuple(Int, Bytes, default_value=[99,'bottles'])
839 value = Tuple(Int, Bytes, default_value=[99,'bottles'])
807
840
808 class TestMultiTuple(TraitTestBase):
841 class TestMultiTuple(TraitTestBase):
809
842
810 obj = MultiTupleTrait()
843 obj = MultiTupleTrait()
811
844
812 _default_value = (99,'bottles')
845 _default_value = (99,'bottles')
813 _good_values = [(1,'a'), (2,'b')]
846 _good_values = [(1,'a'), (2,'b')]
814 _bad_values = ((),10, 'a', (1,'a',3), ('a',1))
847 _bad_values = ((),10, 'a', (1,'a',3), ('a',1))
@@ -1,1352 +1,1397 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight Traits like module.
4 A lightweight Traits like module.
5
5
6 This is designed to provide a lightweight, simple, pure Python version of
6 This is designed to provide a lightweight, simple, pure Python version of
7 many of the capabilities of enthought.traits. This includes:
7 many of the capabilities of enthought.traits. This includes:
8
8
9 * Validation
9 * Validation
10 * Type specification with defaults
10 * Type specification with defaults
11 * Static and dynamic notification
11 * Static and dynamic notification
12 * Basic predefined types
12 * Basic predefined types
13 * An API that is similar to enthought.traits
13 * An API that is similar to enthought.traits
14
14
15 We don't support:
15 We don't support:
16
16
17 * Delegation
17 * Delegation
18 * Automatic GUI generation
18 * Automatic GUI generation
19 * A full set of trait types. Most importantly, we don't provide container
19 * A full set of trait types. Most importantly, we don't provide container
20 traits (list, dict, tuple) that can trigger notifications if their
20 traits (list, dict, tuple) that can trigger notifications if their
21 contents change.
21 contents change.
22 * API compatibility with enthought.traits
22 * API compatibility with enthought.traits
23
23
24 There are also some important difference in our design:
24 There are also some important difference in our design:
25
25
26 * enthought.traits does not validate default values. We do.
26 * enthought.traits does not validate default values. We do.
27
27
28 We choose to create this module because we need these capabilities, but
28 We choose to create this module because we need these capabilities, but
29 we need them to be pure Python so they work in all Python implementations,
29 we need them to be pure Python so they work in all Python implementations,
30 including Jython and IronPython.
30 including Jython and IronPython.
31
31
32 Authors:
32 Authors:
33
33
34 * Brian Granger
34 * Brian Granger
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
35 * Enthought, Inc. Some of the code in this file comes from enthought.traits
36 and is licensed under the BSD license. Also, many of the ideas also come
36 and is licensed under the BSD license. Also, many of the ideas also come
37 from enthought.traits even though our implementation is very different.
37 from enthought.traits even though our implementation is very different.
38 """
38 """
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Copyright (C) 2008-2009 The IPython Development Team
41 # Copyright (C) 2008-2009 The IPython Development Team
42 #
42 #
43 # Distributed under the terms of the BSD License. The full license is in
43 # Distributed under the terms of the BSD License. The full license is in
44 # the file COPYING, distributed as part of this software.
44 # the file COPYING, distributed as part of this software.
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Imports
48 # Imports
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 import inspect
52 import inspect
53 import re
53 import sys
54 import sys
54 import types
55 import types
55 from types import (
56 from types import (
56 InstanceType, ClassType, FunctionType,
57 InstanceType, ClassType, FunctionType,
57 ListType, TupleType
58 ListType, TupleType
58 )
59 )
59 from .importstring import import_item
60 from .importstring import import_item
60
61
61 ClassTypes = (ClassType, type)
62 ClassTypes = (ClassType, type)
62
63
63 SequenceTypes = (ListType, TupleType, set, frozenset)
64 SequenceTypes = (ListType, TupleType, set, frozenset)
64
65
65 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
66 # Basic classes
67 # Basic classes
67 #-----------------------------------------------------------------------------
68 #-----------------------------------------------------------------------------
68
69
69
70
70 class NoDefaultSpecified ( object ): pass
71 class NoDefaultSpecified ( object ): pass
71 NoDefaultSpecified = NoDefaultSpecified()
72 NoDefaultSpecified = NoDefaultSpecified()
72
73
73
74
74 class Undefined ( object ): pass
75 class Undefined ( object ): pass
75 Undefined = Undefined()
76 Undefined = Undefined()
76
77
77 class TraitError(Exception):
78 class TraitError(Exception):
78 pass
79 pass
79
80
80 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
81 # Utilities
82 # Utilities
82 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
83
84
84
85
85 def class_of ( object ):
86 def class_of ( object ):
86 """ Returns a string containing the class name of an object with the
87 """ Returns a string containing the class name of an object with the
87 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
88 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
88 'a PlotValue').
89 'a PlotValue').
89 """
90 """
90 if isinstance( object, basestring ):
91 if isinstance( object, basestring ):
91 return add_article( object )
92 return add_article( object )
92
93
93 return add_article( object.__class__.__name__ )
94 return add_article( object.__class__.__name__ )
94
95
95
96
96 def add_article ( name ):
97 def add_article ( name ):
97 """ Returns a string containing the correct indefinite article ('a' or 'an')
98 """ Returns a string containing the correct indefinite article ('a' or 'an')
98 prefixed to the specified string.
99 prefixed to the specified string.
99 """
100 """
100 if name[:1].lower() in 'aeiou':
101 if name[:1].lower() in 'aeiou':
101 return 'an ' + name
102 return 'an ' + name
102
103
103 return 'a ' + name
104 return 'a ' + name
104
105
105
106
106 def repr_type(obj):
107 def repr_type(obj):
107 """ Return a string representation of a value and its type for readable
108 """ Return a string representation of a value and its type for readable
108 error messages.
109 error messages.
109 """
110 """
110 the_type = type(obj)
111 the_type = type(obj)
111 if the_type is InstanceType:
112 if the_type is InstanceType:
112 # Old-style class.
113 # Old-style class.
113 the_type = obj.__class__
114 the_type = obj.__class__
114 msg = '%r %r' % (obj, the_type)
115 msg = '%r %r' % (obj, the_type)
115 return msg
116 return msg
116
117
117
118
118 def parse_notifier_name(name):
119 def parse_notifier_name(name):
119 """Convert the name argument to a list of names.
120 """Convert the name argument to a list of names.
120
121
121 Examples
122 Examples
122 --------
123 --------
123
124
124 >>> parse_notifier_name('a')
125 >>> parse_notifier_name('a')
125 ['a']
126 ['a']
126 >>> parse_notifier_name(['a','b'])
127 >>> parse_notifier_name(['a','b'])
127 ['a', 'b']
128 ['a', 'b']
128 >>> parse_notifier_name(None)
129 >>> parse_notifier_name(None)
129 ['anytrait']
130 ['anytrait']
130 """
131 """
131 if isinstance(name, str):
132 if isinstance(name, str):
132 return [name]
133 return [name]
133 elif name is None:
134 elif name is None:
134 return ['anytrait']
135 return ['anytrait']
135 elif isinstance(name, (list, tuple)):
136 elif isinstance(name, (list, tuple)):
136 for n in name:
137 for n in name:
137 assert isinstance(n, str), "names must be strings"
138 assert isinstance(n, str), "names must be strings"
138 return name
139 return name
139
140
140
141
141 class _SimpleTest:
142 class _SimpleTest:
142 def __init__ ( self, value ): self.value = value
143 def __init__ ( self, value ): self.value = value
143 def __call__ ( self, test ):
144 def __call__ ( self, test ):
144 return test == self.value
145 return test == self.value
145 def __repr__(self):
146 def __repr__(self):
146 return "<SimpleTest(%r)" % self.value
147 return "<SimpleTest(%r)" % self.value
147 def __str__(self):
148 def __str__(self):
148 return self.__repr__()
149 return self.__repr__()
149
150
150
151
151 def getmembers(object, predicate=None):
152 def getmembers(object, predicate=None):
152 """A safe version of inspect.getmembers that handles missing attributes.
153 """A safe version of inspect.getmembers that handles missing attributes.
153
154
154 This is useful when there are descriptor based attributes that for
155 This is useful when there are descriptor based attributes that for
155 some reason raise AttributeError even though they exist. This happens
156 some reason raise AttributeError even though they exist. This happens
156 in zope.inteface with the __provides__ attribute.
157 in zope.inteface with the __provides__ attribute.
157 """
158 """
158 results = []
159 results = []
159 for key in dir(object):
160 for key in dir(object):
160 try:
161 try:
161 value = getattr(object, key)
162 value = getattr(object, key)
162 except AttributeError:
163 except AttributeError:
163 pass
164 pass
164 else:
165 else:
165 if not predicate or predicate(value):
166 if not predicate or predicate(value):
166 results.append((key, value))
167 results.append((key, value))
167 results.sort()
168 results.sort()
168 return results
169 return results
169
170
170
171
171 #-----------------------------------------------------------------------------
172 #-----------------------------------------------------------------------------
172 # Base TraitType for all traits
173 # Base TraitType for all traits
173 #-----------------------------------------------------------------------------
174 #-----------------------------------------------------------------------------
174
175
175
176
176 class TraitType(object):
177 class TraitType(object):
177 """A base class for all trait descriptors.
178 """A base class for all trait descriptors.
178
179
179 Notes
180 Notes
180 -----
181 -----
181 Our implementation of traits is based on Python's descriptor
182 Our implementation of traits is based on Python's descriptor
182 prototol. This class is the base class for all such descriptors. The
183 prototol. This class is the base class for all such descriptors. The
183 only magic we use is a custom metaclass for the main :class:`HasTraits`
184 only magic we use is a custom metaclass for the main :class:`HasTraits`
184 class that does the following:
185 class that does the following:
185
186
186 1. Sets the :attr:`name` attribute of every :class:`TraitType`
187 1. Sets the :attr:`name` attribute of every :class:`TraitType`
187 instance in the class dict to the name of the attribute.
188 instance in the class dict to the name of the attribute.
188 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
189 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
189 instance in the class dict to the *class* that declared the trait.
190 instance in the class dict to the *class* that declared the trait.
190 This is used by the :class:`This` trait to allow subclasses to
191 This is used by the :class:`This` trait to allow subclasses to
191 accept superclasses for :class:`This` values.
192 accept superclasses for :class:`This` values.
192 """
193 """
193
194
194
195
195 metadata = {}
196 metadata = {}
196 default_value = Undefined
197 default_value = Undefined
197 info_text = 'any value'
198 info_text = 'any value'
198
199
199 def __init__(self, default_value=NoDefaultSpecified, **metadata):
200 def __init__(self, default_value=NoDefaultSpecified, **metadata):
200 """Create a TraitType.
201 """Create a TraitType.
201 """
202 """
202 if default_value is not NoDefaultSpecified:
203 if default_value is not NoDefaultSpecified:
203 self.default_value = default_value
204 self.default_value = default_value
204
205
205 if len(metadata) > 0:
206 if len(metadata) > 0:
206 if len(self.metadata) > 0:
207 if len(self.metadata) > 0:
207 self._metadata = self.metadata.copy()
208 self._metadata = self.metadata.copy()
208 self._metadata.update(metadata)
209 self._metadata.update(metadata)
209 else:
210 else:
210 self._metadata = metadata
211 self._metadata = metadata
211 else:
212 else:
212 self._metadata = self.metadata
213 self._metadata = self.metadata
213
214
214 self.init()
215 self.init()
215
216
216 def init(self):
217 def init(self):
217 pass
218 pass
218
219
219 def get_default_value(self):
220 def get_default_value(self):
220 """Create a new instance of the default value."""
221 """Create a new instance of the default value."""
221 return self.default_value
222 return self.default_value
222
223
223 def instance_init(self, obj):
224 def instance_init(self, obj):
224 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
225 """This is called by :meth:`HasTraits.__new__` to finish init'ing.
225
226
226 Some stages of initialization must be delayed until the parent
227 Some stages of initialization must be delayed until the parent
227 :class:`HasTraits` instance has been created. This method is
228 :class:`HasTraits` instance has been created. This method is
228 called in :meth:`HasTraits.__new__` after the instance has been
229 called in :meth:`HasTraits.__new__` after the instance has been
229 created.
230 created.
230
231
231 This method trigger the creation and validation of default values
232 This method trigger the creation and validation of default values
232 and also things like the resolution of str given class names in
233 and also things like the resolution of str given class names in
233 :class:`Type` and :class`Instance`.
234 :class:`Type` and :class`Instance`.
234
235
235 Parameters
236 Parameters
236 ----------
237 ----------
237 obj : :class:`HasTraits` instance
238 obj : :class:`HasTraits` instance
238 The parent :class:`HasTraits` instance that has just been
239 The parent :class:`HasTraits` instance that has just been
239 created.
240 created.
240 """
241 """
241 self.set_default_value(obj)
242 self.set_default_value(obj)
242
243
243 def set_default_value(self, obj):
244 def set_default_value(self, obj):
244 """Set the default value on a per instance basis.
245 """Set the default value on a per instance basis.
245
246
246 This method is called by :meth:`instance_init` to create and
247 This method is called by :meth:`instance_init` to create and
247 validate the default value. The creation and validation of
248 validate the default value. The creation and validation of
248 default values must be delayed until the parent :class:`HasTraits`
249 default values must be delayed until the parent :class:`HasTraits`
249 class has been instantiated.
250 class has been instantiated.
250 """
251 """
251 # Check for a deferred initializer defined in the same class as the
252 # Check for a deferred initializer defined in the same class as the
252 # trait declaration or above.
253 # trait declaration or above.
253 mro = type(obj).mro()
254 mro = type(obj).mro()
254 meth_name = '_%s_default' % self.name
255 meth_name = '_%s_default' % self.name
255 for cls in mro[:mro.index(self.this_class)+1]:
256 for cls in mro[:mro.index(self.this_class)+1]:
256 if meth_name in cls.__dict__:
257 if meth_name in cls.__dict__:
257 break
258 break
258 else:
259 else:
259 # We didn't find one. Do static initialization.
260 # We didn't find one. Do static initialization.
260 dv = self.get_default_value()
261 dv = self.get_default_value()
261 newdv = self._validate(obj, dv)
262 newdv = self._validate(obj, dv)
262 obj._trait_values[self.name] = newdv
263 obj._trait_values[self.name] = newdv
263 return
264 return
264 # Complete the dynamic initialization.
265 # Complete the dynamic initialization.
265 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
266 obj._trait_dyn_inits[self.name] = cls.__dict__[meth_name]
266
267
267 def __get__(self, obj, cls=None):
268 def __get__(self, obj, cls=None):
268 """Get the value of the trait by self.name for the instance.
269 """Get the value of the trait by self.name for the instance.
269
270
270 Default values are instantiated when :meth:`HasTraits.__new__`
271 Default values are instantiated when :meth:`HasTraits.__new__`
271 is called. Thus by the time this method gets called either the
272 is called. Thus by the time this method gets called either the
272 default value or a user defined value (they called :meth:`__set__`)
273 default value or a user defined value (they called :meth:`__set__`)
273 is in the :class:`HasTraits` instance.
274 is in the :class:`HasTraits` instance.
274 """
275 """
275 if obj is None:
276 if obj is None:
276 return self
277 return self
277 else:
278 else:
278 try:
279 try:
279 value = obj._trait_values[self.name]
280 value = obj._trait_values[self.name]
280 except KeyError:
281 except KeyError:
281 # Check for a dynamic initializer.
282 # Check for a dynamic initializer.
282 if self.name in obj._trait_dyn_inits:
283 if self.name in obj._trait_dyn_inits:
283 value = obj._trait_dyn_inits[self.name](obj)
284 value = obj._trait_dyn_inits[self.name](obj)
284 # FIXME: Do we really validate here?
285 # FIXME: Do we really validate here?
285 value = self._validate(obj, value)
286 value = self._validate(obj, value)
286 obj._trait_values[self.name] = value
287 obj._trait_values[self.name] = value
287 return value
288 return value
288 else:
289 else:
289 raise TraitError('Unexpected error in TraitType: '
290 raise TraitError('Unexpected error in TraitType: '
290 'both default value and dynamic initializer are '
291 'both default value and dynamic initializer are '
291 'absent.')
292 'absent.')
292 except Exception:
293 except Exception:
293 # HasTraits should call set_default_value to populate
294 # HasTraits should call set_default_value to populate
294 # this. So this should never be reached.
295 # this. So this should never be reached.
295 raise TraitError('Unexpected error in TraitType: '
296 raise TraitError('Unexpected error in TraitType: '
296 'default value not set properly')
297 'default value not set properly')
297 else:
298 else:
298 return value
299 return value
299
300
300 def __set__(self, obj, value):
301 def __set__(self, obj, value):
301 new_value = self._validate(obj, value)
302 new_value = self._validate(obj, value)
302 old_value = self.__get__(obj)
303 old_value = self.__get__(obj)
303 if old_value != new_value:
304 if old_value != new_value:
304 obj._trait_values[self.name] = new_value
305 obj._trait_values[self.name] = new_value
305 obj._notify_trait(self.name, old_value, new_value)
306 obj._notify_trait(self.name, old_value, new_value)
306
307
307 def _validate(self, obj, value):
308 def _validate(self, obj, value):
308 if hasattr(self, 'validate'):
309 if hasattr(self, 'validate'):
309 return self.validate(obj, value)
310 return self.validate(obj, value)
310 elif hasattr(self, 'is_valid_for'):
311 elif hasattr(self, 'is_valid_for'):
311 valid = self.is_valid_for(value)
312 valid = self.is_valid_for(value)
312 if valid:
313 if valid:
313 return value
314 return value
314 else:
315 else:
315 raise TraitError('invalid value for type: %r' % value)
316 raise TraitError('invalid value for type: %r' % value)
316 elif hasattr(self, 'value_for'):
317 elif hasattr(self, 'value_for'):
317 return self.value_for(value)
318 return self.value_for(value)
318 else:
319 else:
319 return value
320 return value
320
321
321 def info(self):
322 def info(self):
322 return self.info_text
323 return self.info_text
323
324
324 def error(self, obj, value):
325 def error(self, obj, value):
325 if obj is not None:
326 if obj is not None:
326 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
327 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
327 % (self.name, class_of(obj),
328 % (self.name, class_of(obj),
328 self.info(), repr_type(value))
329 self.info(), repr_type(value))
329 else:
330 else:
330 e = "The '%s' trait must be %s, but a value of %r was specified." \
331 e = "The '%s' trait must be %s, but a value of %r was specified." \
331 % (self.name, self.info(), repr_type(value))
332 % (self.name, self.info(), repr_type(value))
332 raise TraitError(e)
333 raise TraitError(e)
333
334
334 def get_metadata(self, key):
335 def get_metadata(self, key):
335 return getattr(self, '_metadata', {}).get(key, None)
336 return getattr(self, '_metadata', {}).get(key, None)
336
337
337 def set_metadata(self, key, value):
338 def set_metadata(self, key, value):
338 getattr(self, '_metadata', {})[key] = value
339 getattr(self, '_metadata', {})[key] = value
339
340
340
341
341 #-----------------------------------------------------------------------------
342 #-----------------------------------------------------------------------------
342 # The HasTraits implementation
343 # The HasTraits implementation
343 #-----------------------------------------------------------------------------
344 #-----------------------------------------------------------------------------
344
345
345
346
346 class MetaHasTraits(type):
347 class MetaHasTraits(type):
347 """A metaclass for HasTraits.
348 """A metaclass for HasTraits.
348
349
349 This metaclass makes sure that any TraitType class attributes are
350 This metaclass makes sure that any TraitType class attributes are
350 instantiated and sets their name attribute.
351 instantiated and sets their name attribute.
351 """
352 """
352
353
353 def __new__(mcls, name, bases, classdict):
354 def __new__(mcls, name, bases, classdict):
354 """Create the HasTraits class.
355 """Create the HasTraits class.
355
356
356 This instantiates all TraitTypes in the class dict and sets their
357 This instantiates all TraitTypes in the class dict and sets their
357 :attr:`name` attribute.
358 :attr:`name` attribute.
358 """
359 """
359 # print "MetaHasTraitlets (mcls, name): ", mcls, name
360 # print "MetaHasTraitlets (mcls, name): ", mcls, name
360 # print "MetaHasTraitlets (bases): ", bases
361 # print "MetaHasTraitlets (bases): ", bases
361 # print "MetaHasTraitlets (classdict): ", classdict
362 # print "MetaHasTraitlets (classdict): ", classdict
362 for k,v in classdict.iteritems():
363 for k,v in classdict.iteritems():
363 if isinstance(v, TraitType):
364 if isinstance(v, TraitType):
364 v.name = k
365 v.name = k
365 elif inspect.isclass(v):
366 elif inspect.isclass(v):
366 if issubclass(v, TraitType):
367 if issubclass(v, TraitType):
367 vinst = v()
368 vinst = v()
368 vinst.name = k
369 vinst.name = k
369 classdict[k] = vinst
370 classdict[k] = vinst
370 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
371 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
371
372
372 def __init__(cls, name, bases, classdict):
373 def __init__(cls, name, bases, classdict):
373 """Finish initializing the HasTraits class.
374 """Finish initializing the HasTraits class.
374
375
375 This sets the :attr:`this_class` attribute of each TraitType in the
376 This sets the :attr:`this_class` attribute of each TraitType in the
376 class dict to the newly created class ``cls``.
377 class dict to the newly created class ``cls``.
377 """
378 """
378 for k, v in classdict.iteritems():
379 for k, v in classdict.iteritems():
379 if isinstance(v, TraitType):
380 if isinstance(v, TraitType):
380 v.this_class = cls
381 v.this_class = cls
381 super(MetaHasTraits, cls).__init__(name, bases, classdict)
382 super(MetaHasTraits, cls).__init__(name, bases, classdict)
382
383
383 class HasTraits(object):
384 class HasTraits(object):
384
385
385 __metaclass__ = MetaHasTraits
386 __metaclass__ = MetaHasTraits
386
387
387 def __new__(cls, **kw):
388 def __new__(cls, **kw):
388 # This is needed because in Python 2.6 object.__new__ only accepts
389 # This is needed because in Python 2.6 object.__new__ only accepts
389 # the cls argument.
390 # the cls argument.
390 new_meth = super(HasTraits, cls).__new__
391 new_meth = super(HasTraits, cls).__new__
391 if new_meth is object.__new__:
392 if new_meth is object.__new__:
392 inst = new_meth(cls)
393 inst = new_meth(cls)
393 else:
394 else:
394 inst = new_meth(cls, **kw)
395 inst = new_meth(cls, **kw)
395 inst._trait_values = {}
396 inst._trait_values = {}
396 inst._trait_notifiers = {}
397 inst._trait_notifiers = {}
397 inst._trait_dyn_inits = {}
398 inst._trait_dyn_inits = {}
398 # Here we tell all the TraitType instances to set their default
399 # Here we tell all the TraitType instances to set their default
399 # values on the instance.
400 # values on the instance.
400 for key in dir(cls):
401 for key in dir(cls):
401 # Some descriptors raise AttributeError like zope.interface's
402 # Some descriptors raise AttributeError like zope.interface's
402 # __provides__ attributes even though they exist. This causes
403 # __provides__ attributes even though they exist. This causes
403 # AttributeErrors even though they are listed in dir(cls).
404 # AttributeErrors even though they are listed in dir(cls).
404 try:
405 try:
405 value = getattr(cls, key)
406 value = getattr(cls, key)
406 except AttributeError:
407 except AttributeError:
407 pass
408 pass
408 else:
409 else:
409 if isinstance(value, TraitType):
410 if isinstance(value, TraitType):
410 value.instance_init(inst)
411 value.instance_init(inst)
411
412
412 return inst
413 return inst
413
414
414 def __init__(self, **kw):
415 def __init__(self, **kw):
415 # Allow trait values to be set using keyword arguments.
416 # Allow trait values to be set using keyword arguments.
416 # We need to use setattr for this to trigger validation and
417 # We need to use setattr for this to trigger validation and
417 # notifications.
418 # notifications.
418 for key, value in kw.iteritems():
419 for key, value in kw.iteritems():
419 setattr(self, key, value)
420 setattr(self, key, value)
420
421
421 def _notify_trait(self, name, old_value, new_value):
422 def _notify_trait(self, name, old_value, new_value):
422
423
423 # First dynamic ones
424 # First dynamic ones
424 callables = self._trait_notifiers.get(name,[])
425 callables = self._trait_notifiers.get(name,[])
425 more_callables = self._trait_notifiers.get('anytrait',[])
426 more_callables = self._trait_notifiers.get('anytrait',[])
426 callables.extend(more_callables)
427 callables.extend(more_callables)
427
428
428 # Now static ones
429 # Now static ones
429 try:
430 try:
430 cb = getattr(self, '_%s_changed' % name)
431 cb = getattr(self, '_%s_changed' % name)
431 except:
432 except:
432 pass
433 pass
433 else:
434 else:
434 callables.append(cb)
435 callables.append(cb)
435
436
436 # Call them all now
437 # Call them all now
437 for c in callables:
438 for c in callables:
438 # Traits catches and logs errors here. I allow them to raise
439 # Traits catches and logs errors here. I allow them to raise
439 if callable(c):
440 if callable(c):
440 argspec = inspect.getargspec(c)
441 argspec = inspect.getargspec(c)
441 nargs = len(argspec[0])
442 nargs = len(argspec[0])
442 # Bound methods have an additional 'self' argument
443 # Bound methods have an additional 'self' argument
443 # I don't know how to treat unbound methods, but they
444 # I don't know how to treat unbound methods, but they
444 # can't really be used for callbacks.
445 # can't really be used for callbacks.
445 if isinstance(c, types.MethodType):
446 if isinstance(c, types.MethodType):
446 offset = -1
447 offset = -1
447 else:
448 else:
448 offset = 0
449 offset = 0
449 if nargs + offset == 0:
450 if nargs + offset == 0:
450 c()
451 c()
451 elif nargs + offset == 1:
452 elif nargs + offset == 1:
452 c(name)
453 c(name)
453 elif nargs + offset == 2:
454 elif nargs + offset == 2:
454 c(name, new_value)
455 c(name, new_value)
455 elif nargs + offset == 3:
456 elif nargs + offset == 3:
456 c(name, old_value, new_value)
457 c(name, old_value, new_value)
457 else:
458 else:
458 raise TraitError('a trait changed callback '
459 raise TraitError('a trait changed callback '
459 'must have 0-3 arguments.')
460 'must have 0-3 arguments.')
460 else:
461 else:
461 raise TraitError('a trait changed callback '
462 raise TraitError('a trait changed callback '
462 'must be callable.')
463 'must be callable.')
463
464
464
465
465 def _add_notifiers(self, handler, name):
466 def _add_notifiers(self, handler, name):
466 if not self._trait_notifiers.has_key(name):
467 if not self._trait_notifiers.has_key(name):
467 nlist = []
468 nlist = []
468 self._trait_notifiers[name] = nlist
469 self._trait_notifiers[name] = nlist
469 else:
470 else:
470 nlist = self._trait_notifiers[name]
471 nlist = self._trait_notifiers[name]
471 if handler not in nlist:
472 if handler not in nlist:
472 nlist.append(handler)
473 nlist.append(handler)
473
474
474 def _remove_notifiers(self, handler, name):
475 def _remove_notifiers(self, handler, name):
475 if self._trait_notifiers.has_key(name):
476 if self._trait_notifiers.has_key(name):
476 nlist = self._trait_notifiers[name]
477 nlist = self._trait_notifiers[name]
477 try:
478 try:
478 index = nlist.index(handler)
479 index = nlist.index(handler)
479 except ValueError:
480 except ValueError:
480 pass
481 pass
481 else:
482 else:
482 del nlist[index]
483 del nlist[index]
483
484
484 def on_trait_change(self, handler, name=None, remove=False):
485 def on_trait_change(self, handler, name=None, remove=False):
485 """Setup a handler to be called when a trait changes.
486 """Setup a handler to be called when a trait changes.
486
487
487 This is used to setup dynamic notifications of trait changes.
488 This is used to setup dynamic notifications of trait changes.
488
489
489 Static handlers can be created by creating methods on a HasTraits
490 Static handlers can be created by creating methods on a HasTraits
490 subclass with the naming convention '_[traitname]_changed'. Thus,
491 subclass with the naming convention '_[traitname]_changed'. Thus,
491 to create static handler for the trait 'a', create the method
492 to create static handler for the trait 'a', create the method
492 _a_changed(self, name, old, new) (fewer arguments can be used, see
493 _a_changed(self, name, old, new) (fewer arguments can be used, see
493 below).
494 below).
494
495
495 Parameters
496 Parameters
496 ----------
497 ----------
497 handler : callable
498 handler : callable
498 A callable that is called when a trait changes. Its
499 A callable that is called when a trait changes. Its
499 signature can be handler(), handler(name), handler(name, new)
500 signature can be handler(), handler(name), handler(name, new)
500 or handler(name, old, new).
501 or handler(name, old, new).
501 name : list, str, None
502 name : list, str, None
502 If None, the handler will apply to all traits. If a list
503 If None, the handler will apply to all traits. If a list
503 of str, handler will apply to all names in the list. If a
504 of str, handler will apply to all names in the list. If a
504 str, the handler will apply just to that name.
505 str, the handler will apply just to that name.
505 remove : bool
506 remove : bool
506 If False (the default), then install the handler. If True
507 If False (the default), then install the handler. If True
507 then unintall it.
508 then unintall it.
508 """
509 """
509 if remove:
510 if remove:
510 names = parse_notifier_name(name)
511 names = parse_notifier_name(name)
511 for n in names:
512 for n in names:
512 self._remove_notifiers(handler, n)
513 self._remove_notifiers(handler, n)
513 else:
514 else:
514 names = parse_notifier_name(name)
515 names = parse_notifier_name(name)
515 for n in names:
516 for n in names:
516 self._add_notifiers(handler, n)
517 self._add_notifiers(handler, n)
517
518
518 @classmethod
519 @classmethod
519 def class_trait_names(cls, **metadata):
520 def class_trait_names(cls, **metadata):
520 """Get a list of all the names of this classes traits.
521 """Get a list of all the names of this classes traits.
521
522
522 This method is just like the :meth:`trait_names` method, but is unbound.
523 This method is just like the :meth:`trait_names` method, but is unbound.
523 """
524 """
524 return cls.class_traits(**metadata).keys()
525 return cls.class_traits(**metadata).keys()
525
526
526 @classmethod
527 @classmethod
527 def class_traits(cls, **metadata):
528 def class_traits(cls, **metadata):
528 """Get a list of all the traits of this class.
529 """Get a list of all the traits of this class.
529
530
530 This method is just like the :meth:`traits` method, but is unbound.
531 This method is just like the :meth:`traits` method, but is unbound.
531
532
532 The TraitTypes returned don't know anything about the values
533 The TraitTypes returned don't know anything about the values
533 that the various HasTrait's instances are holding.
534 that the various HasTrait's instances are holding.
534
535
535 This follows the same algorithm as traits does and does not allow
536 This follows the same algorithm as traits does and does not allow
536 for any simple way of specifying merely that a metadata name
537 for any simple way of specifying merely that a metadata name
537 exists, but has any value. This is because get_metadata returns
538 exists, but has any value. This is because get_metadata returns
538 None if a metadata key doesn't exist.
539 None if a metadata key doesn't exist.
539 """
540 """
540 traits = dict([memb for memb in getmembers(cls) if \
541 traits = dict([memb for memb in getmembers(cls) if \
541 isinstance(memb[1], TraitType)])
542 isinstance(memb[1], TraitType)])
542
543
543 if len(metadata) == 0:
544 if len(metadata) == 0:
544 return traits
545 return traits
545
546
546 for meta_name, meta_eval in metadata.items():
547 for meta_name, meta_eval in metadata.items():
547 if type(meta_eval) is not FunctionType:
548 if type(meta_eval) is not FunctionType:
548 metadata[meta_name] = _SimpleTest(meta_eval)
549 metadata[meta_name] = _SimpleTest(meta_eval)
549
550
550 result = {}
551 result = {}
551 for name, trait in traits.items():
552 for name, trait in traits.items():
552 for meta_name, meta_eval in metadata.items():
553 for meta_name, meta_eval in metadata.items():
553 if not meta_eval(trait.get_metadata(meta_name)):
554 if not meta_eval(trait.get_metadata(meta_name)):
554 break
555 break
555 else:
556 else:
556 result[name] = trait
557 result[name] = trait
557
558
558 return result
559 return result
559
560
560 def trait_names(self, **metadata):
561 def trait_names(self, **metadata):
561 """Get a list of all the names of this classes traits."""
562 """Get a list of all the names of this classes traits."""
562 return self.traits(**metadata).keys()
563 return self.traits(**metadata).keys()
563
564
564 def traits(self, **metadata):
565 def traits(self, **metadata):
565 """Get a list of all the traits of this class.
566 """Get a list of all the traits of this class.
566
567
567 The TraitTypes returned don't know anything about the values
568 The TraitTypes returned don't know anything about the values
568 that the various HasTrait's instances are holding.
569 that the various HasTrait's instances are holding.
569
570
570 This follows the same algorithm as traits does and does not allow
571 This follows the same algorithm as traits does and does not allow
571 for any simple way of specifying merely that a metadata name
572 for any simple way of specifying merely that a metadata name
572 exists, but has any value. This is because get_metadata returns
573 exists, but has any value. This is because get_metadata returns
573 None if a metadata key doesn't exist.
574 None if a metadata key doesn't exist.
574 """
575 """
575 traits = dict([memb for memb in getmembers(self.__class__) if \
576 traits = dict([memb for memb in getmembers(self.__class__) if \
576 isinstance(memb[1], TraitType)])
577 isinstance(memb[1], TraitType)])
577
578
578 if len(metadata) == 0:
579 if len(metadata) == 0:
579 return traits
580 return traits
580
581
581 for meta_name, meta_eval in metadata.items():
582 for meta_name, meta_eval in metadata.items():
582 if type(meta_eval) is not FunctionType:
583 if type(meta_eval) is not FunctionType:
583 metadata[meta_name] = _SimpleTest(meta_eval)
584 metadata[meta_name] = _SimpleTest(meta_eval)
584
585
585 result = {}
586 result = {}
586 for name, trait in traits.items():
587 for name, trait in traits.items():
587 for meta_name, meta_eval in metadata.items():
588 for meta_name, meta_eval in metadata.items():
588 if not meta_eval(trait.get_metadata(meta_name)):
589 if not meta_eval(trait.get_metadata(meta_name)):
589 break
590 break
590 else:
591 else:
591 result[name] = trait
592 result[name] = trait
592
593
593 return result
594 return result
594
595
595 def trait_metadata(self, traitname, key):
596 def trait_metadata(self, traitname, key):
596 """Get metadata values for trait by key."""
597 """Get metadata values for trait by key."""
597 try:
598 try:
598 trait = getattr(self.__class__, traitname)
599 trait = getattr(self.__class__, traitname)
599 except AttributeError:
600 except AttributeError:
600 raise TraitError("Class %s does not have a trait named %s" %
601 raise TraitError("Class %s does not have a trait named %s" %
601 (self.__class__.__name__, traitname))
602 (self.__class__.__name__, traitname))
602 else:
603 else:
603 return trait.get_metadata(key)
604 return trait.get_metadata(key)
604
605
605 #-----------------------------------------------------------------------------
606 #-----------------------------------------------------------------------------
606 # Actual TraitTypes implementations/subclasses
607 # Actual TraitTypes implementations/subclasses
607 #-----------------------------------------------------------------------------
608 #-----------------------------------------------------------------------------
608
609
609 #-----------------------------------------------------------------------------
610 #-----------------------------------------------------------------------------
610 # TraitTypes subclasses for handling classes and instances of classes
611 # TraitTypes subclasses for handling classes and instances of classes
611 #-----------------------------------------------------------------------------
612 #-----------------------------------------------------------------------------
612
613
613
614
614 class ClassBasedTraitType(TraitType):
615 class ClassBasedTraitType(TraitType):
615 """A trait with error reporting for Type, Instance and This."""
616 """A trait with error reporting for Type, Instance and This."""
616
617
617 def error(self, obj, value):
618 def error(self, obj, value):
618 kind = type(value)
619 kind = type(value)
619 if kind is InstanceType:
620 if kind is InstanceType:
620 msg = 'class %s' % value.__class__.__name__
621 msg = 'class %s' % value.__class__.__name__
621 else:
622 else:
622 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
623 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
623
624
624 if obj is not None:
625 if obj is not None:
625 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
626 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
626 % (self.name, class_of(obj),
627 % (self.name, class_of(obj),
627 self.info(), msg)
628 self.info(), msg)
628 else:
629 else:
629 e = "The '%s' trait must be %s, but a value of %r was specified." \
630 e = "The '%s' trait must be %s, but a value of %r was specified." \
630 % (self.name, self.info(), msg)
631 % (self.name, self.info(), msg)
631
632
632 raise TraitError(e)
633 raise TraitError(e)
633
634
634
635
635 class Type(ClassBasedTraitType):
636 class Type(ClassBasedTraitType):
636 """A trait whose value must be a subclass of a specified class."""
637 """A trait whose value must be a subclass of a specified class."""
637
638
638 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
639 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
639 """Construct a Type trait
640 """Construct a Type trait
640
641
641 A Type trait specifies that its values must be subclasses of
642 A Type trait specifies that its values must be subclasses of
642 a particular class.
643 a particular class.
643
644
644 If only ``default_value`` is given, it is used for the ``klass`` as
645 If only ``default_value`` is given, it is used for the ``klass`` as
645 well.
646 well.
646
647
647 Parameters
648 Parameters
648 ----------
649 ----------
649 default_value : class, str or None
650 default_value : class, str or None
650 The default value must be a subclass of klass. If an str,
651 The default value must be a subclass of klass. If an str,
651 the str must be a fully specified class name, like 'foo.bar.Bah'.
652 the str must be a fully specified class name, like 'foo.bar.Bah'.
652 The string is resolved into real class, when the parent
653 The string is resolved into real class, when the parent
653 :class:`HasTraits` class is instantiated.
654 :class:`HasTraits` class is instantiated.
654 klass : class, str, None
655 klass : class, str, None
655 Values of this trait must be a subclass of klass. The klass
656 Values of this trait must be a subclass of klass. The klass
656 may be specified in a string like: 'foo.bar.MyClass'.
657 may be specified in a string like: 'foo.bar.MyClass'.
657 The string is resolved into real class, when the parent
658 The string is resolved into real class, when the parent
658 :class:`HasTraits` class is instantiated.
659 :class:`HasTraits` class is instantiated.
659 allow_none : boolean
660 allow_none : boolean
660 Indicates whether None is allowed as an assignable value. Even if
661 Indicates whether None is allowed as an assignable value. Even if
661 ``False``, the default value may be ``None``.
662 ``False``, the default value may be ``None``.
662 """
663 """
663 if default_value is None:
664 if default_value is None:
664 if klass is None:
665 if klass is None:
665 klass = object
666 klass = object
666 elif klass is None:
667 elif klass is None:
667 klass = default_value
668 klass = default_value
668
669
669 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
670 if not (inspect.isclass(klass) or isinstance(klass, basestring)):
670 raise TraitError("A Type trait must specify a class.")
671 raise TraitError("A Type trait must specify a class.")
671
672
672 self.klass = klass
673 self.klass = klass
673 self._allow_none = allow_none
674 self._allow_none = allow_none
674
675
675 super(Type, self).__init__(default_value, **metadata)
676 super(Type, self).__init__(default_value, **metadata)
676
677
677 def validate(self, obj, value):
678 def validate(self, obj, value):
678 """Validates that the value is a valid object instance."""
679 """Validates that the value is a valid object instance."""
679 try:
680 try:
680 if issubclass(value, self.klass):
681 if issubclass(value, self.klass):
681 return value
682 return value
682 except:
683 except:
683 if (value is None) and (self._allow_none):
684 if (value is None) and (self._allow_none):
684 return value
685 return value
685
686
686 self.error(obj, value)
687 self.error(obj, value)
687
688
688 def info(self):
689 def info(self):
689 """ Returns a description of the trait."""
690 """ Returns a description of the trait."""
690 if isinstance(self.klass, basestring):
691 if isinstance(self.klass, basestring):
691 klass = self.klass
692 klass = self.klass
692 else:
693 else:
693 klass = self.klass.__name__
694 klass = self.klass.__name__
694 result = 'a subclass of ' + klass
695 result = 'a subclass of ' + klass
695 if self._allow_none:
696 if self._allow_none:
696 return result + ' or None'
697 return result + ' or None'
697 return result
698 return result
698
699
699 def instance_init(self, obj):
700 def instance_init(self, obj):
700 self._resolve_classes()
701 self._resolve_classes()
701 super(Type, self).instance_init(obj)
702 super(Type, self).instance_init(obj)
702
703
703 def _resolve_classes(self):
704 def _resolve_classes(self):
704 if isinstance(self.klass, basestring):
705 if isinstance(self.klass, basestring):
705 self.klass = import_item(self.klass)
706 self.klass = import_item(self.klass)
706 if isinstance(self.default_value, basestring):
707 if isinstance(self.default_value, basestring):
707 self.default_value = import_item(self.default_value)
708 self.default_value = import_item(self.default_value)
708
709
709 def get_default_value(self):
710 def get_default_value(self):
710 return self.default_value
711 return self.default_value
711
712
712
713
713 class DefaultValueGenerator(object):
714 class DefaultValueGenerator(object):
714 """A class for generating new default value instances."""
715 """A class for generating new default value instances."""
715
716
716 def __init__(self, *args, **kw):
717 def __init__(self, *args, **kw):
717 self.args = args
718 self.args = args
718 self.kw = kw
719 self.kw = kw
719
720
720 def generate(self, klass):
721 def generate(self, klass):
721 return klass(*self.args, **self.kw)
722 return klass(*self.args, **self.kw)
722
723
723
724
724 class Instance(ClassBasedTraitType):
725 class Instance(ClassBasedTraitType):
725 """A trait whose value must be an instance of a specified class.
726 """A trait whose value must be an instance of a specified class.
726
727
727 The value can also be an instance of a subclass of the specified class.
728 The value can also be an instance of a subclass of the specified class.
728 """
729 """
729
730
730 def __init__(self, klass=None, args=None, kw=None,
731 def __init__(self, klass=None, args=None, kw=None,
731 allow_none=True, **metadata ):
732 allow_none=True, **metadata ):
732 """Construct an Instance trait.
733 """Construct an Instance trait.
733
734
734 This trait allows values that are instances of a particular
735 This trait allows values that are instances of a particular
735 class or its sublclasses. Our implementation is quite different
736 class or its sublclasses. Our implementation is quite different
736 from that of enthough.traits as we don't allow instances to be used
737 from that of enthough.traits as we don't allow instances to be used
737 for klass and we handle the ``args`` and ``kw`` arguments differently.
738 for klass and we handle the ``args`` and ``kw`` arguments differently.
738
739
739 Parameters
740 Parameters
740 ----------
741 ----------
741 klass : class, str
742 klass : class, str
742 The class that forms the basis for the trait. Class names
743 The class that forms the basis for the trait. Class names
743 can also be specified as strings, like 'foo.bar.Bar'.
744 can also be specified as strings, like 'foo.bar.Bar'.
744 args : tuple
745 args : tuple
745 Positional arguments for generating the default value.
746 Positional arguments for generating the default value.
746 kw : dict
747 kw : dict
747 Keyword arguments for generating the default value.
748 Keyword arguments for generating the default value.
748 allow_none : bool
749 allow_none : bool
749 Indicates whether None is allowed as a value.
750 Indicates whether None is allowed as a value.
750
751
751 Default Value
752 Default Value
752 -------------
753 -------------
753 If both ``args`` and ``kw`` are None, then the default value is None.
754 If both ``args`` and ``kw`` are None, then the default value is None.
754 If ``args`` is a tuple and ``kw`` is a dict, then the default is
755 If ``args`` is a tuple and ``kw`` is a dict, then the default is
755 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
756 created as ``klass(*args, **kw)``. If either ``args`` or ``kw`` is
756 not (but not both), None is replace by ``()`` or ``{}``.
757 not (but not both), None is replace by ``()`` or ``{}``.
757 """
758 """
758
759
759 self._allow_none = allow_none
760 self._allow_none = allow_none
760
761
761 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
762 if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, basestring))):
762 raise TraitError('The klass argument must be a class'
763 raise TraitError('The klass argument must be a class'
763 ' you gave: %r' % klass)
764 ' you gave: %r' % klass)
764 self.klass = klass
765 self.klass = klass
765
766
766 # self.klass is a class, so handle default_value
767 # self.klass is a class, so handle default_value
767 if args is None and kw is None:
768 if args is None and kw is None:
768 default_value = None
769 default_value = None
769 else:
770 else:
770 if args is None:
771 if args is None:
771 # kw is not None
772 # kw is not None
772 args = ()
773 args = ()
773 elif kw is None:
774 elif kw is None:
774 # args is not None
775 # args is not None
775 kw = {}
776 kw = {}
776
777
777 if not isinstance(kw, dict):
778 if not isinstance(kw, dict):
778 raise TraitError("The 'kw' argument must be a dict or None.")
779 raise TraitError("The 'kw' argument must be a dict or None.")
779 if not isinstance(args, tuple):
780 if not isinstance(args, tuple):
780 raise TraitError("The 'args' argument must be a tuple or None.")
781 raise TraitError("The 'args' argument must be a tuple or None.")
781
782
782 default_value = DefaultValueGenerator(*args, **kw)
783 default_value = DefaultValueGenerator(*args, **kw)
783
784
784 super(Instance, self).__init__(default_value, **metadata)
785 super(Instance, self).__init__(default_value, **metadata)
785
786
786 def validate(self, obj, value):
787 def validate(self, obj, value):
787 if value is None:
788 if value is None:
788 if self._allow_none:
789 if self._allow_none:
789 return value
790 return value
790 self.error(obj, value)
791 self.error(obj, value)
791
792
792 if isinstance(value, self.klass):
793 if isinstance(value, self.klass):
793 return value
794 return value
794 else:
795 else:
795 self.error(obj, value)
796 self.error(obj, value)
796
797
797 def info(self):
798 def info(self):
798 if isinstance(self.klass, basestring):
799 if isinstance(self.klass, basestring):
799 klass = self.klass
800 klass = self.klass
800 else:
801 else:
801 klass = self.klass.__name__
802 klass = self.klass.__name__
802 result = class_of(klass)
803 result = class_of(klass)
803 if self._allow_none:
804 if self._allow_none:
804 return result + ' or None'
805 return result + ' or None'
805
806
806 return result
807 return result
807
808
808 def instance_init(self, obj):
809 def instance_init(self, obj):
809 self._resolve_classes()
810 self._resolve_classes()
810 super(Instance, self).instance_init(obj)
811 super(Instance, self).instance_init(obj)
811
812
812 def _resolve_classes(self):
813 def _resolve_classes(self):
813 if isinstance(self.klass, basestring):
814 if isinstance(self.klass, basestring):
814 self.klass = import_item(self.klass)
815 self.klass = import_item(self.klass)
815
816
816 def get_default_value(self):
817 def get_default_value(self):
817 """Instantiate a default value instance.
818 """Instantiate a default value instance.
818
819
819 This is called when the containing HasTraits classes'
820 This is called when the containing HasTraits classes'
820 :meth:`__new__` method is called to ensure that a unique instance
821 :meth:`__new__` method is called to ensure that a unique instance
821 is created for each HasTraits instance.
822 is created for each HasTraits instance.
822 """
823 """
823 dv = self.default_value
824 dv = self.default_value
824 if isinstance(dv, DefaultValueGenerator):
825 if isinstance(dv, DefaultValueGenerator):
825 return dv.generate(self.klass)
826 return dv.generate(self.klass)
826 else:
827 else:
827 return dv
828 return dv
828
829
829
830
830 class This(ClassBasedTraitType):
831 class This(ClassBasedTraitType):
831 """A trait for instances of the class containing this trait.
832 """A trait for instances of the class containing this trait.
832
833
833 Because how how and when class bodies are executed, the ``This``
834 Because how how and when class bodies are executed, the ``This``
834 trait can only have a default value of None. This, and because we
835 trait can only have a default value of None. This, and because we
835 always validate default values, ``allow_none`` is *always* true.
836 always validate default values, ``allow_none`` is *always* true.
836 """
837 """
837
838
838 info_text = 'an instance of the same type as the receiver or None'
839 info_text = 'an instance of the same type as the receiver or None'
839
840
840 def __init__(self, **metadata):
841 def __init__(self, **metadata):
841 super(This, self).__init__(None, **metadata)
842 super(This, self).__init__(None, **metadata)
842
843
843 def validate(self, obj, value):
844 def validate(self, obj, value):
844 # What if value is a superclass of obj.__class__? This is
845 # What if value is a superclass of obj.__class__? This is
845 # complicated if it was the superclass that defined the This
846 # complicated if it was the superclass that defined the This
846 # trait.
847 # trait.
847 if isinstance(value, self.this_class) or (value is None):
848 if isinstance(value, self.this_class) or (value is None):
848 return value
849 return value
849 else:
850 else:
850 self.error(obj, value)
851 self.error(obj, value)
851
852
852
853
853 #-----------------------------------------------------------------------------
854 #-----------------------------------------------------------------------------
854 # Basic TraitTypes implementations/subclasses
855 # Basic TraitTypes implementations/subclasses
855 #-----------------------------------------------------------------------------
856 #-----------------------------------------------------------------------------
856
857
857
858
858 class Any(TraitType):
859 class Any(TraitType):
859 default_value = None
860 default_value = None
860 info_text = 'any value'
861 info_text = 'any value'
861
862
862
863
863 class Int(TraitType):
864 class Int(TraitType):
864 """A integer trait."""
865 """A integer trait."""
865
866
866 default_value = 0
867 default_value = 0
867 info_text = 'an integer'
868 info_text = 'an integer'
868
869
869 def validate(self, obj, value):
870 def validate(self, obj, value):
870 if isinstance(value, int):
871 if isinstance(value, int):
871 return value
872 return value
872 self.error(obj, value)
873 self.error(obj, value)
873
874
874 class CInt(Int):
875 class CInt(Int):
875 """A casting version of the int trait."""
876 """A casting version of the int trait."""
876
877
877 def validate(self, obj, value):
878 def validate(self, obj, value):
878 try:
879 try:
879 return int(value)
880 return int(value)
880 except:
881 except:
881 self.error(obj, value)
882 self.error(obj, value)
882
883
883
884
884 class Long(TraitType):
885 class Long(TraitType):
885 """A long integer trait."""
886 """A long integer trait."""
886
887
887 default_value = 0L
888 default_value = 0L
888 info_text = 'a long'
889 info_text = 'a long'
889
890
890 def validate(self, obj, value):
891 def validate(self, obj, value):
891 if isinstance(value, long):
892 if isinstance(value, long):
892 return value
893 return value
893 if isinstance(value, int):
894 if isinstance(value, int):
894 return long(value)
895 return long(value)
895 self.error(obj, value)
896 self.error(obj, value)
896
897
897
898
898 class CLong(Long):
899 class CLong(Long):
899 """A casting version of the long integer trait."""
900 """A casting version of the long integer trait."""
900
901
901 def validate(self, obj, value):
902 def validate(self, obj, value):
902 try:
903 try:
903 return long(value)
904 return long(value)
904 except:
905 except:
905 self.error(obj, value)
906 self.error(obj, value)
906
907
907
908
908 class Float(TraitType):
909 class Float(TraitType):
909 """A float trait."""
910 """A float trait."""
910
911
911 default_value = 0.0
912 default_value = 0.0
912 info_text = 'a float'
913 info_text = 'a float'
913
914
914 def validate(self, obj, value):
915 def validate(self, obj, value):
915 if isinstance(value, float):
916 if isinstance(value, float):
916 return value
917 return value
917 if isinstance(value, int):
918 if isinstance(value, int):
918 return float(value)
919 return float(value)
919 self.error(obj, value)
920 self.error(obj, value)
920
921
921
922
922 class CFloat(Float):
923 class CFloat(Float):
923 """A casting version of the float trait."""
924 """A casting version of the float trait."""
924
925
925 def validate(self, obj, value):
926 def validate(self, obj, value):
926 try:
927 try:
927 return float(value)
928 return float(value)
928 except:
929 except:
929 self.error(obj, value)
930 self.error(obj, value)
930
931
931 class Complex(TraitType):
932 class Complex(TraitType):
932 """A trait for complex numbers."""
933 """A trait for complex numbers."""
933
934
934 default_value = 0.0 + 0.0j
935 default_value = 0.0 + 0.0j
935 info_text = 'a complex number'
936 info_text = 'a complex number'
936
937
937 def validate(self, obj, value):
938 def validate(self, obj, value):
938 if isinstance(value, complex):
939 if isinstance(value, complex):
939 return value
940 return value
940 if isinstance(value, (float, int)):
941 if isinstance(value, (float, int)):
941 return complex(value)
942 return complex(value)
942 self.error(obj, value)
943 self.error(obj, value)
943
944
944
945
945 class CComplex(Complex):
946 class CComplex(Complex):
946 """A casting version of the complex number trait."""
947 """A casting version of the complex number trait."""
947
948
948 def validate (self, obj, value):
949 def validate (self, obj, value):
949 try:
950 try:
950 return complex(value)
951 return complex(value)
951 except:
952 except:
952 self.error(obj, value)
953 self.error(obj, value)
953
954
954 # We should always be explicit about whether we're using bytes or unicode, both
955 # We should always be explicit about whether we're using bytes or unicode, both
955 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
956 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
956 # we don't have a Str type.
957 # we don't have a Str type.
957 class Bytes(TraitType):
958 class Bytes(TraitType):
958 """A trait for strings."""
959 """A trait for strings."""
959
960
960 default_value = ''
961 default_value = ''
961 info_text = 'a string'
962 info_text = 'a string'
962
963
963 def validate(self, obj, value):
964 def validate(self, obj, value):
964 if isinstance(value, bytes):
965 if isinstance(value, bytes):
965 return value
966 return value
966 self.error(obj, value)
967 self.error(obj, value)
967
968
968
969
969 class CBytes(Bytes):
970 class CBytes(Bytes):
970 """A casting version of the string trait."""
971 """A casting version of the string trait."""
971
972
972 def validate(self, obj, value):
973 def validate(self, obj, value):
973 try:
974 try:
974 return bytes(value)
975 return bytes(value)
975 except:
976 except:
976 self.error(obj, value)
977 self.error(obj, value)
977
978
978
979
979 class Unicode(TraitType):
980 class Unicode(TraitType):
980 """A trait for unicode strings."""
981 """A trait for unicode strings."""
981
982
982 default_value = u''
983 default_value = u''
983 info_text = 'a unicode string'
984 info_text = 'a unicode string'
984
985
985 def validate(self, obj, value):
986 def validate(self, obj, value):
986 if isinstance(value, unicode):
987 if isinstance(value, unicode):
987 return value
988 return value
988 if isinstance(value, bytes):
989 if isinstance(value, bytes):
989 return unicode(value)
990 return unicode(value)
990 self.error(obj, value)
991 self.error(obj, value)
991
992
992
993
993 class CUnicode(Unicode):
994 class CUnicode(Unicode):
994 """A casting version of the unicode trait."""
995 """A casting version of the unicode trait."""
995
996
996 def validate(self, obj, value):
997 def validate(self, obj, value):
997 try:
998 try:
998 return unicode(value)
999 return unicode(value)
999 except:
1000 except:
1000 self.error(obj, value)
1001 self.error(obj, value)
1002
1003
1004 class ObjectName(TraitType):
1005 """A string holding a valid object name in this version of Python.
1006
1007 This does not check that the name exists in any scope."""
1008 info_text = "a valid object identifier in Python"
1009
1010 if sys.version_info[0] < 3:
1011 # Python 2:
1012 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
1013 def isidentifier(self, s):
1014 return bool(self._name_re.match(s))
1015
1016 def coerce_str(self, obj, value):
1017 "In Python 2, coerce ascii-only unicode to str"
1018 if isinstance(value, unicode):
1019 try:
1020 return str(value)
1021 except UnicodeEncodeError:
1022 self.error(obj, value)
1023 return value
1024
1025 else:
1026 # Python 3:
1027 isidentifier = staticmethod(lambda s: s.isidentifier())
1028 coerce_str = staticmethod(lambda _,s: s)
1029
1030 def validate(self, obj, value):
1031 value = self.coerce_str(obj, value)
1032
1033 if isinstance(value, str) and self.isidentifier(value):
1034 return value
1035 self.error(obj, value)
1036
1037 class DottedObjectName(ObjectName):
1038 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1039 def validate(self, obj, value):
1040 value = self.coerce_str(obj, value)
1041
1042 if isinstance(value, str) and all(self.isidentifier(x) \
1043 for x in value.split('.')):
1044 return value
1045 self.error(obj, value)
1001
1046
1002
1047
1003 class Bool(TraitType):
1048 class Bool(TraitType):
1004 """A boolean (True, False) trait."""
1049 """A boolean (True, False) trait."""
1005
1050
1006 default_value = False
1051 default_value = False
1007 info_text = 'a boolean'
1052 info_text = 'a boolean'
1008
1053
1009 def validate(self, obj, value):
1054 def validate(self, obj, value):
1010 if isinstance(value, bool):
1055 if isinstance(value, bool):
1011 return value
1056 return value
1012 self.error(obj, value)
1057 self.error(obj, value)
1013
1058
1014
1059
1015 class CBool(Bool):
1060 class CBool(Bool):
1016 """A casting version of the boolean trait."""
1061 """A casting version of the boolean trait."""
1017
1062
1018 def validate(self, obj, value):
1063 def validate(self, obj, value):
1019 try:
1064 try:
1020 return bool(value)
1065 return bool(value)
1021 except:
1066 except:
1022 self.error(obj, value)
1067 self.error(obj, value)
1023
1068
1024
1069
1025 class Enum(TraitType):
1070 class Enum(TraitType):
1026 """An enum that whose value must be in a given sequence."""
1071 """An enum that whose value must be in a given sequence."""
1027
1072
1028 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1073 def __init__(self, values, default_value=None, allow_none=True, **metadata):
1029 self.values = values
1074 self.values = values
1030 self._allow_none = allow_none
1075 self._allow_none = allow_none
1031 super(Enum, self).__init__(default_value, **metadata)
1076 super(Enum, self).__init__(default_value, **metadata)
1032
1077
1033 def validate(self, obj, value):
1078 def validate(self, obj, value):
1034 if value is None:
1079 if value is None:
1035 if self._allow_none:
1080 if self._allow_none:
1036 return value
1081 return value
1037
1082
1038 if value in self.values:
1083 if value in self.values:
1039 return value
1084 return value
1040 self.error(obj, value)
1085 self.error(obj, value)
1041
1086
1042 def info(self):
1087 def info(self):
1043 """ Returns a description of the trait."""
1088 """ Returns a description of the trait."""
1044 result = 'any of ' + repr(self.values)
1089 result = 'any of ' + repr(self.values)
1045 if self._allow_none:
1090 if self._allow_none:
1046 return result + ' or None'
1091 return result + ' or None'
1047 return result
1092 return result
1048
1093
1049 class CaselessStrEnum(Enum):
1094 class CaselessStrEnum(Enum):
1050 """An enum of strings that are caseless in validate."""
1095 """An enum of strings that are caseless in validate."""
1051
1096
1052 def validate(self, obj, value):
1097 def validate(self, obj, value):
1053 if value is None:
1098 if value is None:
1054 if self._allow_none:
1099 if self._allow_none:
1055 return value
1100 return value
1056
1101
1057 if not isinstance(value, str):
1102 if not isinstance(value, str):
1058 self.error(obj, value)
1103 self.error(obj, value)
1059
1104
1060 for v in self.values:
1105 for v in self.values:
1061 if v.lower() == value.lower():
1106 if v.lower() == value.lower():
1062 return v
1107 return v
1063 self.error(obj, value)
1108 self.error(obj, value)
1064
1109
1065 class Container(Instance):
1110 class Container(Instance):
1066 """An instance of a container (list, set, etc.)
1111 """An instance of a container (list, set, etc.)
1067
1112
1068 To be subclassed by overriding klass.
1113 To be subclassed by overriding klass.
1069 """
1114 """
1070 klass = None
1115 klass = None
1071 _valid_defaults = SequenceTypes
1116 _valid_defaults = SequenceTypes
1072 _trait = None
1117 _trait = None
1073
1118
1074 def __init__(self, trait=None, default_value=None, allow_none=True,
1119 def __init__(self, trait=None, default_value=None, allow_none=True,
1075 **metadata):
1120 **metadata):
1076 """Create a container trait type from a list, set, or tuple.
1121 """Create a container trait type from a list, set, or tuple.
1077
1122
1078 The default value is created by doing ``List(default_value)``,
1123 The default value is created by doing ``List(default_value)``,
1079 which creates a copy of the ``default_value``.
1124 which creates a copy of the ``default_value``.
1080
1125
1081 ``trait`` can be specified, which restricts the type of elements
1126 ``trait`` can be specified, which restricts the type of elements
1082 in the container to that TraitType.
1127 in the container to that TraitType.
1083
1128
1084 If only one arg is given and it is not a Trait, it is taken as
1129 If only one arg is given and it is not a Trait, it is taken as
1085 ``default_value``:
1130 ``default_value``:
1086
1131
1087 ``c = List([1,2,3])``
1132 ``c = List([1,2,3])``
1088
1133
1089 Parameters
1134 Parameters
1090 ----------
1135 ----------
1091
1136
1092 trait : TraitType [ optional ]
1137 trait : TraitType [ optional ]
1093 the type for restricting the contents of the Container. If unspecified,
1138 the type for restricting the contents of the Container. If unspecified,
1094 types are not checked.
1139 types are not checked.
1095
1140
1096 default_value : SequenceType [ optional ]
1141 default_value : SequenceType [ optional ]
1097 The default value for the Trait. Must be list/tuple/set, and
1142 The default value for the Trait. Must be list/tuple/set, and
1098 will be cast to the container type.
1143 will be cast to the container type.
1099
1144
1100 allow_none : Bool [ default True ]
1145 allow_none : Bool [ default True ]
1101 Whether to allow the value to be None
1146 Whether to allow the value to be None
1102
1147
1103 **metadata : any
1148 **metadata : any
1104 further keys for extensions to the Trait (e.g. config)
1149 further keys for extensions to the Trait (e.g. config)
1105
1150
1106 """
1151 """
1107 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1152 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1108
1153
1109 # allow List([values]):
1154 # allow List([values]):
1110 if default_value is None and not istrait(trait):
1155 if default_value is None and not istrait(trait):
1111 default_value = trait
1156 default_value = trait
1112 trait = None
1157 trait = None
1113
1158
1114 if default_value is None:
1159 if default_value is None:
1115 args = ()
1160 args = ()
1116 elif isinstance(default_value, self._valid_defaults):
1161 elif isinstance(default_value, self._valid_defaults):
1117 args = (default_value,)
1162 args = (default_value,)
1118 else:
1163 else:
1119 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1164 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1120
1165
1121 if istrait(trait):
1166 if istrait(trait):
1122 self._trait = trait()
1167 self._trait = trait()
1123 self._trait.name = 'element'
1168 self._trait.name = 'element'
1124 elif trait is not None:
1169 elif trait is not None:
1125 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1170 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1126
1171
1127 super(Container,self).__init__(klass=self.klass, args=args,
1172 super(Container,self).__init__(klass=self.klass, args=args,
1128 allow_none=allow_none, **metadata)
1173 allow_none=allow_none, **metadata)
1129
1174
1130 def element_error(self, obj, element, validator):
1175 def element_error(self, obj, element, validator):
1131 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1176 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1132 % (self.name, class_of(obj), validator.info(), repr_type(element))
1177 % (self.name, class_of(obj), validator.info(), repr_type(element))
1133 raise TraitError(e)
1178 raise TraitError(e)
1134
1179
1135 def validate(self, obj, value):
1180 def validate(self, obj, value):
1136 value = super(Container, self).validate(obj, value)
1181 value = super(Container, self).validate(obj, value)
1137 if value is None:
1182 if value is None:
1138 return value
1183 return value
1139
1184
1140 value = self.validate_elements(obj, value)
1185 value = self.validate_elements(obj, value)
1141
1186
1142 return value
1187 return value
1143
1188
1144 def validate_elements(self, obj, value):
1189 def validate_elements(self, obj, value):
1145 validated = []
1190 validated = []
1146 if self._trait is None or isinstance(self._trait, Any):
1191 if self._trait is None or isinstance(self._trait, Any):
1147 return value
1192 return value
1148 for v in value:
1193 for v in value:
1149 try:
1194 try:
1150 v = self._trait.validate(obj, v)
1195 v = self._trait.validate(obj, v)
1151 except TraitError:
1196 except TraitError:
1152 self.element_error(obj, v, self._trait)
1197 self.element_error(obj, v, self._trait)
1153 else:
1198 else:
1154 validated.append(v)
1199 validated.append(v)
1155 return self.klass(validated)
1200 return self.klass(validated)
1156
1201
1157
1202
1158 class List(Container):
1203 class List(Container):
1159 """An instance of a Python list."""
1204 """An instance of a Python list."""
1160 klass = list
1205 klass = list
1161
1206
1162 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint,
1207 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxint,
1163 allow_none=True, **metadata):
1208 allow_none=True, **metadata):
1164 """Create a List trait type from a list, set, or tuple.
1209 """Create a List trait type from a list, set, or tuple.
1165
1210
1166 The default value is created by doing ``List(default_value)``,
1211 The default value is created by doing ``List(default_value)``,
1167 which creates a copy of the ``default_value``.
1212 which creates a copy of the ``default_value``.
1168
1213
1169 ``trait`` can be specified, which restricts the type of elements
1214 ``trait`` can be specified, which restricts the type of elements
1170 in the container to that TraitType.
1215 in the container to that TraitType.
1171
1216
1172 If only one arg is given and it is not a Trait, it is taken as
1217 If only one arg is given and it is not a Trait, it is taken as
1173 ``default_value``:
1218 ``default_value``:
1174
1219
1175 ``c = List([1,2,3])``
1220 ``c = List([1,2,3])``
1176
1221
1177 Parameters
1222 Parameters
1178 ----------
1223 ----------
1179
1224
1180 trait : TraitType [ optional ]
1225 trait : TraitType [ optional ]
1181 the type for restricting the contents of the Container. If unspecified,
1226 the type for restricting the contents of the Container. If unspecified,
1182 types are not checked.
1227 types are not checked.
1183
1228
1184 default_value : SequenceType [ optional ]
1229 default_value : SequenceType [ optional ]
1185 The default value for the Trait. Must be list/tuple/set, and
1230 The default value for the Trait. Must be list/tuple/set, and
1186 will be cast to the container type.
1231 will be cast to the container type.
1187
1232
1188 minlen : Int [ default 0 ]
1233 minlen : Int [ default 0 ]
1189 The minimum length of the input list
1234 The minimum length of the input list
1190
1235
1191 maxlen : Int [ default sys.maxint ]
1236 maxlen : Int [ default sys.maxint ]
1192 The maximum length of the input list
1237 The maximum length of the input list
1193
1238
1194 allow_none : Bool [ default True ]
1239 allow_none : Bool [ default True ]
1195 Whether to allow the value to be None
1240 Whether to allow the value to be None
1196
1241
1197 **metadata : any
1242 **metadata : any
1198 further keys for extensions to the Trait (e.g. config)
1243 further keys for extensions to the Trait (e.g. config)
1199
1244
1200 """
1245 """
1201 self._minlen = minlen
1246 self._minlen = minlen
1202 self._maxlen = maxlen
1247 self._maxlen = maxlen
1203 super(List, self).__init__(trait=trait, default_value=default_value,
1248 super(List, self).__init__(trait=trait, default_value=default_value,
1204 allow_none=allow_none, **metadata)
1249 allow_none=allow_none, **metadata)
1205
1250
1206 def length_error(self, obj, value):
1251 def length_error(self, obj, value):
1207 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1252 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1208 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1253 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1209 raise TraitError(e)
1254 raise TraitError(e)
1210
1255
1211 def validate_elements(self, obj, value):
1256 def validate_elements(self, obj, value):
1212 length = len(value)
1257 length = len(value)
1213 if length < self._minlen or length > self._maxlen:
1258 if length < self._minlen or length > self._maxlen:
1214 self.length_error(obj, value)
1259 self.length_error(obj, value)
1215
1260
1216 return super(List, self).validate_elements(obj, value)
1261 return super(List, self).validate_elements(obj, value)
1217
1262
1218
1263
1219 class Set(Container):
1264 class Set(Container):
1220 """An instance of a Python set."""
1265 """An instance of a Python set."""
1221 klass = set
1266 klass = set
1222
1267
1223 class Tuple(Container):
1268 class Tuple(Container):
1224 """An instance of a Python tuple."""
1269 """An instance of a Python tuple."""
1225 klass = tuple
1270 klass = tuple
1226
1271
1227 def __init__(self, *traits, **metadata):
1272 def __init__(self, *traits, **metadata):
1228 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1273 """Tuple(*traits, default_value=None, allow_none=True, **medatata)
1229
1274
1230 Create a tuple from a list, set, or tuple.
1275 Create a tuple from a list, set, or tuple.
1231
1276
1232 Create a fixed-type tuple with Traits:
1277 Create a fixed-type tuple with Traits:
1233
1278
1234 ``t = Tuple(Int, Str, CStr)``
1279 ``t = Tuple(Int, Str, CStr)``
1235
1280
1236 would be length 3, with Int,Str,CStr for each element.
1281 would be length 3, with Int,Str,CStr for each element.
1237
1282
1238 If only one arg is given and it is not a Trait, it is taken as
1283 If only one arg is given and it is not a Trait, it is taken as
1239 default_value:
1284 default_value:
1240
1285
1241 ``t = Tuple((1,2,3))``
1286 ``t = Tuple((1,2,3))``
1242
1287
1243 Otherwise, ``default_value`` *must* be specified by keyword.
1288 Otherwise, ``default_value`` *must* be specified by keyword.
1244
1289
1245 Parameters
1290 Parameters
1246 ----------
1291 ----------
1247
1292
1248 *traits : TraitTypes [ optional ]
1293 *traits : TraitTypes [ optional ]
1249 the tsype for restricting the contents of the Tuple. If unspecified,
1294 the tsype for restricting the contents of the Tuple. If unspecified,
1250 types are not checked. If specified, then each positional argument
1295 types are not checked. If specified, then each positional argument
1251 corresponds to an element of the tuple. Tuples defined with traits
1296 corresponds to an element of the tuple. Tuples defined with traits
1252 are of fixed length.
1297 are of fixed length.
1253
1298
1254 default_value : SequenceType [ optional ]
1299 default_value : SequenceType [ optional ]
1255 The default value for the Tuple. Must be list/tuple/set, and
1300 The default value for the Tuple. Must be list/tuple/set, and
1256 will be cast to a tuple. If `traits` are specified, the
1301 will be cast to a tuple. If `traits` are specified, the
1257 `default_value` must conform to the shape and type they specify.
1302 `default_value` must conform to the shape and type they specify.
1258
1303
1259 allow_none : Bool [ default True ]
1304 allow_none : Bool [ default True ]
1260 Whether to allow the value to be None
1305 Whether to allow the value to be None
1261
1306
1262 **metadata : any
1307 **metadata : any
1263 further keys for extensions to the Trait (e.g. config)
1308 further keys for extensions to the Trait (e.g. config)
1264
1309
1265 """
1310 """
1266 default_value = metadata.pop('default_value', None)
1311 default_value = metadata.pop('default_value', None)
1267 allow_none = metadata.pop('allow_none', True)
1312 allow_none = metadata.pop('allow_none', True)
1268
1313
1269 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1314 istrait = lambda t: isinstance(t, type) and issubclass(t, TraitType)
1270
1315
1271 # allow Tuple((values,)):
1316 # allow Tuple((values,)):
1272 if len(traits) == 1 and default_value is None and not istrait(traits[0]):
1317 if len(traits) == 1 and default_value is None and not istrait(traits[0]):
1273 default_value = traits[0]
1318 default_value = traits[0]
1274 traits = ()
1319 traits = ()
1275
1320
1276 if default_value is None:
1321 if default_value is None:
1277 args = ()
1322 args = ()
1278 elif isinstance(default_value, self._valid_defaults):
1323 elif isinstance(default_value, self._valid_defaults):
1279 args = (default_value,)
1324 args = (default_value,)
1280 else:
1325 else:
1281 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1326 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1282
1327
1283 self._traits = []
1328 self._traits = []
1284 for trait in traits:
1329 for trait in traits:
1285 t = trait()
1330 t = trait()
1286 t.name = 'element'
1331 t.name = 'element'
1287 self._traits.append(t)
1332 self._traits.append(t)
1288
1333
1289 if self._traits and default_value is None:
1334 if self._traits and default_value is None:
1290 # don't allow default to be an empty container if length is specified
1335 # don't allow default to be an empty container if length is specified
1291 args = None
1336 args = None
1292 super(Container,self).__init__(klass=self.klass, args=args,
1337 super(Container,self).__init__(klass=self.klass, args=args,
1293 allow_none=allow_none, **metadata)
1338 allow_none=allow_none, **metadata)
1294
1339
1295 def validate_elements(self, obj, value):
1340 def validate_elements(self, obj, value):
1296 if not self._traits:
1341 if not self._traits:
1297 # nothing to validate
1342 # nothing to validate
1298 return value
1343 return value
1299 if len(value) != len(self._traits):
1344 if len(value) != len(self._traits):
1300 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1345 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1301 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1346 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1302 raise TraitError(e)
1347 raise TraitError(e)
1303
1348
1304 validated = []
1349 validated = []
1305 for t,v in zip(self._traits, value):
1350 for t,v in zip(self._traits, value):
1306 try:
1351 try:
1307 v = t.validate(obj, v)
1352 v = t.validate(obj, v)
1308 except TraitError:
1353 except TraitError:
1309 self.element_error(obj, v, t)
1354 self.element_error(obj, v, t)
1310 else:
1355 else:
1311 validated.append(v)
1356 validated.append(v)
1312 return tuple(validated)
1357 return tuple(validated)
1313
1358
1314
1359
1315 class Dict(Instance):
1360 class Dict(Instance):
1316 """An instance of a Python dict."""
1361 """An instance of a Python dict."""
1317
1362
1318 def __init__(self, default_value=None, allow_none=True, **metadata):
1363 def __init__(self, default_value=None, allow_none=True, **metadata):
1319 """Create a dict trait type from a dict.
1364 """Create a dict trait type from a dict.
1320
1365
1321 The default value is created by doing ``dict(default_value)``,
1366 The default value is created by doing ``dict(default_value)``,
1322 which creates a copy of the ``default_value``.
1367 which creates a copy of the ``default_value``.
1323 """
1368 """
1324 if default_value is None:
1369 if default_value is None:
1325 args = ((),)
1370 args = ((),)
1326 elif isinstance(default_value, dict):
1371 elif isinstance(default_value, dict):
1327 args = (default_value,)
1372 args = (default_value,)
1328 elif isinstance(default_value, SequenceTypes):
1373 elif isinstance(default_value, SequenceTypes):
1329 args = (default_value,)
1374 args = (default_value,)
1330 else:
1375 else:
1331 raise TypeError('default value of Dict was %s' % default_value)
1376 raise TypeError('default value of Dict was %s' % default_value)
1332
1377
1333 super(Dict,self).__init__(klass=dict, args=args,
1378 super(Dict,self).__init__(klass=dict, args=args,
1334 allow_none=allow_none, **metadata)
1379 allow_none=allow_none, **metadata)
1335
1380
1336 class TCPAddress(TraitType):
1381 class TCPAddress(TraitType):
1337 """A trait for an (ip, port) tuple.
1382 """A trait for an (ip, port) tuple.
1338
1383
1339 This allows for both IPv4 IP addresses as well as hostnames.
1384 This allows for both IPv4 IP addresses as well as hostnames.
1340 """
1385 """
1341
1386
1342 default_value = ('127.0.0.1', 0)
1387 default_value = ('127.0.0.1', 0)
1343 info_text = 'an (ip, port) tuple'
1388 info_text = 'an (ip, port) tuple'
1344
1389
1345 def validate(self, obj, value):
1390 def validate(self, obj, value):
1346 if isinstance(value, tuple):
1391 if isinstance(value, tuple):
1347 if len(value) == 2:
1392 if len(value) == 2:
1348 if isinstance(value[0], basestring) and isinstance(value[1], int):
1393 if isinstance(value[0], basestring) and isinstance(value[1], int):
1349 port = value[1]
1394 port = value[1]
1350 if port >= 0 and port <= 65535:
1395 if port >= 0 and port <= 65535:
1351 return value
1396 return value
1352 self.error(obj, value)
1397 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now