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