##// END OF EJS Templates
move @annotate to py3compat
MinRK -
Show More
@@ -1,12 +1,12 b''
1 1 from .widget import Widget, DOMWidget, CallbackDispatcher
2 2
3 3 from .widget_bool import CheckboxWidget, ToggleButtonWidget
4 4 from .widget_button import ButtonWidget
5 5 from .widget_container import ContainerWidget, PopupWidget
6 6 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
7 7 from .widget_image import ImageWidget
8 8 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
9 9 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
10 10 from .widget_selectioncontainer import TabWidget, AccordionWidget
11 11 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
12 from .interaction import interact, interactive, annotate, const
12 from .interaction import interact, interactive, const
@@ -1,262 +1,248 b''
1 1 """Interact with functions using widgets."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (c) 2013, the IPython Development Team.
5 5 #
6 6 # Distributed under the terms of the Modified BSD License.
7 7 #
8 8 # The full license is in the file COPYING.txt, distributed with this software.
9 9 #-----------------------------------------------------------------------------
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Imports
13 13 #-----------------------------------------------------------------------------
14 14
15 15 from __future__ import print_function
16 16
17 17 try: # Python >= 3.3
18 18 from inspect import signature, Parameter
19 19 except ImportError:
20 20 from IPython.utils.signatures import signature, Parameter
21 21 from inspect import getcallargs
22 22
23 23 from IPython.html.widgets import (Widget, TextWidget,
24 24 FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
25 25 ContainerWidget, DOMWidget)
26 26 from IPython.display import display, clear_output
27 27 from IPython.utils.py3compat import string_types, unicode_type
28 28 from IPython.utils.traitlets import HasTraits, Any, Unicode
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Classes and Functions
32 32 #-----------------------------------------------------------------------------
33 33
34 34
35 35 def _matches(o, pattern):
36 36 """Match a pattern of types in a sequence."""
37 37 if not len(o) == len(pattern):
38 38 return False
39 39 comps = zip(o,pattern)
40 40 return all(isinstance(obj,kind) for obj,kind in comps)
41 41
42 42
43 43 def _get_min_max_value(min, max, value):
44 44 """Return min, max, value given input values with possible None."""
45 45 if value is None:
46 46 if not max > min:
47 47 raise ValueError('max must be greater than min: (min={0}, max={1})'.format(min, max))
48 48 value = min + abs(min-max)/2
49 49 value = type(min)(value)
50 50 elif min is None and max is None:
51 51 if value == 0.0:
52 52 min, max, value = 0.0, 1.0, 0.5
53 53 elif value == 0:
54 54 min, max, value = 0, 1, 0
55 55 elif isinstance(value, float):
56 56 min, max = (-value, 3.0*value) if value > 0 else (3.0*value, -value)
57 57 elif isinstance(value, int):
58 58 min, max = (-value, 3*value) if value > 0 else (3*value, -value)
59 59 else:
60 60 raise TypeError('expected a number, got: %r' % value)
61 61 else:
62 62 raise ValueError('unable to infer range, value from: ({0}, {1}, {2})'.format(min, max, value))
63 63 return min, max, value
64 64
65 65 def _widget_abbrev_single_value(o):
66 66 """Make widgets from single values, which can be used written as parameter defaults."""
67 67 if isinstance(o, string_types):
68 68 return TextWidget(value=unicode_type(o))
69 69 elif isinstance(o, dict):
70 70 # get a single value in a Python 2+3 way:
71 71 value = next(iter(o.values()))
72 72 return DropdownWidget(value=value, values=o)
73 73 elif isinstance(o, bool):
74 74 return CheckboxWidget(value=o)
75 75 elif isinstance(o, float):
76 76 min, max, value = _get_min_max_value(None, None, o)
77 77 return FloatSliderWidget(value=o, min=min, max=max)
78 78 elif isinstance(o, int):
79 79 min, max, value = _get_min_max_value(None, None, o)
80 80 return IntSliderWidget(value=o, min=min, max=max)
81 81 else:
82 82 return None
83 83
84 84 def _widget_abbrev(o):
85 85 """Make widgets from abbreviations: single values, lists or tuples."""
86 86 if isinstance(o, (list, tuple)):
87 87 if _matches(o, (int, int)):
88 88 min, max, value = _get_min_max_value(o[0], o[1], None)
89 89 return IntSliderWidget(value=value, min=min, max=max)
90 90 elif _matches(o, (int, int, int)):
91 91 min, max, value = _get_min_max_value(o[0], o[1], None)
92 92 return IntSliderWidget(value=value, min=min, max=max, step=o[2])
93 93 elif _matches(o, (float, float)):
94 94 min, max, value = _get_min_max_value(o[0], o[1], None)
95 95 return FloatSliderWidget(value=value, min=min, max=max)
96 96 elif _matches(o, (float, float, float)):
97 97 min, max, value = _get_min_max_value(o[0], o[1], None)
98 98 return FloatSliderWidget(value=value, min=min, max=max, step=o[2])
99 99 elif _matches(o, (float, float, int)):
100 100 min, max, value = _get_min_max_value(o[0], o[1], None)
101 101 return FloatSliderWidget(value=value, min=min, max=max, step=float(o[2]))
102 102 elif all(isinstance(x, string_types) for x in o):
103 103 return DropdownWidget(value=unicode_type(o[0]),
104 104 values=[unicode_type(k) for k in o])
105 105 else:
106 106 return _widget_abbrev_single_value(o)
107 107
108 108 def _widget_from_abbrev(abbrev):
109 109 """Build a Widget intstance given an abbreviation or Widget."""
110 110 if isinstance(abbrev, Widget) or isinstance(abbrev, const):
111 111 return abbrev
112 112
113 113 widget = _widget_abbrev(abbrev)
114 114 if widget is None:
115 115 raise ValueError("%r cannot be transformed to a Widget" % abbrev)
116 116 return widget
117 117
118 118 def _yield_abbreviations_for_parameter(param, kwargs):
119 119 """Get an abbreviation for a function parameter."""
120 120 name = param.name
121 121 kind = param.kind
122 122 ann = param.annotation
123 123 default = param.default
124 124 empty = Parameter.empty
125 125 not_found = (None, None)
126 126 if kind == Parameter.POSITIONAL_OR_KEYWORD:
127 127 if name in kwargs:
128 128 yield name, kwargs.pop(name)
129 129 elif ann is not empty:
130 130 if default is empty:
131 131 yield name, ann
132 132 else:
133 133 yield name, ann
134 134 elif default is not empty:
135 135 yield name, default
136 136 else:
137 137 yield not_found
138 138 elif kind == Parameter.KEYWORD_ONLY:
139 139 if name in kwargs:
140 140 yield name, kwargs.pop(name)
141 141 elif ann is not empty:
142 142 yield name, ann
143 143 elif default is not empty:
144 144 yield name, default
145 145 else:
146 146 yield not_found
147 147 elif kind == Parameter.VAR_KEYWORD:
148 148 # In this case name=kwargs and we yield the items in kwargs with their keys.
149 149 for k, v in kwargs.copy().items():
150 150 kwargs.pop(k)
151 151 yield k, v
152 152
153 153 def _find_abbreviations(f, kwargs):
154 154 """Find the abbreviations for a function and kwargs passed to interact."""
155 155 new_kwargs = []
156 156 for param in signature(f).parameters.values():
157 157 for name, value in _yield_abbreviations_for_parameter(param, kwargs):
158 158 if value is None:
159 159 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
160 160 new_kwargs.append((name, value))
161 161 return new_kwargs
162 162
163 163 def _widgets_from_abbreviations(seq):
164 164 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
165 165 result = []
166 166 for name, abbrev in seq:
167 167 widget = _widget_from_abbrev(abbrev)
168 168 widget.description = name
169 169 result.append(widget)
170 170 return result
171 171
172 172 def interactive(__interact_f, **kwargs):
173 173 """Build a group of widgets to interact with a function."""
174 174 f = __interact_f
175 175 co = kwargs.pop('clear_output', True)
176 176 kwargs_widgets = []
177 177 container = ContainerWidget()
178 178 container.result = None
179 179 container.args = []
180 180 container.kwargs = dict()
181 181 kwargs = kwargs.copy()
182 182
183 183 new_kwargs = _find_abbreviations(f, kwargs)
184 184 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
185 185 # that will lead to a valid call of the function. This protects against unspecified
186 186 # and doubly-specified arguments.
187 187 getcallargs(f, **{n:v for n,v in new_kwargs})
188 188 # Now build the widgets from the abbreviations.
189 189 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
190 190 kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
191 191
192 192 # This has to be done as an assignment, not using container.children.append,
193 193 # so that traitlets notices the update. We skip any objects (such as const) that
194 194 # are not DOMWidgets.
195 195 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
196 196 container.children = c
197 197
198 198 # Build the callback
199 199 def call_f(name, old, new):
200 200 container.args = []
201 201 for widget in kwargs_widgets:
202 202 value = widget.value
203 203 container.kwargs[widget.description] = value
204 204 if co:
205 205 clear_output(wait=True)
206 206 container.result = f(*container.args, **container.kwargs)
207 207
208 208 # Wire up the widgets
209 209 for widget in kwargs_widgets:
210 210 widget.on_trait_change(call_f, 'value')
211 211
212 212 container.on_displayed(lambda _: call_f(None, None, None))
213 213
214 214 return container
215 215
216 216 def interact(__interact_f=None, **kwargs):
217 217 """interact(f, **kwargs)
218 218
219 219 Interact with a function using widgets."""
220 220 # positional arg support in: https://gist.github.com/8851331
221 221 if __interact_f is not None:
222 222 # This branch handles the cases:
223 223 # 1. interact(f, **kwargs)
224 224 # 2. @interact
225 225 # def f(*args, **kwargs):
226 226 # ...
227 227 f = __interact_f
228 228 w = interactive(f, **kwargs)
229 229 f.widget = w
230 230 display(w)
231 231 else:
232 232 # This branch handles the case:
233 233 # @interact(a=30, b=40)
234 234 # def f(*args, **kwargs):
235 235 # ...
236 236 def dec(f):
237 237 w = interactive(f, **kwargs)
238 238 f.widget = w
239 239 display(w)
240 240 return f
241 241 return dec
242 242
243 243 class const(HasTraits):
244 244 """A pseudo-widget whose value is constant and never client synced."""
245 245 value = Any(help="Any Python object")
246 246 description = Unicode('', help="Any Python object")
247 247 def __init__(self, value, **kwargs):
248 248 super(const, self).__init__(value=value, **kwargs)
249
250 def annotate(**kwargs):
251 """Python 3 compatible function annotation for Python 2."""
252 if not kwargs:
253 raise ValueError('annotations must be provided as keyword arguments')
254 def dec(f):
255 if hasattr(f, '__annotations__'):
256 for k, v in kwargs.items():
257 f.__annotations__[k] = v
258 else:
259 f.__annotations__ = kwargs
260 return f
261 return dec
262
@@ -1,249 +1,264 b''
1 1 # coding: utf-8
2 2 """Compatibility tricks for Python 3. Mainly to do with unicode."""
3 3 import functools
4 4 import os
5 5 import sys
6 6 import re
7 7 import types
8 8
9 9 from .encoding import DEFAULT_ENCODING
10 10
11 11 orig_open = open
12 12
13 13 def no_code(x, encoding=None):
14 14 return x
15 15
16 16 def decode(s, encoding=None):
17 17 encoding = encoding or DEFAULT_ENCODING
18 18 return s.decode(encoding, "replace")
19 19
20 20 def encode(u, encoding=None):
21 21 encoding = encoding or DEFAULT_ENCODING
22 22 return u.encode(encoding, "replace")
23 23
24 24
25 25 def cast_unicode(s, encoding=None):
26 26 if isinstance(s, bytes):
27 27 return decode(s, encoding)
28 28 return s
29 29
30 30 def cast_bytes(s, encoding=None):
31 31 if not isinstance(s, bytes):
32 32 return encode(s, encoding)
33 33 return s
34 34
35 35 def _modify_str_or_docstring(str_change_func):
36 36 @functools.wraps(str_change_func)
37 37 def wrapper(func_or_str):
38 38 if isinstance(func_or_str, string_types):
39 39 func = None
40 40 doc = func_or_str
41 41 else:
42 42 func = func_or_str
43 43 doc = func.__doc__
44 44
45 45 doc = str_change_func(doc)
46 46
47 47 if func:
48 48 func.__doc__ = doc
49 49 return func
50 50 return doc
51 51 return wrapper
52 52
53 53 def safe_unicode(e):
54 54 """unicode(e) with various fallbacks. Used for exceptions, which may not be
55 55 safe to call unicode() on.
56 56 """
57 57 try:
58 58 return unicode_type(e)
59 59 except UnicodeError:
60 60 pass
61 61
62 62 try:
63 63 return str_to_unicode(str(e))
64 64 except UnicodeError:
65 65 pass
66 66
67 67 try:
68 68 return str_to_unicode(repr(e))
69 69 except UnicodeError:
70 70 pass
71 71
72 72 return u'Unrecoverably corrupt evalue'
73 73
74 74 if sys.version_info[0] >= 3:
75 75 PY3 = True
76 76
77 77 # keep reference to builtin_mod because the kernel overrides that value
78 78 # to forward requests to a frontend.
79 79 def input(prompt=''):
80 80 return builtin_mod.input(prompt)
81 81
82 82 builtin_mod_name = "builtins"
83 83 import builtins as builtin_mod
84 84
85 85 str_to_unicode = no_code
86 86 unicode_to_str = no_code
87 87 str_to_bytes = encode
88 88 bytes_to_str = decode
89 89 cast_bytes_py2 = no_code
90 90 cast_unicode_py2 = no_code
91 91
92 92 string_types = (str,)
93 93 unicode_type = str
94 94
95 95 def isidentifier(s, dotted=False):
96 96 if dotted:
97 97 return all(isidentifier(a) for a in s.split("."))
98 98 return s.isidentifier()
99 99
100 100 open = orig_open
101 101 xrange = range
102 102 def iteritems(d): return iter(d.items())
103 103 def itervalues(d): return iter(d.values())
104 104 getcwd = os.getcwd
105 105
106 106 MethodType = types.MethodType
107 107
108 108 def execfile(fname, glob, loc=None):
109 109 loc = loc if (loc is not None) else glob
110 110 with open(fname, 'rb') as f:
111 111 exec(compile(f.read(), fname, 'exec'), glob, loc)
112 112
113 113 # Refactor print statements in doctests.
114 114 _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE)
115 115 def _print_statement_sub(match):
116 116 expr = match.groups('expr')
117 117 return "print(%s)" % expr
118 118
119 119 @_modify_str_or_docstring
120 120 def doctest_refactor_print(doc):
121 121 """Refactor 'print x' statements in a doctest to print(x) style. 2to3
122 122 unfortunately doesn't pick up on our doctests.
123 123
124 124 Can accept a string or a function, so it can be used as a decorator."""
125 125 return _print_statement_re.sub(_print_statement_sub, doc)
126 126
127 127 # Abstract u'abc' syntax:
128 128 @_modify_str_or_docstring
129 129 def u_format(s):
130 130 """"{u}'abc'" --> "'abc'" (Python 3)
131 131
132 132 Accepts a string or a function, so it can be used as a decorator."""
133 133 return s.format(u='')
134 134
135 135 else:
136 136 PY3 = False
137 137
138 138 # keep reference to builtin_mod because the kernel overrides that value
139 139 # to forward requests to a frontend.
140 140 def input(prompt=''):
141 141 return builtin_mod.raw_input(prompt)
142 142
143 143 builtin_mod_name = "__builtin__"
144 144 import __builtin__ as builtin_mod
145 145
146 146 str_to_unicode = decode
147 147 unicode_to_str = encode
148 148 str_to_bytes = no_code
149 149 bytes_to_str = no_code
150 150 cast_bytes_py2 = cast_bytes
151 151 cast_unicode_py2 = cast_unicode
152 152
153 153 string_types = (str, unicode)
154 154 unicode_type = unicode
155 155
156 156 import re
157 157 _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$")
158 158 def isidentifier(s, dotted=False):
159 159 if dotted:
160 160 return all(isidentifier(a) for a in s.split("."))
161 161 return bool(_name_re.match(s))
162 162
163 163 class open(object):
164 164 """Wrapper providing key part of Python 3 open() interface."""
165 165 def __init__(self, fname, mode="r", encoding="utf-8"):
166 166 self.f = orig_open(fname, mode)
167 167 self.enc = encoding
168 168
169 169 def write(self, s):
170 170 return self.f.write(s.encode(self.enc))
171 171
172 172 def read(self, size=-1):
173 173 return self.f.read(size).decode(self.enc)
174 174
175 175 def close(self):
176 176 return self.f.close()
177 177
178 178 def __enter__(self):
179 179 return self
180 180
181 181 def __exit__(self, etype, value, traceback):
182 182 self.f.close()
183 183
184 184 xrange = xrange
185 185 def iteritems(d): return d.iteritems()
186 186 def itervalues(d): return d.itervalues()
187 187 getcwd = os.getcwdu
188 188
189 189 def MethodType(func, instance):
190 190 return types.MethodType(func, instance, type(instance))
191 191
192 192 def doctest_refactor_print(func_or_str):
193 193 return func_or_str
194 194
195 195
196 196 # Abstract u'abc' syntax:
197 197 @_modify_str_or_docstring
198 198 def u_format(s):
199 199 """"{u}'abc'" --> "u'abc'" (Python 2)
200 200
201 201 Accepts a string or a function, so it can be used as a decorator."""
202 202 return s.format(u='u')
203 203
204 204 if sys.platform == 'win32':
205 205 def execfile(fname, glob=None, loc=None):
206 206 loc = loc if (loc is not None) else glob
207 207 # The rstrip() is necessary b/c trailing whitespace in files will
208 208 # cause an IndentationError in Python 2.6 (this was fixed in 2.7,
209 209 # but we still support 2.6). See issue 1027.
210 210 scripttext = builtin_mod.open(fname).read().rstrip() + '\n'
211 211 # compile converts unicode filename to str assuming
212 212 # ascii. Let's do the conversion before calling compile
213 213 if isinstance(fname, unicode):
214 214 filename = unicode_to_str(fname)
215 215 else:
216 216 filename = fname
217 217 exec(compile(scripttext, filename, 'exec'), glob, loc)
218 218 else:
219 219 def execfile(fname, *where):
220 220 if isinstance(fname, unicode):
221 221 filename = fname.encode(sys.getfilesystemencoding())
222 222 else:
223 223 filename = fname
224 224 builtin_mod.execfile(filename, *where)
225 225
226
227 def annotate(**kwargs):
228 """Python 3 compatible function annotation for Python 2."""
229 if not kwargs:
230 raise ValueError('annotations must be provided as keyword arguments')
231 def dec(f):
232 if hasattr(f, '__annotations__'):
233 for k, v in kwargs.items():
234 f.__annotations__[k] = v
235 else:
236 f.__annotations__ = kwargs
237 return f
238 return dec
239
240
226 241 # Parts below taken from six:
227 242 # Copyright (c) 2010-2013 Benjamin Peterson
228 243 #
229 244 # Permission is hereby granted, free of charge, to any person obtaining a copy
230 245 # of this software and associated documentation files (the "Software"), to deal
231 246 # in the Software without restriction, including without limitation the rights
232 247 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
233 248 # copies of the Software, and to permit persons to whom the Software is
234 249 # furnished to do so, subject to the following conditions:
235 250 #
236 251 # The above copyright notice and this permission notice shall be included in all
237 252 # copies or substantial portions of the Software.
238 253 #
239 254 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
240 255 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
241 256 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
242 257 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
243 258 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
244 259 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
245 260 # SOFTWARE.
246 261
247 262 def with_metaclass(meta, *bases):
248 263 """Create a base class with a metaclass."""
249 264 return meta("_NewBase", bases, {})
General Comments 0
You need to be logged in to leave comments. Login now