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