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