##// END OF EJS Templates
inherit from Unicode
Sylvain Corlay -
Show More
@@ -1,29 +1,28 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Trait types for html widgets.
3 Trait types for html widgets.
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import re
9 import re
10 from IPython.utils import traitlets
10 from IPython.utils import traitlets
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Utilities
13 # Utilities
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 _color_names = ['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred ', 'indigo ', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']
16 _color_names = ['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'honeydew', 'hotpink', 'indianred ', 'indigo ', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen']
17 _color_re = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
17 _color_re = re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$')
18
18
19
19
20 class Color(traitlets._CoercedString):
20 class Color(traitlets.Unicode):
21 """A string holding a valid HTML color such as 'blue', '#060482', '#A80'"""
21 """A string holding a valid HTML color such as 'blue', '#060482', '#A80'"""
22
22
23 info_text = 'a valid HTML color'
23 info_text = 'a valid HTML color'
24
24
25 def validate(self, obj, value):
25 def validate(self, obj, value):
26 value = self._coerce_str(obj, value)
27 if value.lower() in _color_names or _color_re.match(value):
26 if value.lower() in _color_names or _color_re.match(value):
28 return value
27 return value
29 self.error(obj, value) No newline at end of file
28 self.error(obj, value)
@@ -1,1808 +1,1803 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 Inheritance diagram:
31 Inheritance diagram:
32
32
33 .. inheritance-diagram:: IPython.utils.traitlets
33 .. inheritance-diagram:: IPython.utils.traitlets
34 :parts: 3
34 :parts: 3
35 """
35 """
36
36
37 # Copyright (c) IPython Development Team.
37 # Copyright (c) IPython Development Team.
38 # Distributed under the terms of the Modified BSD License.
38 # Distributed under the terms of the Modified BSD License.
39 #
39 #
40 # Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
40 # Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
41 # also under the terms of the Modified BSD License.
41 # also under the terms of the Modified BSD License.
42
42
43 import contextlib
43 import contextlib
44 import inspect
44 import inspect
45 import re
45 import re
46 import sys
46 import sys
47 import types
47 import types
48 from types import FunctionType
48 from types import FunctionType
49 try:
49 try:
50 from types import ClassType, InstanceType
50 from types import ClassType, InstanceType
51 ClassTypes = (ClassType, type)
51 ClassTypes = (ClassType, type)
52 except:
52 except:
53 ClassTypes = (type,)
53 ClassTypes = (type,)
54 from warnings import warn
54 from warnings import warn
55
55
56 from .importstring import import_item
56 from .importstring import import_item
57 from IPython.utils import py3compat
57 from IPython.utils import py3compat
58 from IPython.utils import eventful
58 from IPython.utils import eventful
59 from IPython.utils.py3compat import iteritems, string_types
59 from IPython.utils.py3compat import iteritems, string_types
60 from IPython.testing.skipdoctest import skip_doctest
60 from IPython.testing.skipdoctest import skip_doctest
61
61
62 SequenceTypes = (list, tuple, set, frozenset)
62 SequenceTypes = (list, tuple, set, frozenset)
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Basic classes
65 # Basic classes
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67
67
68
68
69 class NoDefaultSpecified ( object ): pass
69 class NoDefaultSpecified ( object ): pass
70 NoDefaultSpecified = NoDefaultSpecified()
70 NoDefaultSpecified = NoDefaultSpecified()
71
71
72
72
73 class Undefined ( object ): pass
73 class Undefined ( object ): pass
74 Undefined = Undefined()
74 Undefined = Undefined()
75
75
76 class TraitError(Exception):
76 class TraitError(Exception):
77 pass
77 pass
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Utilities
80 # Utilities
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82
82
83
83
84 def class_of ( object ):
84 def class_of ( object ):
85 """ Returns a string containing the class name of an object with the
85 """ Returns a string containing the class name of an object with the
86 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
86 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
87 'a PlotValue').
87 'a PlotValue').
88 """
88 """
89 if isinstance( object, py3compat.string_types ):
89 if isinstance( object, py3compat.string_types ):
90 return add_article( object )
90 return add_article( object )
91
91
92 return add_article( object.__class__.__name__ )
92 return add_article( object.__class__.__name__ )
93
93
94
94
95 def add_article ( name ):
95 def add_article ( name ):
96 """ Returns a string containing the correct indefinite article ('a' or 'an')
96 """ Returns a string containing the correct indefinite article ('a' or 'an')
97 prefixed to the specified string.
97 prefixed to the specified string.
98 """
98 """
99 if name[:1].lower() in 'aeiou':
99 if name[:1].lower() in 'aeiou':
100 return 'an ' + name
100 return 'an ' + name
101
101
102 return 'a ' + name
102 return 'a ' + name
103
103
104
104
105 def repr_type(obj):
105 def repr_type(obj):
106 """ Return a string representation of a value and its type for readable
106 """ Return a string representation of a value and its type for readable
107 error messages.
107 error messages.
108 """
108 """
109 the_type = type(obj)
109 the_type = type(obj)
110 if (not py3compat.PY3) and the_type is InstanceType:
110 if (not py3compat.PY3) and the_type is InstanceType:
111 # Old-style class.
111 # Old-style class.
112 the_type = obj.__class__
112 the_type = obj.__class__
113 msg = '%r %r' % (obj, the_type)
113 msg = '%r %r' % (obj, the_type)
114 return msg
114 return msg
115
115
116
116
117 def is_trait(t):
117 def is_trait(t):
118 """ Returns whether the given value is an instance or subclass of TraitType.
118 """ Returns whether the given value is an instance or subclass of TraitType.
119 """
119 """
120 return (isinstance(t, TraitType) or
120 return (isinstance(t, TraitType) or
121 (isinstance(t, type) and issubclass(t, TraitType)))
121 (isinstance(t, type) and issubclass(t, TraitType)))
122
122
123
123
124 def parse_notifier_name(name):
124 def parse_notifier_name(name):
125 """Convert the name argument to a list of names.
125 """Convert the name argument to a list of names.
126
126
127 Examples
127 Examples
128 --------
128 --------
129
129
130 >>> parse_notifier_name('a')
130 >>> parse_notifier_name('a')
131 ['a']
131 ['a']
132 >>> parse_notifier_name(['a','b'])
132 >>> parse_notifier_name(['a','b'])
133 ['a', 'b']
133 ['a', 'b']
134 >>> parse_notifier_name(None)
134 >>> parse_notifier_name(None)
135 ['anytrait']
135 ['anytrait']
136 """
136 """
137 if isinstance(name, string_types):
137 if isinstance(name, string_types):
138 return [name]
138 return [name]
139 elif name is None:
139 elif name is None:
140 return ['anytrait']
140 return ['anytrait']
141 elif isinstance(name, (list, tuple)):
141 elif isinstance(name, (list, tuple)):
142 for n in name:
142 for n in name:
143 assert isinstance(n, string_types), "names must be strings"
143 assert isinstance(n, string_types), "names must be strings"
144 return name
144 return name
145
145
146
146
147 class _SimpleTest:
147 class _SimpleTest:
148 def __init__ ( self, value ): self.value = value
148 def __init__ ( self, value ): self.value = value
149 def __call__ ( self, test ):
149 def __call__ ( self, test ):
150 return test == self.value
150 return test == self.value
151 def __repr__(self):
151 def __repr__(self):
152 return "<SimpleTest(%r)" % self.value
152 return "<SimpleTest(%r)" % self.value
153 def __str__(self):
153 def __str__(self):
154 return self.__repr__()
154 return self.__repr__()
155
155
156
156
157 def getmembers(object, predicate=None):
157 def getmembers(object, predicate=None):
158 """A safe version of inspect.getmembers that handles missing attributes.
158 """A safe version of inspect.getmembers that handles missing attributes.
159
159
160 This is useful when there are descriptor based attributes that for
160 This is useful when there are descriptor based attributes that for
161 some reason raise AttributeError even though they exist. This happens
161 some reason raise AttributeError even though they exist. This happens
162 in zope.inteface with the __provides__ attribute.
162 in zope.inteface with the __provides__ attribute.
163 """
163 """
164 results = []
164 results = []
165 for key in dir(object):
165 for key in dir(object):
166 try:
166 try:
167 value = getattr(object, key)
167 value = getattr(object, key)
168 except AttributeError:
168 except AttributeError:
169 pass
169 pass
170 else:
170 else:
171 if not predicate or predicate(value):
171 if not predicate or predicate(value):
172 results.append((key, value))
172 results.append((key, value))
173 results.sort()
173 results.sort()
174 return results
174 return results
175
175
176 def _validate_link(*tuples):
176 def _validate_link(*tuples):
177 """Validate arguments for traitlet link functions"""
177 """Validate arguments for traitlet link functions"""
178 for t in tuples:
178 for t in tuples:
179 if not len(t) == 2:
179 if not len(t) == 2:
180 raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
180 raise TypeError("Each linked traitlet must be specified as (HasTraits, 'trait_name'), not %r" % t)
181 obj, trait_name = t
181 obj, trait_name = t
182 if not isinstance(obj, HasTraits):
182 if not isinstance(obj, HasTraits):
183 raise TypeError("Each object must be HasTraits, not %r" % type(obj))
183 raise TypeError("Each object must be HasTraits, not %r" % type(obj))
184 if not trait_name in obj.traits():
184 if not trait_name in obj.traits():
185 raise TypeError("%r has no trait %r" % (obj, trait_name))
185 raise TypeError("%r has no trait %r" % (obj, trait_name))
186
186
187 @skip_doctest
187 @skip_doctest
188 class link(object):
188 class link(object):
189 """Link traits from different objects together so they remain in sync.
189 """Link traits from different objects together so they remain in sync.
190
190
191 Parameters
191 Parameters
192 ----------
192 ----------
193 *args : pairs of objects/attributes
193 *args : pairs of objects/attributes
194
194
195 Examples
195 Examples
196 --------
196 --------
197
197
198 >>> c = link((obj1, 'value'), (obj2, 'value'), (obj3, 'value'))
198 >>> c = link((obj1, 'value'), (obj2, 'value'), (obj3, 'value'))
199 >>> obj1.value = 5 # updates other objects as well
199 >>> obj1.value = 5 # updates other objects as well
200 """
200 """
201 updating = False
201 updating = False
202 def __init__(self, *args):
202 def __init__(self, *args):
203 if len(args) < 2:
203 if len(args) < 2:
204 raise TypeError('At least two traitlets must be provided.')
204 raise TypeError('At least two traitlets must be provided.')
205 _validate_link(*args)
205 _validate_link(*args)
206
206
207 self.objects = {}
207 self.objects = {}
208
208
209 initial = getattr(args[0][0], args[0][1])
209 initial = getattr(args[0][0], args[0][1])
210 for obj, attr in args:
210 for obj, attr in args:
211 setattr(obj, attr, initial)
211 setattr(obj, attr, initial)
212
212
213 callback = self._make_closure(obj, attr)
213 callback = self._make_closure(obj, attr)
214 obj.on_trait_change(callback, attr)
214 obj.on_trait_change(callback, attr)
215 self.objects[(obj, attr)] = callback
215 self.objects[(obj, attr)] = callback
216
216
217 @contextlib.contextmanager
217 @contextlib.contextmanager
218 def _busy_updating(self):
218 def _busy_updating(self):
219 self.updating = True
219 self.updating = True
220 try:
220 try:
221 yield
221 yield
222 finally:
222 finally:
223 self.updating = False
223 self.updating = False
224
224
225 def _make_closure(self, sending_obj, sending_attr):
225 def _make_closure(self, sending_obj, sending_attr):
226 def update(name, old, new):
226 def update(name, old, new):
227 self._update(sending_obj, sending_attr, new)
227 self._update(sending_obj, sending_attr, new)
228 return update
228 return update
229
229
230 def _update(self, sending_obj, sending_attr, new):
230 def _update(self, sending_obj, sending_attr, new):
231 if self.updating:
231 if self.updating:
232 return
232 return
233 with self._busy_updating():
233 with self._busy_updating():
234 for obj, attr in self.objects.keys():
234 for obj, attr in self.objects.keys():
235 setattr(obj, attr, new)
235 setattr(obj, attr, new)
236
236
237 def unlink(self):
237 def unlink(self):
238 for key, callback in self.objects.items():
238 for key, callback in self.objects.items():
239 (obj, attr) = key
239 (obj, attr) = key
240 obj.on_trait_change(callback, attr, remove=True)
240 obj.on_trait_change(callback, attr, remove=True)
241
241
242 @skip_doctest
242 @skip_doctest
243 class directional_link(object):
243 class directional_link(object):
244 """Link the trait of a source object with traits of target objects.
244 """Link the trait of a source object with traits of target objects.
245
245
246 Parameters
246 Parameters
247 ----------
247 ----------
248 source : pair of object, name
248 source : pair of object, name
249 targets : pairs of objects/attributes
249 targets : pairs of objects/attributes
250
250
251 Examples
251 Examples
252 --------
252 --------
253
253
254 >>> c = directional_link((src, 'value'), (tgt1, 'value'), (tgt2, 'value'))
254 >>> c = directional_link((src, 'value'), (tgt1, 'value'), (tgt2, 'value'))
255 >>> src.value = 5 # updates target objects
255 >>> src.value = 5 # updates target objects
256 >>> tgt1.value = 6 # does not update other objects
256 >>> tgt1.value = 6 # does not update other objects
257 """
257 """
258 updating = False
258 updating = False
259
259
260 def __init__(self, source, *targets):
260 def __init__(self, source, *targets):
261 if len(targets) < 1:
261 if len(targets) < 1:
262 raise TypeError('At least two traitlets must be provided.')
262 raise TypeError('At least two traitlets must be provided.')
263 _validate_link(source, *targets)
263 _validate_link(source, *targets)
264 self.source = source
264 self.source = source
265 self.targets = targets
265 self.targets = targets
266
266
267 # Update current value
267 # Update current value
268 src_attr_value = getattr(source[0], source[1])
268 src_attr_value = getattr(source[0], source[1])
269 for obj, attr in targets:
269 for obj, attr in targets:
270 setattr(obj, attr, src_attr_value)
270 setattr(obj, attr, src_attr_value)
271
271
272 # Wire
272 # Wire
273 self.source[0].on_trait_change(self._update, self.source[1])
273 self.source[0].on_trait_change(self._update, self.source[1])
274
274
275 @contextlib.contextmanager
275 @contextlib.contextmanager
276 def _busy_updating(self):
276 def _busy_updating(self):
277 self.updating = True
277 self.updating = True
278 try:
278 try:
279 yield
279 yield
280 finally:
280 finally:
281 self.updating = False
281 self.updating = False
282
282
283 def _update(self, name, old, new):
283 def _update(self, name, old, new):
284 if self.updating:
284 if self.updating:
285 return
285 return
286 with self._busy_updating():
286 with self._busy_updating():
287 for obj, attr in self.targets:
287 for obj, attr in self.targets:
288 setattr(obj, attr, new)
288 setattr(obj, attr, new)
289
289
290 def unlink(self):
290 def unlink(self):
291 self.source[0].on_trait_change(self._update, self.source[1], remove=True)
291 self.source[0].on_trait_change(self._update, self.source[1], remove=True)
292 self.source = None
292 self.source = None
293 self.targets = []
293 self.targets = []
294
294
295 dlink = directional_link
295 dlink = directional_link
296
296
297 #-----------------------------------------------------------------------------
297 #-----------------------------------------------------------------------------
298 # Base TraitType for all traits
298 # Base TraitType for all traits
299 #-----------------------------------------------------------------------------
299 #-----------------------------------------------------------------------------
300
300
301
301
302 class TraitType(object):
302 class TraitType(object):
303 """A base class for all trait descriptors.
303 """A base class for all trait descriptors.
304
304
305 Notes
305 Notes
306 -----
306 -----
307 Our implementation of traits is based on Python's descriptor
307 Our implementation of traits is based on Python's descriptor
308 prototol. This class is the base class for all such descriptors. The
308 prototol. This class is the base class for all such descriptors. The
309 only magic we use is a custom metaclass for the main :class:`HasTraits`
309 only magic we use is a custom metaclass for the main :class:`HasTraits`
310 class that does the following:
310 class that does the following:
311
311
312 1. Sets the :attr:`name` attribute of every :class:`TraitType`
312 1. Sets the :attr:`name` attribute of every :class:`TraitType`
313 instance in the class dict to the name of the attribute.
313 instance in the class dict to the name of the attribute.
314 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
314 2. Sets the :attr:`this_class` attribute of every :class:`TraitType`
315 instance in the class dict to the *class* that declared the trait.
315 instance in the class dict to the *class* that declared the trait.
316 This is used by the :class:`This` trait to allow subclasses to
316 This is used by the :class:`This` trait to allow subclasses to
317 accept superclasses for :class:`This` values.
317 accept superclasses for :class:`This` values.
318 """
318 """
319
319
320
320
321 metadata = {}
321 metadata = {}
322 default_value = Undefined
322 default_value = Undefined
323 allow_none = False
323 allow_none = False
324 info_text = 'any value'
324 info_text = 'any value'
325
325
326 def __init__(self, default_value=NoDefaultSpecified, allow_none=None, **metadata):
326 def __init__(self, default_value=NoDefaultSpecified, allow_none=None, **metadata):
327 """Create a TraitType.
327 """Create a TraitType.
328 """
328 """
329 if default_value is not NoDefaultSpecified:
329 if default_value is not NoDefaultSpecified:
330 self.default_value = default_value
330 self.default_value = default_value
331 if allow_none is not None:
331 if allow_none is not None:
332 self.allow_none = allow_none
332 self.allow_none = allow_none
333
333
334 if 'default' in metadata:
334 if 'default' in metadata:
335 # Warn the user that they probably meant default_value.
335 # Warn the user that they probably meant default_value.
336 warn(
336 warn(
337 "Parameter 'default' passed to TraitType. "
337 "Parameter 'default' passed to TraitType. "
338 "Did you mean 'default_value'?"
338 "Did you mean 'default_value'?"
339 )
339 )
340
340
341 if len(metadata) > 0:
341 if len(metadata) > 0:
342 if len(self.metadata) > 0:
342 if len(self.metadata) > 0:
343 self._metadata = self.metadata.copy()
343 self._metadata = self.metadata.copy()
344 self._metadata.update(metadata)
344 self._metadata.update(metadata)
345 else:
345 else:
346 self._metadata = metadata
346 self._metadata = metadata
347 else:
347 else:
348 self._metadata = self.metadata
348 self._metadata = self.metadata
349
349
350 self.init()
350 self.init()
351
351
352 def init(self):
352 def init(self):
353 pass
353 pass
354
354
355 def get_default_value(self):
355 def get_default_value(self):
356 """Create a new instance of the default value."""
356 """Create a new instance of the default value."""
357 return self.default_value
357 return self.default_value
358
358
359 def instance_init(self):
359 def instance_init(self):
360 """Part of the initialization which may depends on the underlying
360 """Part of the initialization which may depends on the underlying
361 HasTraits instance.
361 HasTraits instance.
362
362
363 It is typically overloaded for specific trait types.
363 It is typically overloaded for specific trait types.
364
364
365 This method is called by :meth:`HasTraits.__new__` and in the
365 This method is called by :meth:`HasTraits.__new__` and in the
366 :meth:`TraitType.instance_init` method of trait types holding
366 :meth:`TraitType.instance_init` method of trait types holding
367 other trait types.
367 other trait types.
368 """
368 """
369 pass
369 pass
370
370
371 def init_default_value(self, obj):
371 def init_default_value(self, obj):
372 """Instantiate the default value for the trait type.
372 """Instantiate the default value for the trait type.
373
373
374 This method is called by :meth:`TraitType.set_default_value` in the
374 This method is called by :meth:`TraitType.set_default_value` in the
375 case a default value is provided at construction time or later when
375 case a default value is provided at construction time or later when
376 accessing the trait value for the first time in
376 accessing the trait value for the first time in
377 :meth:`HasTraits.__get__`.
377 :meth:`HasTraits.__get__`.
378 """
378 """
379 value = self.get_default_value()
379 value = self.get_default_value()
380 value = self._validate(obj, value)
380 value = self._validate(obj, value)
381 obj._trait_values[self.name] = value
381 obj._trait_values[self.name] = value
382 return value
382 return value
383
383
384 def set_default_value(self, obj):
384 def set_default_value(self, obj):
385 """Set the default value on a per instance basis.
385 """Set the default value on a per instance basis.
386
386
387 This method is called by :meth:`HasTraits.__new__` to instantiate and
387 This method is called by :meth:`HasTraits.__new__` to instantiate and
388 validate the default value. The creation and validation of
388 validate the default value. The creation and validation of
389 default values must be delayed until the parent :class:`HasTraits`
389 default values must be delayed until the parent :class:`HasTraits`
390 class has been instantiated.
390 class has been instantiated.
391 Parameters
391 Parameters
392 ----------
392 ----------
393 obj : :class:`HasTraits` instance
393 obj : :class:`HasTraits` instance
394 The parent :class:`HasTraits` instance that has just been
394 The parent :class:`HasTraits` instance that has just been
395 created.
395 created.
396 """
396 """
397 # Check for a deferred initializer defined in the same class as the
397 # Check for a deferred initializer defined in the same class as the
398 # trait declaration or above.
398 # trait declaration or above.
399 mro = type(obj).mro()
399 mro = type(obj).mro()
400 meth_name = '_%s_default' % self.name
400 meth_name = '_%s_default' % self.name
401 for cls in mro[:mro.index(self.this_class)+1]:
401 for cls in mro[:mro.index(self.this_class)+1]:
402 if meth_name in cls.__dict__:
402 if meth_name in cls.__dict__:
403 break
403 break
404 else:
404 else:
405 # We didn't find one. Do static initialization.
405 # We didn't find one. Do static initialization.
406 self.init_default_value(obj)
406 self.init_default_value(obj)
407 return
407 return
408 # Complete the dynamic initialization.
408 # Complete the dynamic initialization.
409 obj._trait_dyn_inits[self.name] = meth_name
409 obj._trait_dyn_inits[self.name] = meth_name
410
410
411 def __get__(self, obj, cls=None):
411 def __get__(self, obj, cls=None):
412 """Get the value of the trait by self.name for the instance.
412 """Get the value of the trait by self.name for the instance.
413
413
414 Default values are instantiated when :meth:`HasTraits.__new__`
414 Default values are instantiated when :meth:`HasTraits.__new__`
415 is called. Thus by the time this method gets called either the
415 is called. Thus by the time this method gets called either the
416 default value or a user defined value (they called :meth:`__set__`)
416 default value or a user defined value (they called :meth:`__set__`)
417 is in the :class:`HasTraits` instance.
417 is in the :class:`HasTraits` instance.
418 """
418 """
419 if obj is None:
419 if obj is None:
420 return self
420 return self
421 else:
421 else:
422 try:
422 try:
423 value = obj._trait_values[self.name]
423 value = obj._trait_values[self.name]
424 except KeyError:
424 except KeyError:
425 # Check for a dynamic initializer.
425 # Check for a dynamic initializer.
426 if self.name in obj._trait_dyn_inits:
426 if self.name in obj._trait_dyn_inits:
427 method = getattr(obj, obj._trait_dyn_inits[self.name])
427 method = getattr(obj, obj._trait_dyn_inits[self.name])
428 value = method()
428 value = method()
429 # FIXME: Do we really validate here?
429 # FIXME: Do we really validate here?
430 value = self._validate(obj, value)
430 value = self._validate(obj, value)
431 obj._trait_values[self.name] = value
431 obj._trait_values[self.name] = value
432 return value
432 return value
433 else:
433 else:
434 return self.init_default_value(obj)
434 return self.init_default_value(obj)
435 except Exception:
435 except Exception:
436 # HasTraits should call set_default_value to populate
436 # HasTraits should call set_default_value to populate
437 # this. So this should never be reached.
437 # this. So this should never be reached.
438 raise TraitError('Unexpected error in TraitType: '
438 raise TraitError('Unexpected error in TraitType: '
439 'default value not set properly')
439 'default value not set properly')
440 else:
440 else:
441 return value
441 return value
442
442
443 def __set__(self, obj, value):
443 def __set__(self, obj, value):
444 new_value = self._validate(obj, value)
444 new_value = self._validate(obj, value)
445 try:
445 try:
446 old_value = obj._trait_values[self.name]
446 old_value = obj._trait_values[self.name]
447 except KeyError:
447 except KeyError:
448 old_value = None
448 old_value = None
449
449
450 obj._trait_values[self.name] = new_value
450 obj._trait_values[self.name] = new_value
451 try:
451 try:
452 silent = bool(old_value == new_value)
452 silent = bool(old_value == new_value)
453 except:
453 except:
454 # if there is an error in comparing, default to notify
454 # if there is an error in comparing, default to notify
455 silent = False
455 silent = False
456 if silent is not True:
456 if silent is not True:
457 # we explicitly compare silent to True just in case the equality
457 # we explicitly compare silent to True just in case the equality
458 # comparison above returns something other than True/False
458 # comparison above returns something other than True/False
459 obj._notify_trait(self.name, old_value, new_value)
459 obj._notify_trait(self.name, old_value, new_value)
460
460
461 def _validate(self, obj, value):
461 def _validate(self, obj, value):
462 if value is None and self.allow_none:
462 if value is None and self.allow_none:
463 return value
463 return value
464 if hasattr(self, 'validate'):
464 if hasattr(self, 'validate'):
465 value = self.validate(obj, value)
465 value = self.validate(obj, value)
466 try:
466 try:
467 obj_validate = getattr(obj, '_%s_validate' % self.name)
467 obj_validate = getattr(obj, '_%s_validate' % self.name)
468 except (AttributeError, RuntimeError):
468 except (AttributeError, RuntimeError):
469 # Qt mixins raise RuntimeError on missing attrs accessed before __init__
469 # Qt mixins raise RuntimeError on missing attrs accessed before __init__
470 pass
470 pass
471 else:
471 else:
472 value = obj_validate(value, self)
472 value = obj_validate(value, self)
473 return value
473 return value
474
474
475 def __or__(self, other):
475 def __or__(self, other):
476 if isinstance(other, Union):
476 if isinstance(other, Union):
477 return Union([self] + other.trait_types)
477 return Union([self] + other.trait_types)
478 else:
478 else:
479 return Union([self, other])
479 return Union([self, other])
480
480
481 def info(self):
481 def info(self):
482 return self.info_text
482 return self.info_text
483
483
484 def error(self, obj, value):
484 def error(self, obj, value):
485 if obj is not None:
485 if obj is not None:
486 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
486 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
487 % (self.name, class_of(obj),
487 % (self.name, class_of(obj),
488 self.info(), repr_type(value))
488 self.info(), repr_type(value))
489 else:
489 else:
490 e = "The '%s' trait must be %s, but a value of %r was specified." \
490 e = "The '%s' trait must be %s, but a value of %r was specified." \
491 % (self.name, self.info(), repr_type(value))
491 % (self.name, self.info(), repr_type(value))
492 raise TraitError(e)
492 raise TraitError(e)
493
493
494 def get_metadata(self, key, default=None):
494 def get_metadata(self, key, default=None):
495 return getattr(self, '_metadata', {}).get(key, default)
495 return getattr(self, '_metadata', {}).get(key, default)
496
496
497 def set_metadata(self, key, value):
497 def set_metadata(self, key, value):
498 getattr(self, '_metadata', {})[key] = value
498 getattr(self, '_metadata', {})[key] = value
499
499
500
500
501 #-----------------------------------------------------------------------------
501 #-----------------------------------------------------------------------------
502 # The HasTraits implementation
502 # The HasTraits implementation
503 #-----------------------------------------------------------------------------
503 #-----------------------------------------------------------------------------
504
504
505
505
506 class MetaHasTraits(type):
506 class MetaHasTraits(type):
507 """A metaclass for HasTraits.
507 """A metaclass for HasTraits.
508
508
509 This metaclass makes sure that any TraitType class attributes are
509 This metaclass makes sure that any TraitType class attributes are
510 instantiated and sets their name attribute.
510 instantiated and sets their name attribute.
511 """
511 """
512
512
513 def __new__(mcls, name, bases, classdict):
513 def __new__(mcls, name, bases, classdict):
514 """Create the HasTraits class.
514 """Create the HasTraits class.
515
515
516 This instantiates all TraitTypes in the class dict and sets their
516 This instantiates all TraitTypes in the class dict and sets their
517 :attr:`name` attribute.
517 :attr:`name` attribute.
518 """
518 """
519 # print "MetaHasTraitlets (mcls, name): ", mcls, name
519 # print "MetaHasTraitlets (mcls, name): ", mcls, name
520 # print "MetaHasTraitlets (bases): ", bases
520 # print "MetaHasTraitlets (bases): ", bases
521 # print "MetaHasTraitlets (classdict): ", classdict
521 # print "MetaHasTraitlets (classdict): ", classdict
522 for k,v in iteritems(classdict):
522 for k,v in iteritems(classdict):
523 if isinstance(v, TraitType):
523 if isinstance(v, TraitType):
524 v.name = k
524 v.name = k
525 elif inspect.isclass(v):
525 elif inspect.isclass(v):
526 if issubclass(v, TraitType):
526 if issubclass(v, TraitType):
527 vinst = v()
527 vinst = v()
528 vinst.name = k
528 vinst.name = k
529 classdict[k] = vinst
529 classdict[k] = vinst
530 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
530 return super(MetaHasTraits, mcls).__new__(mcls, name, bases, classdict)
531
531
532 def __init__(cls, name, bases, classdict):
532 def __init__(cls, name, bases, classdict):
533 """Finish initializing the HasTraits class.
533 """Finish initializing the HasTraits class.
534
534
535 This sets the :attr:`this_class` attribute of each TraitType in the
535 This sets the :attr:`this_class` attribute of each TraitType in the
536 class dict to the newly created class ``cls``.
536 class dict to the newly created class ``cls``.
537 """
537 """
538 for k, v in iteritems(classdict):
538 for k, v in iteritems(classdict):
539 if isinstance(v, TraitType):
539 if isinstance(v, TraitType):
540 v.this_class = cls
540 v.this_class = cls
541 super(MetaHasTraits, cls).__init__(name, bases, classdict)
541 super(MetaHasTraits, cls).__init__(name, bases, classdict)
542
542
543 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
543 class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):
544
544
545 def __new__(cls, *args, **kw):
545 def __new__(cls, *args, **kw):
546 # This is needed because object.__new__ only accepts
546 # This is needed because object.__new__ only accepts
547 # the cls argument.
547 # the cls argument.
548 new_meth = super(HasTraits, cls).__new__
548 new_meth = super(HasTraits, cls).__new__
549 if new_meth is object.__new__:
549 if new_meth is object.__new__:
550 inst = new_meth(cls)
550 inst = new_meth(cls)
551 else:
551 else:
552 inst = new_meth(cls, **kw)
552 inst = new_meth(cls, **kw)
553 inst._trait_values = {}
553 inst._trait_values = {}
554 inst._trait_notifiers = {}
554 inst._trait_notifiers = {}
555 inst._trait_dyn_inits = {}
555 inst._trait_dyn_inits = {}
556 # Here we tell all the TraitType instances to set their default
556 # Here we tell all the TraitType instances to set their default
557 # values on the instance.
557 # values on the instance.
558 for key in dir(cls):
558 for key in dir(cls):
559 # Some descriptors raise AttributeError like zope.interface's
559 # Some descriptors raise AttributeError like zope.interface's
560 # __provides__ attributes even though they exist. This causes
560 # __provides__ attributes even though they exist. This causes
561 # AttributeErrors even though they are listed in dir(cls).
561 # AttributeErrors even though they are listed in dir(cls).
562 try:
562 try:
563 value = getattr(cls, key)
563 value = getattr(cls, key)
564 except AttributeError:
564 except AttributeError:
565 pass
565 pass
566 else:
566 else:
567 if isinstance(value, TraitType):
567 if isinstance(value, TraitType):
568 value.instance_init()
568 value.instance_init()
569 if key not in kw:
569 if key not in kw:
570 value.set_default_value(inst)
570 value.set_default_value(inst)
571
571
572 return inst
572 return inst
573
573
574 def __init__(self, *args, **kw):
574 def __init__(self, *args, **kw):
575 # Allow trait values to be set using keyword arguments.
575 # Allow trait values to be set using keyword arguments.
576 # We need to use setattr for this to trigger validation and
576 # We need to use setattr for this to trigger validation and
577 # notifications.
577 # notifications.
578 for key, value in iteritems(kw):
578 for key, value in iteritems(kw):
579 setattr(self, key, value)
579 setattr(self, key, value)
580
580
581 def _notify_trait(self, name, old_value, new_value):
581 def _notify_trait(self, name, old_value, new_value):
582
582
583 # First dynamic ones
583 # First dynamic ones
584 callables = []
584 callables = []
585 callables.extend(self._trait_notifiers.get(name,[]))
585 callables.extend(self._trait_notifiers.get(name,[]))
586 callables.extend(self._trait_notifiers.get('anytrait',[]))
586 callables.extend(self._trait_notifiers.get('anytrait',[]))
587
587
588 # Now static ones
588 # Now static ones
589 try:
589 try:
590 cb = getattr(self, '_%s_changed' % name)
590 cb = getattr(self, '_%s_changed' % name)
591 except:
591 except:
592 pass
592 pass
593 else:
593 else:
594 callables.append(cb)
594 callables.append(cb)
595
595
596 # Call them all now
596 # Call them all now
597 for c in callables:
597 for c in callables:
598 # Traits catches and logs errors here. I allow them to raise
598 # Traits catches and logs errors here. I allow them to raise
599 if callable(c):
599 if callable(c):
600 argspec = inspect.getargspec(c)
600 argspec = inspect.getargspec(c)
601 nargs = len(argspec[0])
601 nargs = len(argspec[0])
602 # Bound methods have an additional 'self' argument
602 # Bound methods have an additional 'self' argument
603 # I don't know how to treat unbound methods, but they
603 # I don't know how to treat unbound methods, but they
604 # can't really be used for callbacks.
604 # can't really be used for callbacks.
605 if isinstance(c, types.MethodType):
605 if isinstance(c, types.MethodType):
606 offset = -1
606 offset = -1
607 else:
607 else:
608 offset = 0
608 offset = 0
609 if nargs + offset == 0:
609 if nargs + offset == 0:
610 c()
610 c()
611 elif nargs + offset == 1:
611 elif nargs + offset == 1:
612 c(name)
612 c(name)
613 elif nargs + offset == 2:
613 elif nargs + offset == 2:
614 c(name, new_value)
614 c(name, new_value)
615 elif nargs + offset == 3:
615 elif nargs + offset == 3:
616 c(name, old_value, new_value)
616 c(name, old_value, new_value)
617 else:
617 else:
618 raise TraitError('a trait changed callback '
618 raise TraitError('a trait changed callback '
619 'must have 0-3 arguments.')
619 'must have 0-3 arguments.')
620 else:
620 else:
621 raise TraitError('a trait changed callback '
621 raise TraitError('a trait changed callback '
622 'must be callable.')
622 'must be callable.')
623
623
624
624
625 def _add_notifiers(self, handler, name):
625 def _add_notifiers(self, handler, name):
626 if name not in self._trait_notifiers:
626 if name not in self._trait_notifiers:
627 nlist = []
627 nlist = []
628 self._trait_notifiers[name] = nlist
628 self._trait_notifiers[name] = nlist
629 else:
629 else:
630 nlist = self._trait_notifiers[name]
630 nlist = self._trait_notifiers[name]
631 if handler not in nlist:
631 if handler not in nlist:
632 nlist.append(handler)
632 nlist.append(handler)
633
633
634 def _remove_notifiers(self, handler, name):
634 def _remove_notifiers(self, handler, name):
635 if name in self._trait_notifiers:
635 if name in self._trait_notifiers:
636 nlist = self._trait_notifiers[name]
636 nlist = self._trait_notifiers[name]
637 try:
637 try:
638 index = nlist.index(handler)
638 index = nlist.index(handler)
639 except ValueError:
639 except ValueError:
640 pass
640 pass
641 else:
641 else:
642 del nlist[index]
642 del nlist[index]
643
643
644 def on_trait_change(self, handler, name=None, remove=False):
644 def on_trait_change(self, handler, name=None, remove=False):
645 """Setup a handler to be called when a trait changes.
645 """Setup a handler to be called when a trait changes.
646
646
647 This is used to setup dynamic notifications of trait changes.
647 This is used to setup dynamic notifications of trait changes.
648
648
649 Static handlers can be created by creating methods on a HasTraits
649 Static handlers can be created by creating methods on a HasTraits
650 subclass with the naming convention '_[traitname]_changed'. Thus,
650 subclass with the naming convention '_[traitname]_changed'. Thus,
651 to create static handler for the trait 'a', create the method
651 to create static handler for the trait 'a', create the method
652 _a_changed(self, name, old, new) (fewer arguments can be used, see
652 _a_changed(self, name, old, new) (fewer arguments can be used, see
653 below).
653 below).
654
654
655 Parameters
655 Parameters
656 ----------
656 ----------
657 handler : callable
657 handler : callable
658 A callable that is called when a trait changes. Its
658 A callable that is called when a trait changes. Its
659 signature can be handler(), handler(name), handler(name, new)
659 signature can be handler(), handler(name), handler(name, new)
660 or handler(name, old, new).
660 or handler(name, old, new).
661 name : list, str, None
661 name : list, str, None
662 If None, the handler will apply to all traits. If a list
662 If None, the handler will apply to all traits. If a list
663 of str, handler will apply to all names in the list. If a
663 of str, handler will apply to all names in the list. If a
664 str, the handler will apply just to that name.
664 str, the handler will apply just to that name.
665 remove : bool
665 remove : bool
666 If False (the default), then install the handler. If True
666 If False (the default), then install the handler. If True
667 then unintall it.
667 then unintall it.
668 """
668 """
669 if remove:
669 if remove:
670 names = parse_notifier_name(name)
670 names = parse_notifier_name(name)
671 for n in names:
671 for n in names:
672 self._remove_notifiers(handler, n)
672 self._remove_notifiers(handler, n)
673 else:
673 else:
674 names = parse_notifier_name(name)
674 names = parse_notifier_name(name)
675 for n in names:
675 for n in names:
676 self._add_notifiers(handler, n)
676 self._add_notifiers(handler, n)
677
677
678 @classmethod
678 @classmethod
679 def class_trait_names(cls, **metadata):
679 def class_trait_names(cls, **metadata):
680 """Get a list of all the names of this class' traits.
680 """Get a list of all the names of this class' traits.
681
681
682 This method is just like the :meth:`trait_names` method,
682 This method is just like the :meth:`trait_names` method,
683 but is unbound.
683 but is unbound.
684 """
684 """
685 return cls.class_traits(**metadata).keys()
685 return cls.class_traits(**metadata).keys()
686
686
687 @classmethod
687 @classmethod
688 def class_traits(cls, **metadata):
688 def class_traits(cls, **metadata):
689 """Get a `dict` of all the traits of this class. The dictionary
689 """Get a `dict` of all the traits of this class. The dictionary
690 is keyed on the name and the values are the TraitType objects.
690 is keyed on the name and the values are the TraitType objects.
691
691
692 This method is just like the :meth:`traits` method, but is unbound.
692 This method is just like the :meth:`traits` method, but is unbound.
693
693
694 The TraitTypes returned don't know anything about the values
694 The TraitTypes returned don't know anything about the values
695 that the various HasTrait's instances are holding.
695 that the various HasTrait's instances are holding.
696
696
697 The metadata kwargs allow functions to be passed in which
697 The metadata kwargs allow functions to be passed in which
698 filter traits based on metadata values. The functions should
698 filter traits based on metadata values. The functions should
699 take a single value as an argument and return a boolean. If
699 take a single value as an argument and return a boolean. If
700 any function returns False, then the trait is not included in
700 any function returns False, then the trait is not included in
701 the output. This does not allow for any simple way of
701 the output. This does not allow for any simple way of
702 testing that a metadata name exists and has any
702 testing that a metadata name exists and has any
703 value because get_metadata returns None if a metadata key
703 value because get_metadata returns None if a metadata key
704 doesn't exist.
704 doesn't exist.
705 """
705 """
706 traits = dict([memb for memb in getmembers(cls) if
706 traits = dict([memb for memb in getmembers(cls) if
707 isinstance(memb[1], TraitType)])
707 isinstance(memb[1], TraitType)])
708
708
709 if len(metadata) == 0:
709 if len(metadata) == 0:
710 return traits
710 return traits
711
711
712 for meta_name, meta_eval in metadata.items():
712 for meta_name, meta_eval in metadata.items():
713 if type(meta_eval) is not FunctionType:
713 if type(meta_eval) is not FunctionType:
714 metadata[meta_name] = _SimpleTest(meta_eval)
714 metadata[meta_name] = _SimpleTest(meta_eval)
715
715
716 result = {}
716 result = {}
717 for name, trait in traits.items():
717 for name, trait in traits.items():
718 for meta_name, meta_eval in metadata.items():
718 for meta_name, meta_eval in metadata.items():
719 if not meta_eval(trait.get_metadata(meta_name)):
719 if not meta_eval(trait.get_metadata(meta_name)):
720 break
720 break
721 else:
721 else:
722 result[name] = trait
722 result[name] = trait
723
723
724 return result
724 return result
725
725
726 def trait_names(self, **metadata):
726 def trait_names(self, **metadata):
727 """Get a list of all the names of this class' traits."""
727 """Get a list of all the names of this class' traits."""
728 return self.traits(**metadata).keys()
728 return self.traits(**metadata).keys()
729
729
730 def traits(self, **metadata):
730 def traits(self, **metadata):
731 """Get a `dict` of all the traits of this class. The dictionary
731 """Get a `dict` of all the traits of this class. The dictionary
732 is keyed on the name and the values are the TraitType objects.
732 is keyed on the name and the values are the TraitType objects.
733
733
734 The TraitTypes returned don't know anything about the values
734 The TraitTypes returned don't know anything about the values
735 that the various HasTrait's instances are holding.
735 that the various HasTrait's instances are holding.
736
736
737 The metadata kwargs allow functions to be passed in which
737 The metadata kwargs allow functions to be passed in which
738 filter traits based on metadata values. The functions should
738 filter traits based on metadata values. The functions should
739 take a single value as an argument and return a boolean. If
739 take a single value as an argument and return a boolean. If
740 any function returns False, then the trait is not included in
740 any function returns False, then the trait is not included in
741 the output. This does not allow for any simple way of
741 the output. This does not allow for any simple way of
742 testing that a metadata name exists and has any
742 testing that a metadata name exists and has any
743 value because get_metadata returns None if a metadata key
743 value because get_metadata returns None if a metadata key
744 doesn't exist.
744 doesn't exist.
745 """
745 """
746 traits = dict([memb for memb in getmembers(self.__class__) if
746 traits = dict([memb for memb in getmembers(self.__class__) if
747 isinstance(memb[1], TraitType)])
747 isinstance(memb[1], TraitType)])
748
748
749 if len(metadata) == 0:
749 if len(metadata) == 0:
750 return traits
750 return traits
751
751
752 for meta_name, meta_eval in metadata.items():
752 for meta_name, meta_eval in metadata.items():
753 if type(meta_eval) is not FunctionType:
753 if type(meta_eval) is not FunctionType:
754 metadata[meta_name] = _SimpleTest(meta_eval)
754 metadata[meta_name] = _SimpleTest(meta_eval)
755
755
756 result = {}
756 result = {}
757 for name, trait in traits.items():
757 for name, trait in traits.items():
758 for meta_name, meta_eval in metadata.items():
758 for meta_name, meta_eval in metadata.items():
759 if not meta_eval(trait.get_metadata(meta_name)):
759 if not meta_eval(trait.get_metadata(meta_name)):
760 break
760 break
761 else:
761 else:
762 result[name] = trait
762 result[name] = trait
763
763
764 return result
764 return result
765
765
766 def trait_metadata(self, traitname, key, default=None):
766 def trait_metadata(self, traitname, key, default=None):
767 """Get metadata values for trait by key."""
767 """Get metadata values for trait by key."""
768 try:
768 try:
769 trait = getattr(self.__class__, traitname)
769 trait = getattr(self.__class__, traitname)
770 except AttributeError:
770 except AttributeError:
771 raise TraitError("Class %s does not have a trait named %s" %
771 raise TraitError("Class %s does not have a trait named %s" %
772 (self.__class__.__name__, traitname))
772 (self.__class__.__name__, traitname))
773 else:
773 else:
774 return trait.get_metadata(key, default)
774 return trait.get_metadata(key, default)
775
775
776 #-----------------------------------------------------------------------------
776 #-----------------------------------------------------------------------------
777 # Actual TraitTypes implementations/subclasses
777 # Actual TraitTypes implementations/subclasses
778 #-----------------------------------------------------------------------------
778 #-----------------------------------------------------------------------------
779
779
780 #-----------------------------------------------------------------------------
780 #-----------------------------------------------------------------------------
781 # TraitTypes subclasses for handling classes and instances of classes
781 # TraitTypes subclasses for handling classes and instances of classes
782 #-----------------------------------------------------------------------------
782 #-----------------------------------------------------------------------------
783
783
784
784
785 class ClassBasedTraitType(TraitType):
785 class ClassBasedTraitType(TraitType):
786 """
786 """
787 A trait with error reporting and string -> type resolution for Type,
787 A trait with error reporting and string -> type resolution for Type,
788 Instance and This.
788 Instance and This.
789 """
789 """
790
790
791 def _resolve_string(self, string):
791 def _resolve_string(self, string):
792 """
792 """
793 Resolve a string supplied for a type into an actual object.
793 Resolve a string supplied for a type into an actual object.
794 """
794 """
795 return import_item(string)
795 return import_item(string)
796
796
797 def error(self, obj, value):
797 def error(self, obj, value):
798 kind = type(value)
798 kind = type(value)
799 if (not py3compat.PY3) and kind is InstanceType:
799 if (not py3compat.PY3) and kind is InstanceType:
800 msg = 'class %s' % value.__class__.__name__
800 msg = 'class %s' % value.__class__.__name__
801 else:
801 else:
802 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
802 msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) )
803
803
804 if obj is not None:
804 if obj is not None:
805 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
805 e = "The '%s' trait of %s instance must be %s, but a value of %s was specified." \
806 % (self.name, class_of(obj),
806 % (self.name, class_of(obj),
807 self.info(), msg)
807 self.info(), msg)
808 else:
808 else:
809 e = "The '%s' trait must be %s, but a value of %r was specified." \
809 e = "The '%s' trait must be %s, but a value of %r was specified." \
810 % (self.name, self.info(), msg)
810 % (self.name, self.info(), msg)
811
811
812 raise TraitError(e)
812 raise TraitError(e)
813
813
814
814
815 class Type(ClassBasedTraitType):
815 class Type(ClassBasedTraitType):
816 """A trait whose value must be a subclass of a specified class."""
816 """A trait whose value must be a subclass of a specified class."""
817
817
818 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
818 def __init__ (self, default_value=None, klass=None, allow_none=True, **metadata ):
819 """Construct a Type trait
819 """Construct a Type trait
820
820
821 A Type trait specifies that its values must be subclasses of
821 A Type trait specifies that its values must be subclasses of
822 a particular class.
822 a particular class.
823
823
824 If only ``default_value`` is given, it is used for the ``klass`` as
824 If only ``default_value`` is given, it is used for the ``klass`` as
825 well.
825 well.
826
826
827 Parameters
827 Parameters
828 ----------
828 ----------
829 default_value : class, str or None
829 default_value : class, str or None
830 The default value must be a subclass of klass. If an str,
830 The default value must be a subclass of klass. If an str,
831 the str must be a fully specified class name, like 'foo.bar.Bah'.
831 the str must be a fully specified class name, like 'foo.bar.Bah'.
832 The string is resolved into real class, when the parent
832 The string is resolved into real class, when the parent
833 :class:`HasTraits` class is instantiated.
833 :class:`HasTraits` class is instantiated.
834 klass : class, str, None
834 klass : class, str, None
835 Values of this trait must be a subclass of klass. The klass
835 Values of this trait must be a subclass of klass. The klass
836 may be specified in a string like: 'foo.bar.MyClass'.
836 may be specified in a string like: 'foo.bar.MyClass'.
837 The string is resolved into real class, when the parent
837 The string is resolved into real class, when the parent
838 :class:`HasTraits` class is instantiated.
838 :class:`HasTraits` class is instantiated.
839 allow_none : bool [ default True ]
839 allow_none : bool [ default True ]
840 Indicates whether None is allowed as an assignable value. Even if
840 Indicates whether None is allowed as an assignable value. Even if
841 ``False``, the default value may be ``None``.
841 ``False``, the default value may be ``None``.
842 """
842 """
843 if default_value is None:
843 if default_value is None:
844 if klass is None:
844 if klass is None:
845 klass = object
845 klass = object
846 elif klass is None:
846 elif klass is None:
847 klass = default_value
847 klass = default_value
848
848
849 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
849 if not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
850 raise TraitError("A Type trait must specify a class.")
850 raise TraitError("A Type trait must specify a class.")
851
851
852 self.klass = klass
852 self.klass = klass
853
853
854 super(Type, self).__init__(default_value, allow_none=allow_none, **metadata)
854 super(Type, self).__init__(default_value, allow_none=allow_none, **metadata)
855
855
856 def validate(self, obj, value):
856 def validate(self, obj, value):
857 """Validates that the value is a valid object instance."""
857 """Validates that the value is a valid object instance."""
858 if isinstance(value, py3compat.string_types):
858 if isinstance(value, py3compat.string_types):
859 try:
859 try:
860 value = self._resolve_string(value)
860 value = self._resolve_string(value)
861 except ImportError:
861 except ImportError:
862 raise TraitError("The '%s' trait of %s instance must be a type, but "
862 raise TraitError("The '%s' trait of %s instance must be a type, but "
863 "%r could not be imported" % (self.name, obj, value))
863 "%r could not be imported" % (self.name, obj, value))
864 try:
864 try:
865 if issubclass(value, self.klass):
865 if issubclass(value, self.klass):
866 return value
866 return value
867 except:
867 except:
868 pass
868 pass
869
869
870 self.error(obj, value)
870 self.error(obj, value)
871
871
872 def info(self):
872 def info(self):
873 """ Returns a description of the trait."""
873 """ Returns a description of the trait."""
874 if isinstance(self.klass, py3compat.string_types):
874 if isinstance(self.klass, py3compat.string_types):
875 klass = self.klass
875 klass = self.klass
876 else:
876 else:
877 klass = self.klass.__name__
877 klass = self.klass.__name__
878 result = 'a subclass of ' + klass
878 result = 'a subclass of ' + klass
879 if self.allow_none:
879 if self.allow_none:
880 return result + ' or None'
880 return result + ' or None'
881 return result
881 return result
882
882
883 def instance_init(self):
883 def instance_init(self):
884 self._resolve_classes()
884 self._resolve_classes()
885 super(Type, self).instance_init()
885 super(Type, self).instance_init()
886
886
887 def _resolve_classes(self):
887 def _resolve_classes(self):
888 if isinstance(self.klass, py3compat.string_types):
888 if isinstance(self.klass, py3compat.string_types):
889 self.klass = self._resolve_string(self.klass)
889 self.klass = self._resolve_string(self.klass)
890 if isinstance(self.default_value, py3compat.string_types):
890 if isinstance(self.default_value, py3compat.string_types):
891 self.default_value = self._resolve_string(self.default_value)
891 self.default_value = self._resolve_string(self.default_value)
892
892
893 def get_default_value(self):
893 def get_default_value(self):
894 return self.default_value
894 return self.default_value
895
895
896
896
897 class DefaultValueGenerator(object):
897 class DefaultValueGenerator(object):
898 """A class for generating new default value instances."""
898 """A class for generating new default value instances."""
899
899
900 def __init__(self, *args, **kw):
900 def __init__(self, *args, **kw):
901 self.args = args
901 self.args = args
902 self.kw = kw
902 self.kw = kw
903
903
904 def generate(self, klass):
904 def generate(self, klass):
905 return klass(*self.args, **self.kw)
905 return klass(*self.args, **self.kw)
906
906
907
907
908 class Instance(ClassBasedTraitType):
908 class Instance(ClassBasedTraitType):
909 """A trait whose value must be an instance of a specified class.
909 """A trait whose value must be an instance of a specified class.
910
910
911 The value can also be an instance of a subclass of the specified class.
911 The value can also be an instance of a subclass of the specified class.
912
912
913 Subclasses can declare default classes by overriding the klass attribute
913 Subclasses can declare default classes by overriding the klass attribute
914 """
914 """
915
915
916 klass = None
916 klass = None
917
917
918 def __init__(self, klass=None, args=None, kw=None,
918 def __init__(self, klass=None, args=None, kw=None,
919 allow_none=True, **metadata ):
919 allow_none=True, **metadata ):
920 """Construct an Instance trait.
920 """Construct an Instance trait.
921
921
922 This trait allows values that are instances of a particular
922 This trait allows values that are instances of a particular
923 class or its subclasses. Our implementation is quite different
923 class or its subclasses. Our implementation is quite different
924 from that of enthough.traits as we don't allow instances to be used
924 from that of enthough.traits as we don't allow instances to be used
925 for klass and we handle the ``args`` and ``kw`` arguments differently.
925 for klass and we handle the ``args`` and ``kw`` arguments differently.
926
926
927 Parameters
927 Parameters
928 ----------
928 ----------
929 klass : class, str
929 klass : class, str
930 The class that forms the basis for the trait. Class names
930 The class that forms the basis for the trait. Class names
931 can also be specified as strings, like 'foo.bar.Bar'.
931 can also be specified as strings, like 'foo.bar.Bar'.
932 args : tuple
932 args : tuple
933 Positional arguments for generating the default value.
933 Positional arguments for generating the default value.
934 kw : dict
934 kw : dict
935 Keyword arguments for generating the default value.
935 Keyword arguments for generating the default value.
936 allow_none : bool [default True]
936 allow_none : bool [default True]
937 Indicates whether None is allowed as a value.
937 Indicates whether None is allowed as a value.
938
938
939 Notes
939 Notes
940 -----
940 -----
941 If both ``args`` and ``kw`` are None, then the default value is None.
941 If both ``args`` and ``kw`` are None, then the default value is None.
942 If ``args`` is a tuple and ``kw`` is a dict, then the default is
942 If ``args`` is a tuple and ``kw`` is a dict, then the default is
943 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
943 created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is
944 None, the None is replaced by ``()`` or ``{}``, respectively.
944 None, the None is replaced by ``()`` or ``{}``, respectively.
945 """
945 """
946 if klass is None:
946 if klass is None:
947 klass = self.klass
947 klass = self.klass
948
948
949 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
949 if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)):
950 self.klass = klass
950 self.klass = klass
951 else:
951 else:
952 raise TraitError('The klass attribute must be a class'
952 raise TraitError('The klass attribute must be a class'
953 ' not: %r' % klass)
953 ' not: %r' % klass)
954
954
955 # self.klass is a class, so handle default_value
955 # self.klass is a class, so handle default_value
956 if args is None and kw is None:
956 if args is None and kw is None:
957 default_value = None
957 default_value = None
958 else:
958 else:
959 if args is None:
959 if args is None:
960 # kw is not None
960 # kw is not None
961 args = ()
961 args = ()
962 elif kw is None:
962 elif kw is None:
963 # args is not None
963 # args is not None
964 kw = {}
964 kw = {}
965
965
966 if not isinstance(kw, dict):
966 if not isinstance(kw, dict):
967 raise TraitError("The 'kw' argument must be a dict or None.")
967 raise TraitError("The 'kw' argument must be a dict or None.")
968 if not isinstance(args, tuple):
968 if not isinstance(args, tuple):
969 raise TraitError("The 'args' argument must be a tuple or None.")
969 raise TraitError("The 'args' argument must be a tuple or None.")
970
970
971 default_value = DefaultValueGenerator(*args, **kw)
971 default_value = DefaultValueGenerator(*args, **kw)
972
972
973 super(Instance, self).__init__(default_value, allow_none=allow_none, **metadata)
973 super(Instance, self).__init__(default_value, allow_none=allow_none, **metadata)
974
974
975 def validate(self, obj, value):
975 def validate(self, obj, value):
976 if isinstance(value, self.klass):
976 if isinstance(value, self.klass):
977 return value
977 return value
978 else:
978 else:
979 self.error(obj, value)
979 self.error(obj, value)
980
980
981 def info(self):
981 def info(self):
982 if isinstance(self.klass, py3compat.string_types):
982 if isinstance(self.klass, py3compat.string_types):
983 klass = self.klass
983 klass = self.klass
984 else:
984 else:
985 klass = self.klass.__name__
985 klass = self.klass.__name__
986 result = class_of(klass)
986 result = class_of(klass)
987 if self.allow_none:
987 if self.allow_none:
988 return result + ' or None'
988 return result + ' or None'
989
989
990 return result
990 return result
991
991
992 def instance_init(self):
992 def instance_init(self):
993 self._resolve_classes()
993 self._resolve_classes()
994 super(Instance, self).instance_init()
994 super(Instance, self).instance_init()
995
995
996 def _resolve_classes(self):
996 def _resolve_classes(self):
997 if isinstance(self.klass, py3compat.string_types):
997 if isinstance(self.klass, py3compat.string_types):
998 self.klass = self._resolve_string(self.klass)
998 self.klass = self._resolve_string(self.klass)
999
999
1000 def get_default_value(self):
1000 def get_default_value(self):
1001 """Instantiate a default value instance.
1001 """Instantiate a default value instance.
1002
1002
1003 This is called when the containing HasTraits classes'
1003 This is called when the containing HasTraits classes'
1004 :meth:`__new__` method is called to ensure that a unique instance
1004 :meth:`__new__` method is called to ensure that a unique instance
1005 is created for each HasTraits instance.
1005 is created for each HasTraits instance.
1006 """
1006 """
1007 dv = self.default_value
1007 dv = self.default_value
1008 if isinstance(dv, DefaultValueGenerator):
1008 if isinstance(dv, DefaultValueGenerator):
1009 return dv.generate(self.klass)
1009 return dv.generate(self.klass)
1010 else:
1010 else:
1011 return dv
1011 return dv
1012
1012
1013
1013
1014 class ForwardDeclaredMixin(object):
1014 class ForwardDeclaredMixin(object):
1015 """
1015 """
1016 Mixin for forward-declared versions of Instance and Type.
1016 Mixin for forward-declared versions of Instance and Type.
1017 """
1017 """
1018 def _resolve_string(self, string):
1018 def _resolve_string(self, string):
1019 """
1019 """
1020 Find the specified class name by looking for it in the module in which
1020 Find the specified class name by looking for it in the module in which
1021 our this_class attribute was defined.
1021 our this_class attribute was defined.
1022 """
1022 """
1023 modname = self.this_class.__module__
1023 modname = self.this_class.__module__
1024 return import_item('.'.join([modname, string]))
1024 return import_item('.'.join([modname, string]))
1025
1025
1026
1026
1027 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
1027 class ForwardDeclaredType(ForwardDeclaredMixin, Type):
1028 """
1028 """
1029 Forward-declared version of Type.
1029 Forward-declared version of Type.
1030 """
1030 """
1031 pass
1031 pass
1032
1032
1033
1033
1034 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1034 class ForwardDeclaredInstance(ForwardDeclaredMixin, Instance):
1035 """
1035 """
1036 Forward-declared version of Instance.
1036 Forward-declared version of Instance.
1037 """
1037 """
1038 pass
1038 pass
1039
1039
1040
1040
1041 class This(ClassBasedTraitType):
1041 class This(ClassBasedTraitType):
1042 """A trait for instances of the class containing this trait.
1042 """A trait for instances of the class containing this trait.
1043
1043
1044 Because how how and when class bodies are executed, the ``This``
1044 Because how how and when class bodies are executed, the ``This``
1045 trait can only have a default value of None. This, and because we
1045 trait can only have a default value of None. This, and because we
1046 always validate default values, ``allow_none`` is *always* true.
1046 always validate default values, ``allow_none`` is *always* true.
1047 """
1047 """
1048
1048
1049 info_text = 'an instance of the same type as the receiver or None'
1049 info_text = 'an instance of the same type as the receiver or None'
1050
1050
1051 def __init__(self, **metadata):
1051 def __init__(self, **metadata):
1052 super(This, self).__init__(None, **metadata)
1052 super(This, self).__init__(None, **metadata)
1053
1053
1054 def validate(self, obj, value):
1054 def validate(self, obj, value):
1055 # What if value is a superclass of obj.__class__? This is
1055 # What if value is a superclass of obj.__class__? This is
1056 # complicated if it was the superclass that defined the This
1056 # complicated if it was the superclass that defined the This
1057 # trait.
1057 # trait.
1058 if isinstance(value, self.this_class) or (value is None):
1058 if isinstance(value, self.this_class) or (value is None):
1059 return value
1059 return value
1060 else:
1060 else:
1061 self.error(obj, value)
1061 self.error(obj, value)
1062
1062
1063
1063
1064 class Union(TraitType):
1064 class Union(TraitType):
1065 """A trait type representing a Union type."""
1065 """A trait type representing a Union type."""
1066
1066
1067 def __init__(self, trait_types, **metadata):
1067 def __init__(self, trait_types, **metadata):
1068 """Construct a Union trait.
1068 """Construct a Union trait.
1069
1069
1070 This trait allows values that are allowed by at least one of the
1070 This trait allows values that are allowed by at least one of the
1071 specified trait types. A Union traitlet cannot have metadata on
1071 specified trait types. A Union traitlet cannot have metadata on
1072 its own, besides the metadata of the listed types.
1072 its own, besides the metadata of the listed types.
1073
1073
1074 Parameters
1074 Parameters
1075 ----------
1075 ----------
1076 trait_types: sequence
1076 trait_types: sequence
1077 The list of trait types of length at least 1.
1077 The list of trait types of length at least 1.
1078
1078
1079 Notes
1079 Notes
1080 -----
1080 -----
1081 Union([Float(), Bool(), Int()]) attempts to validate the provided values
1081 Union([Float(), Bool(), Int()]) attempts to validate the provided values
1082 with the validation function of Float, then Bool, and finally Int.
1082 with the validation function of Float, then Bool, and finally Int.
1083 """
1083 """
1084 self.trait_types = trait_types
1084 self.trait_types = trait_types
1085 self.info_text = " or ".join([tt.info_text for tt in self.trait_types])
1085 self.info_text = " or ".join([tt.info_text for tt in self.trait_types])
1086 self.default_value = self.trait_types[0].get_default_value()
1086 self.default_value = self.trait_types[0].get_default_value()
1087 super(Union, self).__init__(**metadata)
1087 super(Union, self).__init__(**metadata)
1088
1088
1089 def instance_init(self):
1089 def instance_init(self):
1090 for trait_type in self.trait_types:
1090 for trait_type in self.trait_types:
1091 trait_type.name = self.name
1091 trait_type.name = self.name
1092 trait_type.this_class = self.this_class
1092 trait_type.this_class = self.this_class
1093 trait_type.instance_init()
1093 trait_type.instance_init()
1094 super(Union, self).instance_init()
1094 super(Union, self).instance_init()
1095
1095
1096 def validate(self, obj, value):
1096 def validate(self, obj, value):
1097 for trait_type in self.trait_types:
1097 for trait_type in self.trait_types:
1098 try:
1098 try:
1099 v = trait_type._validate(obj, value)
1099 v = trait_type._validate(obj, value)
1100 self._metadata = trait_type._metadata
1100 self._metadata = trait_type._metadata
1101 return v
1101 return v
1102 except TraitError:
1102 except TraitError:
1103 continue
1103 continue
1104 self.error(obj, value)
1104 self.error(obj, value)
1105
1105
1106 def __or__(self, other):
1106 def __or__(self, other):
1107 if isinstance(other, Union):
1107 if isinstance(other, Union):
1108 return Union(self.trait_types + other.trait_types)
1108 return Union(self.trait_types + other.trait_types)
1109 else:
1109 else:
1110 return Union(self.trait_types + [other])
1110 return Union(self.trait_types + [other])
1111
1111
1112 #-----------------------------------------------------------------------------
1112 #-----------------------------------------------------------------------------
1113 # Basic TraitTypes implementations/subclasses
1113 # Basic TraitTypes implementations/subclasses
1114 #-----------------------------------------------------------------------------
1114 #-----------------------------------------------------------------------------
1115
1115
1116
1116
1117 class Any(TraitType):
1117 class Any(TraitType):
1118 default_value = None
1118 default_value = None
1119 info_text = 'any value'
1119 info_text = 'any value'
1120
1120
1121
1121
1122 class Int(TraitType):
1122 class Int(TraitType):
1123 """An int trait."""
1123 """An int trait."""
1124
1124
1125 default_value = 0
1125 default_value = 0
1126 info_text = 'an int'
1126 info_text = 'an int'
1127
1127
1128 def validate(self, obj, value):
1128 def validate(self, obj, value):
1129 if isinstance(value, int):
1129 if isinstance(value, int):
1130 return value
1130 return value
1131 self.error(obj, value)
1131 self.error(obj, value)
1132
1132
1133 class CInt(Int):
1133 class CInt(Int):
1134 """A casting version of the int trait."""
1134 """A casting version of the int trait."""
1135
1135
1136 def validate(self, obj, value):
1136 def validate(self, obj, value):
1137 try:
1137 try:
1138 return int(value)
1138 return int(value)
1139 except:
1139 except:
1140 self.error(obj, value)
1140 self.error(obj, value)
1141
1141
1142 if py3compat.PY3:
1142 if py3compat.PY3:
1143 Long, CLong = Int, CInt
1143 Long, CLong = Int, CInt
1144 Integer = Int
1144 Integer = Int
1145 else:
1145 else:
1146 class Long(TraitType):
1146 class Long(TraitType):
1147 """A long integer trait."""
1147 """A long integer trait."""
1148
1148
1149 default_value = 0
1149 default_value = 0
1150 info_text = 'a long'
1150 info_text = 'a long'
1151
1151
1152 def validate(self, obj, value):
1152 def validate(self, obj, value):
1153 if isinstance(value, long):
1153 if isinstance(value, long):
1154 return value
1154 return value
1155 if isinstance(value, int):
1155 if isinstance(value, int):
1156 return long(value)
1156 return long(value)
1157 self.error(obj, value)
1157 self.error(obj, value)
1158
1158
1159
1159
1160 class CLong(Long):
1160 class CLong(Long):
1161 """A casting version of the long integer trait."""
1161 """A casting version of the long integer trait."""
1162
1162
1163 def validate(self, obj, value):
1163 def validate(self, obj, value):
1164 try:
1164 try:
1165 return long(value)
1165 return long(value)
1166 except:
1166 except:
1167 self.error(obj, value)
1167 self.error(obj, value)
1168
1168
1169 class Integer(TraitType):
1169 class Integer(TraitType):
1170 """An integer trait.
1170 """An integer trait.
1171
1171
1172 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
1172 Longs that are unnecessary (<= sys.maxint) are cast to ints."""
1173
1173
1174 default_value = 0
1174 default_value = 0
1175 info_text = 'an integer'
1175 info_text = 'an integer'
1176
1176
1177 def validate(self, obj, value):
1177 def validate(self, obj, value):
1178 if isinstance(value, int):
1178 if isinstance(value, int):
1179 return value
1179 return value
1180 if isinstance(value, long):
1180 if isinstance(value, long):
1181 # downcast longs that fit in int:
1181 # downcast longs that fit in int:
1182 # note that int(n > sys.maxint) returns a long, so
1182 # note that int(n > sys.maxint) returns a long, so
1183 # we don't need a condition on this cast
1183 # we don't need a condition on this cast
1184 return int(value)
1184 return int(value)
1185 if sys.platform == "cli":
1185 if sys.platform == "cli":
1186 from System import Int64
1186 from System import Int64
1187 if isinstance(value, Int64):
1187 if isinstance(value, Int64):
1188 return int(value)
1188 return int(value)
1189 self.error(obj, value)
1189 self.error(obj, value)
1190
1190
1191
1191
1192 class Float(TraitType):
1192 class Float(TraitType):
1193 """A float trait."""
1193 """A float trait."""
1194
1194
1195 default_value = 0.0
1195 default_value = 0.0
1196 info_text = 'a float'
1196 info_text = 'a float'
1197
1197
1198 def validate(self, obj, value):
1198 def validate(self, obj, value):
1199 if isinstance(value, float):
1199 if isinstance(value, float):
1200 return value
1200 return value
1201 if isinstance(value, int):
1201 if isinstance(value, int):
1202 return float(value)
1202 return float(value)
1203 self.error(obj, value)
1203 self.error(obj, value)
1204
1204
1205
1205
1206 class CFloat(Float):
1206 class CFloat(Float):
1207 """A casting version of the float trait."""
1207 """A casting version of the float trait."""
1208
1208
1209 def validate(self, obj, value):
1209 def validate(self, obj, value):
1210 try:
1210 try:
1211 return float(value)
1211 return float(value)
1212 except:
1212 except:
1213 self.error(obj, value)
1213 self.error(obj, value)
1214
1214
1215 class Complex(TraitType):
1215 class Complex(TraitType):
1216 """A trait for complex numbers."""
1216 """A trait for complex numbers."""
1217
1217
1218 default_value = 0.0 + 0.0j
1218 default_value = 0.0 + 0.0j
1219 info_text = 'a complex number'
1219 info_text = 'a complex number'
1220
1220
1221 def validate(self, obj, value):
1221 def validate(self, obj, value):
1222 if isinstance(value, complex):
1222 if isinstance(value, complex):
1223 return value
1223 return value
1224 if isinstance(value, (float, int)):
1224 if isinstance(value, (float, int)):
1225 return complex(value)
1225 return complex(value)
1226 self.error(obj, value)
1226 self.error(obj, value)
1227
1227
1228
1228
1229 class CComplex(Complex):
1229 class CComplex(Complex):
1230 """A casting version of the complex number trait."""
1230 """A casting version of the complex number trait."""
1231
1231
1232 def validate (self, obj, value):
1232 def validate (self, obj, value):
1233 try:
1233 try:
1234 return complex(value)
1234 return complex(value)
1235 except:
1235 except:
1236 self.error(obj, value)
1236 self.error(obj, value)
1237
1237
1238 # We should always be explicit about whether we're using bytes or unicode, both
1238 # We should always be explicit about whether we're using bytes or unicode, both
1239 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1239 # for Python 3 conversion and for reliable unicode behaviour on Python 2. So
1240 # we don't have a Str type.
1240 # we don't have a Str type.
1241 class Bytes(TraitType):
1241 class Bytes(TraitType):
1242 """A trait for byte strings."""
1242 """A trait for byte strings."""
1243
1243
1244 default_value = b''
1244 default_value = b''
1245 info_text = 'a bytes object'
1245 info_text = 'a bytes object'
1246
1246
1247 def validate(self, obj, value):
1247 def validate(self, obj, value):
1248 if isinstance(value, bytes):
1248 if isinstance(value, bytes):
1249 return value
1249 return value
1250 self.error(obj, value)
1250 self.error(obj, value)
1251
1251
1252
1252
1253 class CBytes(Bytes):
1253 class CBytes(Bytes):
1254 """A casting version of the byte string trait."""
1254 """A casting version of the byte string trait."""
1255
1255
1256 def validate(self, obj, value):
1256 def validate(self, obj, value):
1257 try:
1257 try:
1258 return bytes(value)
1258 return bytes(value)
1259 except:
1259 except:
1260 self.error(obj, value)
1260 self.error(obj, value)
1261
1261
1262
1262
1263 class Unicode(TraitType):
1263 class Unicode(TraitType):
1264 """A trait for unicode strings."""
1264 """A trait for unicode strings."""
1265
1265
1266 default_value = u''
1266 default_value = u''
1267 info_text = 'a unicode string'
1267 info_text = 'a unicode string'
1268
1268
1269 def validate(self, obj, value):
1269 def validate(self, obj, value):
1270 if isinstance(value, py3compat.unicode_type):
1270 if isinstance(value, py3compat.unicode_type):
1271 return value
1271 return value
1272 if isinstance(value, bytes):
1272 if isinstance(value, bytes):
1273 try:
1273 try:
1274 return value.decode('ascii', 'strict')
1274 return value.decode('ascii', 'strict')
1275 except UnicodeDecodeError:
1275 except UnicodeDecodeError:
1276 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1276 msg = "Could not decode {!r} for unicode trait '{}' of {} instance."
1277 raise TraitError(msg.format(value, self.name, class_of(obj)))
1277 raise TraitError(msg.format(value, self.name, class_of(obj)))
1278 self.error(obj, value)
1278 self.error(obj, value)
1279
1279
1280
1280
1281 class CUnicode(Unicode):
1281 class CUnicode(Unicode):
1282 """A casting version of the unicode trait."""
1282 """A casting version of the unicode trait."""
1283
1283
1284 def validate(self, obj, value):
1284 def validate(self, obj, value):
1285 try:
1285 try:
1286 return py3compat.unicode_type(value)
1286 return py3compat.unicode_type(value)
1287 except:
1287 except:
1288 self.error(obj, value)
1288 self.error(obj, value)
1289
1289
1290
1290
1291 class _CoercedString(TraitType):
1291 class ObjectName(TraitType):
1292 """A string holding a valid object name in this version of Python.
1293
1294 This does not check that the name exists in any scope."""
1295 info_text = "a valid object identifier in Python"
1292
1296
1293 if py3compat.PY3:
1297 if py3compat.PY3:
1294 # Python 3:
1298 # Python 3:
1295 _coerce_str = staticmethod(lambda _,s: s)
1299 coerce_str = staticmethod(lambda _,s: s)
1300
1296 else:
1301 else:
1297 # Python 2:
1302 # Python 2:
1298 def _coerce_str(self, obj, value):
1303 def coerce_str(self, obj, value):
1299 "In Python 2, coerce ascii-only unicode to str"
1304 "In Python 2, coerce ascii-only unicode to str"
1300 if isinstance(value, unicode):
1305 if isinstance(value, unicode):
1301 try:
1306 try:
1302 return str(value)
1307 return str(value)
1303 except UnicodeEncodeError:
1308 except UnicodeEncodeError:
1304 self.error(obj, value)
1309 self.error(obj, value)
1305 return value
1310 return value
1306
1311
1307
1308 class ObjectName(_CoercedString):
1309 """A string holding a valid object name in this version of Python.
1310
1311 This does not check that the name exists in any scope."""
1312
1313 info_text = "a valid object identifier in Python"
1314
1315 def validate(self, obj, value):
1312 def validate(self, obj, value):
1316 value = self._coerce_str(obj, value)
1313 value = self.coerce_str(obj, value)
1317
1314
1318 if isinstance(value, string_types) and py3compat.isidentifier(value):
1315 if isinstance(value, string_types) and py3compat.isidentifier(value):
1319 return value
1316 return value
1320 self.error(obj, value)
1317 self.error(obj, value)
1321
1318
1322
1323 class DottedObjectName(ObjectName):
1319 class DottedObjectName(ObjectName):
1324 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1320 """A string holding a valid dotted object name in Python, such as A.b3._c"""
1325
1326 def validate(self, obj, value):
1321 def validate(self, obj, value):
1327 value = self._coerce_str(obj, value)
1322 value = self.coerce_str(obj, value)
1328
1323
1329 if isinstance(value, string_types) and py3compat.isidentifier(value, dotted=True):
1324 if isinstance(value, string_types) and py3compat.isidentifier(value, dotted=True):
1330 return value
1325 return value
1331 self.error(obj, value)
1326 self.error(obj, value)
1332
1327
1333
1328
1334 class Bool(TraitType):
1329 class Bool(TraitType):
1335 """A boolean (True, False) trait."""
1330 """A boolean (True, False) trait."""
1336
1331
1337 default_value = False
1332 default_value = False
1338 info_text = 'a boolean'
1333 info_text = 'a boolean'
1339
1334
1340 def validate(self, obj, value):
1335 def validate(self, obj, value):
1341 if isinstance(value, bool):
1336 if isinstance(value, bool):
1342 return value
1337 return value
1343 self.error(obj, value)
1338 self.error(obj, value)
1344
1339
1345
1340
1346 class CBool(Bool):
1341 class CBool(Bool):
1347 """A casting version of the boolean trait."""
1342 """A casting version of the boolean trait."""
1348
1343
1349 def validate(self, obj, value):
1344 def validate(self, obj, value):
1350 try:
1345 try:
1351 return bool(value)
1346 return bool(value)
1352 except:
1347 except:
1353 self.error(obj, value)
1348 self.error(obj, value)
1354
1349
1355
1350
1356 class Enum(TraitType):
1351 class Enum(TraitType):
1357 """An enum that whose value must be in a given sequence."""
1352 """An enum that whose value must be in a given sequence."""
1358
1353
1359 def __init__(self, values, default_value=None, **metadata):
1354 def __init__(self, values, default_value=None, **metadata):
1360 self.values = values
1355 self.values = values
1361 super(Enum, self).__init__(default_value, **metadata)
1356 super(Enum, self).__init__(default_value, **metadata)
1362
1357
1363 def validate(self, obj, value):
1358 def validate(self, obj, value):
1364 if value in self.values:
1359 if value in self.values:
1365 return value
1360 return value
1366 self.error(obj, value)
1361 self.error(obj, value)
1367
1362
1368 def info(self):
1363 def info(self):
1369 """ Returns a description of the trait."""
1364 """ Returns a description of the trait."""
1370 result = 'any of ' + repr(self.values)
1365 result = 'any of ' + repr(self.values)
1371 if self.allow_none:
1366 if self.allow_none:
1372 return result + ' or None'
1367 return result + ' or None'
1373 return result
1368 return result
1374
1369
1375 class CaselessStrEnum(Enum):
1370 class CaselessStrEnum(Enum):
1376 """An enum of strings that are caseless in validate."""
1371 """An enum of strings that are caseless in validate."""
1377
1372
1378 def validate(self, obj, value):
1373 def validate(self, obj, value):
1379 if not isinstance(value, py3compat.string_types):
1374 if not isinstance(value, py3compat.string_types):
1380 self.error(obj, value)
1375 self.error(obj, value)
1381
1376
1382 for v in self.values:
1377 for v in self.values:
1383 if v.lower() == value.lower():
1378 if v.lower() == value.lower():
1384 return v
1379 return v
1385 self.error(obj, value)
1380 self.error(obj, value)
1386
1381
1387 class Container(Instance):
1382 class Container(Instance):
1388 """An instance of a container (list, set, etc.)
1383 """An instance of a container (list, set, etc.)
1389
1384
1390 To be subclassed by overriding klass.
1385 To be subclassed by overriding klass.
1391 """
1386 """
1392 klass = None
1387 klass = None
1393 _cast_types = ()
1388 _cast_types = ()
1394 _valid_defaults = SequenceTypes
1389 _valid_defaults = SequenceTypes
1395 _trait = None
1390 _trait = None
1396
1391
1397 def __init__(self, trait=None, default_value=None, allow_none=False,
1392 def __init__(self, trait=None, default_value=None, allow_none=False,
1398 **metadata):
1393 **metadata):
1399 """Create a container trait type from a list, set, or tuple.
1394 """Create a container trait type from a list, set, or tuple.
1400
1395
1401 The default value is created by doing ``List(default_value)``,
1396 The default value is created by doing ``List(default_value)``,
1402 which creates a copy of the ``default_value``.
1397 which creates a copy of the ``default_value``.
1403
1398
1404 ``trait`` can be specified, which restricts the type of elements
1399 ``trait`` can be specified, which restricts the type of elements
1405 in the container to that TraitType.
1400 in the container to that TraitType.
1406
1401
1407 If only one arg is given and it is not a Trait, it is taken as
1402 If only one arg is given and it is not a Trait, it is taken as
1408 ``default_value``:
1403 ``default_value``:
1409
1404
1410 ``c = List([1,2,3])``
1405 ``c = List([1,2,3])``
1411
1406
1412 Parameters
1407 Parameters
1413 ----------
1408 ----------
1414
1409
1415 trait : TraitType [ optional ]
1410 trait : TraitType [ optional ]
1416 the type for restricting the contents of the Container. If unspecified,
1411 the type for restricting the contents of the Container. If unspecified,
1417 types are not checked.
1412 types are not checked.
1418
1413
1419 default_value : SequenceType [ optional ]
1414 default_value : SequenceType [ optional ]
1420 The default value for the Trait. Must be list/tuple/set, and
1415 The default value for the Trait. Must be list/tuple/set, and
1421 will be cast to the container type.
1416 will be cast to the container type.
1422
1417
1423 allow_none : bool [ default False ]
1418 allow_none : bool [ default False ]
1424 Whether to allow the value to be None
1419 Whether to allow the value to be None
1425
1420
1426 **metadata : any
1421 **metadata : any
1427 further keys for extensions to the Trait (e.g. config)
1422 further keys for extensions to the Trait (e.g. config)
1428
1423
1429 """
1424 """
1430 # allow List([values]):
1425 # allow List([values]):
1431 if default_value is None and not is_trait(trait):
1426 if default_value is None and not is_trait(trait):
1432 default_value = trait
1427 default_value = trait
1433 trait = None
1428 trait = None
1434
1429
1435 if default_value is None:
1430 if default_value is None:
1436 args = ()
1431 args = ()
1437 elif isinstance(default_value, self._valid_defaults):
1432 elif isinstance(default_value, self._valid_defaults):
1438 args = (default_value,)
1433 args = (default_value,)
1439 else:
1434 else:
1440 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1435 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1441
1436
1442 if is_trait(trait):
1437 if is_trait(trait):
1443 self._trait = trait() if isinstance(trait, type) else trait
1438 self._trait = trait() if isinstance(trait, type) else trait
1444 self._trait.name = 'element'
1439 self._trait.name = 'element'
1445 elif trait is not None:
1440 elif trait is not None:
1446 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1441 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1447
1442
1448 super(Container,self).__init__(klass=self.klass, args=args,
1443 super(Container,self).__init__(klass=self.klass, args=args,
1449 allow_none=allow_none, **metadata)
1444 allow_none=allow_none, **metadata)
1450
1445
1451 def element_error(self, obj, element, validator):
1446 def element_error(self, obj, element, validator):
1452 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1447 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1453 % (self.name, class_of(obj), validator.info(), repr_type(element))
1448 % (self.name, class_of(obj), validator.info(), repr_type(element))
1454 raise TraitError(e)
1449 raise TraitError(e)
1455
1450
1456 def validate(self, obj, value):
1451 def validate(self, obj, value):
1457 if isinstance(value, self._cast_types):
1452 if isinstance(value, self._cast_types):
1458 value = self.klass(value)
1453 value = self.klass(value)
1459 value = super(Container, self).validate(obj, value)
1454 value = super(Container, self).validate(obj, value)
1460 if value is None:
1455 if value is None:
1461 return value
1456 return value
1462
1457
1463 value = self.validate_elements(obj, value)
1458 value = self.validate_elements(obj, value)
1464
1459
1465 return value
1460 return value
1466
1461
1467 def validate_elements(self, obj, value):
1462 def validate_elements(self, obj, value):
1468 validated = []
1463 validated = []
1469 if self._trait is None or isinstance(self._trait, Any):
1464 if self._trait is None or isinstance(self._trait, Any):
1470 return value
1465 return value
1471 for v in value:
1466 for v in value:
1472 try:
1467 try:
1473 v = self._trait._validate(obj, v)
1468 v = self._trait._validate(obj, v)
1474 except TraitError:
1469 except TraitError:
1475 self.element_error(obj, v, self._trait)
1470 self.element_error(obj, v, self._trait)
1476 else:
1471 else:
1477 validated.append(v)
1472 validated.append(v)
1478 return self.klass(validated)
1473 return self.klass(validated)
1479
1474
1480 def instance_init(self):
1475 def instance_init(self):
1481 if isinstance(self._trait, TraitType):
1476 if isinstance(self._trait, TraitType):
1482 self._trait.this_class = self.this_class
1477 self._trait.this_class = self.this_class
1483 self._trait.instance_init()
1478 self._trait.instance_init()
1484 super(Container, self).instance_init()
1479 super(Container, self).instance_init()
1485
1480
1486
1481
1487 class List(Container):
1482 class List(Container):
1488 """An instance of a Python list."""
1483 """An instance of a Python list."""
1489 klass = list
1484 klass = list
1490 _cast_types = (tuple,)
1485 _cast_types = (tuple,)
1491
1486
1492 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize, **metadata):
1487 def __init__(self, trait=None, default_value=None, minlen=0, maxlen=sys.maxsize, **metadata):
1493 """Create a List trait type from a list, set, or tuple.
1488 """Create a List trait type from a list, set, or tuple.
1494
1489
1495 The default value is created by doing ``List(default_value)``,
1490 The default value is created by doing ``List(default_value)``,
1496 which creates a copy of the ``default_value``.
1491 which creates a copy of the ``default_value``.
1497
1492
1498 ``trait`` can be specified, which restricts the type of elements
1493 ``trait`` can be specified, which restricts the type of elements
1499 in the container to that TraitType.
1494 in the container to that TraitType.
1500
1495
1501 If only one arg is given and it is not a Trait, it is taken as
1496 If only one arg is given and it is not a Trait, it is taken as
1502 ``default_value``:
1497 ``default_value``:
1503
1498
1504 ``c = List([1,2,3])``
1499 ``c = List([1,2,3])``
1505
1500
1506 Parameters
1501 Parameters
1507 ----------
1502 ----------
1508
1503
1509 trait : TraitType [ optional ]
1504 trait : TraitType [ optional ]
1510 the type for restricting the contents of the Container. If unspecified,
1505 the type for restricting the contents of the Container. If unspecified,
1511 types are not checked.
1506 types are not checked.
1512
1507
1513 default_value : SequenceType [ optional ]
1508 default_value : SequenceType [ optional ]
1514 The default value for the Trait. Must be list/tuple/set, and
1509 The default value for the Trait. Must be list/tuple/set, and
1515 will be cast to the container type.
1510 will be cast to the container type.
1516
1511
1517 minlen : Int [ default 0 ]
1512 minlen : Int [ default 0 ]
1518 The minimum length of the input list
1513 The minimum length of the input list
1519
1514
1520 maxlen : Int [ default sys.maxsize ]
1515 maxlen : Int [ default sys.maxsize ]
1521 The maximum length of the input list
1516 The maximum length of the input list
1522
1517
1523 allow_none : bool [ default False ]
1518 allow_none : bool [ default False ]
1524 Whether to allow the value to be None
1519 Whether to allow the value to be None
1525
1520
1526 **metadata : any
1521 **metadata : any
1527 further keys for extensions to the Trait (e.g. config)
1522 further keys for extensions to the Trait (e.g. config)
1528
1523
1529 """
1524 """
1530 self._minlen = minlen
1525 self._minlen = minlen
1531 self._maxlen = maxlen
1526 self._maxlen = maxlen
1532 super(List, self).__init__(trait=trait, default_value=default_value,
1527 super(List, self).__init__(trait=trait, default_value=default_value,
1533 **metadata)
1528 **metadata)
1534
1529
1535 def length_error(self, obj, value):
1530 def length_error(self, obj, value):
1536 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1531 e = "The '%s' trait of %s instance must be of length %i <= L <= %i, but a value of %s was specified." \
1537 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1532 % (self.name, class_of(obj), self._minlen, self._maxlen, value)
1538 raise TraitError(e)
1533 raise TraitError(e)
1539
1534
1540 def validate_elements(self, obj, value):
1535 def validate_elements(self, obj, value):
1541 length = len(value)
1536 length = len(value)
1542 if length < self._minlen or length > self._maxlen:
1537 if length < self._minlen or length > self._maxlen:
1543 self.length_error(obj, value)
1538 self.length_error(obj, value)
1544
1539
1545 return super(List, self).validate_elements(obj, value)
1540 return super(List, self).validate_elements(obj, value)
1546
1541
1547 def validate(self, obj, value):
1542 def validate(self, obj, value):
1548 value = super(List, self).validate(obj, value)
1543 value = super(List, self).validate(obj, value)
1549 value = self.validate_elements(obj, value)
1544 value = self.validate_elements(obj, value)
1550 return value
1545 return value
1551
1546
1552
1547
1553 class Set(List):
1548 class Set(List):
1554 """An instance of a Python set."""
1549 """An instance of a Python set."""
1555 klass = set
1550 klass = set
1556 _cast_types = (tuple, list)
1551 _cast_types = (tuple, list)
1557
1552
1558
1553
1559 class Tuple(Container):
1554 class Tuple(Container):
1560 """An instance of a Python tuple."""
1555 """An instance of a Python tuple."""
1561 klass = tuple
1556 klass = tuple
1562 _cast_types = (list,)
1557 _cast_types = (list,)
1563
1558
1564 def __init__(self, *traits, **metadata):
1559 def __init__(self, *traits, **metadata):
1565 """Tuple(*traits, default_value=None, **medatata)
1560 """Tuple(*traits, default_value=None, **medatata)
1566
1561
1567 Create a tuple from a list, set, or tuple.
1562 Create a tuple from a list, set, or tuple.
1568
1563
1569 Create a fixed-type tuple with Traits:
1564 Create a fixed-type tuple with Traits:
1570
1565
1571 ``t = Tuple(Int, Str, CStr)``
1566 ``t = Tuple(Int, Str, CStr)``
1572
1567
1573 would be length 3, with Int,Str,CStr for each element.
1568 would be length 3, with Int,Str,CStr for each element.
1574
1569
1575 If only one arg is given and it is not a Trait, it is taken as
1570 If only one arg is given and it is not a Trait, it is taken as
1576 default_value:
1571 default_value:
1577
1572
1578 ``t = Tuple((1,2,3))``
1573 ``t = Tuple((1,2,3))``
1579
1574
1580 Otherwise, ``default_value`` *must* be specified by keyword.
1575 Otherwise, ``default_value`` *must* be specified by keyword.
1581
1576
1582 Parameters
1577 Parameters
1583 ----------
1578 ----------
1584
1579
1585 *traits : TraitTypes [ optional ]
1580 *traits : TraitTypes [ optional ]
1586 the tsype for restricting the contents of the Tuple. If unspecified,
1581 the tsype for restricting the contents of the Tuple. If unspecified,
1587 types are not checked. If specified, then each positional argument
1582 types are not checked. If specified, then each positional argument
1588 corresponds to an element of the tuple. Tuples defined with traits
1583 corresponds to an element of the tuple. Tuples defined with traits
1589 are of fixed length.
1584 are of fixed length.
1590
1585
1591 default_value : SequenceType [ optional ]
1586 default_value : SequenceType [ optional ]
1592 The default value for the Tuple. Must be list/tuple/set, and
1587 The default value for the Tuple. Must be list/tuple/set, and
1593 will be cast to a tuple. If `traits` are specified, the
1588 will be cast to a tuple. If `traits` are specified, the
1594 `default_value` must conform to the shape and type they specify.
1589 `default_value` must conform to the shape and type they specify.
1595
1590
1596 allow_none : bool [ default False ]
1591 allow_none : bool [ default False ]
1597 Whether to allow the value to be None
1592 Whether to allow the value to be None
1598
1593
1599 **metadata : any
1594 **metadata : any
1600 further keys for extensions to the Trait (e.g. config)
1595 further keys for extensions to the Trait (e.g. config)
1601
1596
1602 """
1597 """
1603 default_value = metadata.pop('default_value', None)
1598 default_value = metadata.pop('default_value', None)
1604 allow_none = metadata.pop('allow_none', True)
1599 allow_none = metadata.pop('allow_none', True)
1605
1600
1606 # allow Tuple((values,)):
1601 # allow Tuple((values,)):
1607 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1602 if len(traits) == 1 and default_value is None and not is_trait(traits[0]):
1608 default_value = traits[0]
1603 default_value = traits[0]
1609 traits = ()
1604 traits = ()
1610
1605
1611 if default_value is None:
1606 if default_value is None:
1612 args = ()
1607 args = ()
1613 elif isinstance(default_value, self._valid_defaults):
1608 elif isinstance(default_value, self._valid_defaults):
1614 args = (default_value,)
1609 args = (default_value,)
1615 else:
1610 else:
1616 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1611 raise TypeError('default value of %s was %s' %(self.__class__.__name__, default_value))
1617
1612
1618 self._traits = []
1613 self._traits = []
1619 for trait in traits:
1614 for trait in traits:
1620 t = trait() if isinstance(trait, type) else trait
1615 t = trait() if isinstance(trait, type) else trait
1621 t.name = 'element'
1616 t.name = 'element'
1622 self._traits.append(t)
1617 self._traits.append(t)
1623
1618
1624 if self._traits and default_value is None:
1619 if self._traits and default_value is None:
1625 # don't allow default to be an empty container if length is specified
1620 # don't allow default to be an empty container if length is specified
1626 args = None
1621 args = None
1627 super(Container,self).__init__(klass=self.klass, args=args, **metadata)
1622 super(Container,self).__init__(klass=self.klass, args=args, **metadata)
1628
1623
1629 def validate_elements(self, obj, value):
1624 def validate_elements(self, obj, value):
1630 if not self._traits:
1625 if not self._traits:
1631 # nothing to validate
1626 # nothing to validate
1632 return value
1627 return value
1633 if len(value) != len(self._traits):
1628 if len(value) != len(self._traits):
1634 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1629 e = "The '%s' trait of %s instance requires %i elements, but a value of %s was specified." \
1635 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1630 % (self.name, class_of(obj), len(self._traits), repr_type(value))
1636 raise TraitError(e)
1631 raise TraitError(e)
1637
1632
1638 validated = []
1633 validated = []
1639 for t, v in zip(self._traits, value):
1634 for t, v in zip(self._traits, value):
1640 try:
1635 try:
1641 v = t._validate(obj, v)
1636 v = t._validate(obj, v)
1642 except TraitError:
1637 except TraitError:
1643 self.element_error(obj, v, t)
1638 self.element_error(obj, v, t)
1644 else:
1639 else:
1645 validated.append(v)
1640 validated.append(v)
1646 return tuple(validated)
1641 return tuple(validated)
1647
1642
1648 def instance_init(self):
1643 def instance_init(self):
1649 for trait in self._traits:
1644 for trait in self._traits:
1650 if isinstance(trait, TraitType):
1645 if isinstance(trait, TraitType):
1651 trait.this_class = self.this_class
1646 trait.this_class = self.this_class
1652 trait.instance_init()
1647 trait.instance_init()
1653 super(Container, self).instance_init()
1648 super(Container, self).instance_init()
1654
1649
1655
1650
1656 class Dict(Instance):
1651 class Dict(Instance):
1657 """An instance of a Python dict."""
1652 """An instance of a Python dict."""
1658 _trait = None
1653 _trait = None
1659
1654
1660 def __init__(self, trait=None, default_value=NoDefaultSpecified, allow_none=False, **metadata):
1655 def __init__(self, trait=None, default_value=NoDefaultSpecified, allow_none=False, **metadata):
1661 """Create a dict trait type from a dict.
1656 """Create a dict trait type from a dict.
1662
1657
1663 The default value is created by doing ``dict(default_value)``,
1658 The default value is created by doing ``dict(default_value)``,
1664 which creates a copy of the ``default_value``.
1659 which creates a copy of the ``default_value``.
1665
1660
1666 trait : TraitType [ optional ]
1661 trait : TraitType [ optional ]
1667 the type for restricting the contents of the Container. If unspecified,
1662 the type for restricting the contents of the Container. If unspecified,
1668 types are not checked.
1663 types are not checked.
1669
1664
1670 default_value : SequenceType [ optional ]
1665 default_value : SequenceType [ optional ]
1671 The default value for the Dict. Must be dict, tuple, or None, and
1666 The default value for the Dict. Must be dict, tuple, or None, and
1672 will be cast to a dict if not None. If `trait` is specified, the
1667 will be cast to a dict if not None. If `trait` is specified, the
1673 `default_value` must conform to the constraints it specifies.
1668 `default_value` must conform to the constraints it specifies.
1674
1669
1675 allow_none : bool [ default False ]
1670 allow_none : bool [ default False ]
1676 Whether to allow the value to be None
1671 Whether to allow the value to be None
1677
1672
1678 """
1673 """
1679 if default_value is NoDefaultSpecified and trait is not None:
1674 if default_value is NoDefaultSpecified and trait is not None:
1680 if not is_trait(trait):
1675 if not is_trait(trait):
1681 default_value = trait
1676 default_value = trait
1682 trait = None
1677 trait = None
1683 if default_value is NoDefaultSpecified:
1678 if default_value is NoDefaultSpecified:
1684 default_value = {}
1679 default_value = {}
1685 if default_value is None:
1680 if default_value is None:
1686 args = None
1681 args = None
1687 elif isinstance(default_value, dict):
1682 elif isinstance(default_value, dict):
1688 args = (default_value,)
1683 args = (default_value,)
1689 elif isinstance(default_value, SequenceTypes):
1684 elif isinstance(default_value, SequenceTypes):
1690 args = (default_value,)
1685 args = (default_value,)
1691 else:
1686 else:
1692 raise TypeError('default value of Dict was %s' % default_value)
1687 raise TypeError('default value of Dict was %s' % default_value)
1693
1688
1694 if is_trait(trait):
1689 if is_trait(trait):
1695 self._trait = trait() if isinstance(trait, type) else trait
1690 self._trait = trait() if isinstance(trait, type) else trait
1696 self._trait.name = 'element'
1691 self._trait.name = 'element'
1697 elif trait is not None:
1692 elif trait is not None:
1698 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1693 raise TypeError("`trait` must be a Trait or None, got %s"%repr_type(trait))
1699
1694
1700 super(Dict,self).__init__(klass=dict, args=args,
1695 super(Dict,self).__init__(klass=dict, args=args,
1701 allow_none=allow_none, **metadata)
1696 allow_none=allow_none, **metadata)
1702
1697
1703 def element_error(self, obj, element, validator):
1698 def element_error(self, obj, element, validator):
1704 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1699 e = "Element of the '%s' trait of %s instance must be %s, but a value of %s was specified." \
1705 % (self.name, class_of(obj), validator.info(), repr_type(element))
1700 % (self.name, class_of(obj), validator.info(), repr_type(element))
1706 raise TraitError(e)
1701 raise TraitError(e)
1707
1702
1708 def validate(self, obj, value):
1703 def validate(self, obj, value):
1709 value = super(Dict, self).validate(obj, value)
1704 value = super(Dict, self).validate(obj, value)
1710 if value is None:
1705 if value is None:
1711 return value
1706 return value
1712 value = self.validate_elements(obj, value)
1707 value = self.validate_elements(obj, value)
1713 return value
1708 return value
1714
1709
1715 def validate_elements(self, obj, value):
1710 def validate_elements(self, obj, value):
1716 if self._trait is None or isinstance(self._trait, Any):
1711 if self._trait is None or isinstance(self._trait, Any):
1717 return value
1712 return value
1718 validated = {}
1713 validated = {}
1719 for key in value:
1714 for key in value:
1720 v = value[key]
1715 v = value[key]
1721 try:
1716 try:
1722 v = self._trait._validate(obj, v)
1717 v = self._trait._validate(obj, v)
1723 except TraitError:
1718 except TraitError:
1724 self.element_error(obj, v, self._trait)
1719 self.element_error(obj, v, self._trait)
1725 else:
1720 else:
1726 validated[key] = v
1721 validated[key] = v
1727 return self.klass(validated)
1722 return self.klass(validated)
1728
1723
1729 def instance_init(self):
1724 def instance_init(self):
1730 if isinstance(self._trait, TraitType):
1725 if isinstance(self._trait, TraitType):
1731 self._trait.this_class = self.this_class
1726 self._trait.this_class = self.this_class
1732 self._trait.instance_init()
1727 self._trait.instance_init()
1733 super(Dict, self).instance_init()
1728 super(Dict, self).instance_init()
1734
1729
1735
1730
1736 class EventfulDict(Instance):
1731 class EventfulDict(Instance):
1737 """An instance of an EventfulDict."""
1732 """An instance of an EventfulDict."""
1738
1733
1739 def __init__(self, default_value={}, allow_none=False, **metadata):
1734 def __init__(self, default_value={}, allow_none=False, **metadata):
1740 """Create a EventfulDict trait type from a dict.
1735 """Create a EventfulDict trait type from a dict.
1741
1736
1742 The default value is created by doing
1737 The default value is created by doing
1743 ``eventful.EvenfulDict(default_value)``, which creates a copy of the
1738 ``eventful.EvenfulDict(default_value)``, which creates a copy of the
1744 ``default_value``.
1739 ``default_value``.
1745 """
1740 """
1746 if default_value is None:
1741 if default_value is None:
1747 args = None
1742 args = None
1748 elif isinstance(default_value, dict):
1743 elif isinstance(default_value, dict):
1749 args = (default_value,)
1744 args = (default_value,)
1750 elif isinstance(default_value, SequenceTypes):
1745 elif isinstance(default_value, SequenceTypes):
1751 args = (default_value,)
1746 args = (default_value,)
1752 else:
1747 else:
1753 raise TypeError('default value of EventfulDict was %s' % default_value)
1748 raise TypeError('default value of EventfulDict was %s' % default_value)
1754
1749
1755 super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
1750 super(EventfulDict, self).__init__(klass=eventful.EventfulDict, args=args,
1756 allow_none=allow_none, **metadata)
1751 allow_none=allow_none, **metadata)
1757
1752
1758
1753
1759 class EventfulList(Instance):
1754 class EventfulList(Instance):
1760 """An instance of an EventfulList."""
1755 """An instance of an EventfulList."""
1761
1756
1762 def __init__(self, default_value=None, allow_none=False, **metadata):
1757 def __init__(self, default_value=None, allow_none=False, **metadata):
1763 """Create a EventfulList trait type from a dict.
1758 """Create a EventfulList trait type from a dict.
1764
1759
1765 The default value is created by doing
1760 The default value is created by doing
1766 ``eventful.EvenfulList(default_value)``, which creates a copy of the
1761 ``eventful.EvenfulList(default_value)``, which creates a copy of the
1767 ``default_value``.
1762 ``default_value``.
1768 """
1763 """
1769 if default_value is None:
1764 if default_value is None:
1770 args = ((),)
1765 args = ((),)
1771 else:
1766 else:
1772 args = (default_value,)
1767 args = (default_value,)
1773
1768
1774 super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
1769 super(EventfulList, self).__init__(klass=eventful.EventfulList, args=args,
1775 allow_none=allow_none, **metadata)
1770 allow_none=allow_none, **metadata)
1776
1771
1777
1772
1778 class TCPAddress(TraitType):
1773 class TCPAddress(TraitType):
1779 """A trait for an (ip, port) tuple.
1774 """A trait for an (ip, port) tuple.
1780
1775
1781 This allows for both IPv4 IP addresses as well as hostnames.
1776 This allows for both IPv4 IP addresses as well as hostnames.
1782 """
1777 """
1783
1778
1784 default_value = ('127.0.0.1', 0)
1779 default_value = ('127.0.0.1', 0)
1785 info_text = 'an (ip, port) tuple'
1780 info_text = 'an (ip, port) tuple'
1786
1781
1787 def validate(self, obj, value):
1782 def validate(self, obj, value):
1788 if isinstance(value, tuple):
1783 if isinstance(value, tuple):
1789 if len(value) == 2:
1784 if len(value) == 2:
1790 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1785 if isinstance(value[0], py3compat.string_types) and isinstance(value[1], int):
1791 port = value[1]
1786 port = value[1]
1792 if port >= 0 and port <= 65535:
1787 if port >= 0 and port <= 65535:
1793 return value
1788 return value
1794 self.error(obj, value)
1789 self.error(obj, value)
1795
1790
1796 class CRegExp(TraitType):
1791 class CRegExp(TraitType):
1797 """A casting compiled regular expression trait.
1792 """A casting compiled regular expression trait.
1798
1793
1799 Accepts both strings and compiled regular expressions. The resulting
1794 Accepts both strings and compiled regular expressions. The resulting
1800 attribute will be a compiled regular expression."""
1795 attribute will be a compiled regular expression."""
1801
1796
1802 info_text = 'a regular expression'
1797 info_text = 'a regular expression'
1803
1798
1804 def validate(self, obj, value):
1799 def validate(self, obj, value):
1805 try:
1800 try:
1806 return re.compile(value)
1801 return re.compile(value)
1807 except:
1802 except:
1808 self.error(obj, value)
1803 self.error(obj, value)
General Comments 0
You need to be logged in to leave comments. Login now