##// END OF EJS Templates
remove positional arg support from interact
MinRK -
Show More
@@ -115,68 +115,50 b' def _widget_from_abbrev(abbrev):'
115 raise ValueError("%r cannot be transformed to a Widget" % abbrev)
115 raise ValueError("%r cannot be transformed to a Widget" % abbrev)
116 return widget
116 return widget
117
117
118 def _yield_abbreviations_for_parameter(param, args, kwargs):
118 def _yield_abbreviations_for_parameter(param, kwargs):
119 """Get an abbreviation for a function parameter."""
119 """Get an abbreviation for a function parameter."""
120 # print(param, args, kwargs)
121 name = param.name
120 name = param.name
122 kind = param.kind
121 kind = param.kind
123 ann = param.annotation
122 ann = param.annotation
124 default = param.default
123 default = param.default
125 empty = Parameter.empty
124 empty = Parameter.empty
126 if kind == Parameter.POSITIONAL_ONLY:
125 not_found = (None, None)
127 if args:
126 if kind == Parameter.POSITIONAL_OR_KEYWORD:
128 yield name, args.pop(0), False
129 elif ann is not empty:
130 yield name, ann, False
131 else:
132 yield None, None, None
133 elif kind == Parameter.POSITIONAL_OR_KEYWORD:
134 if name in kwargs:
127 if name in kwargs:
135 yield name, kwargs.pop(name), True
128 yield name, kwargs.pop(name)
136 elif args:
137 yield name, args.pop(0), False
138 elif ann is not empty:
129 elif ann is not empty:
139 if default is empty:
130 if default is empty:
140 yield name, ann, False
131 yield name, ann
141 else:
132 else:
142 yield name, ann, True
133 yield name, ann
143 elif default is not empty:
134 elif default is not empty:
144 yield name, default, True
135 yield name, default
145 else:
136 else:
146 yield None, None, None
137 yield not_found
147 elif kind == Parameter.VAR_POSITIONAL:
148 # In this case name=args or something and we don't actually know the names.
149 for item in args[::]:
150 args.pop(0)
151 yield '', item, False
152 elif kind == Parameter.KEYWORD_ONLY:
138 elif kind == Parameter.KEYWORD_ONLY:
153 if name in kwargs:
139 if name in kwargs:
154 yield name, kwargs.pop(name), True
140 yield name, kwargs.pop(name)
155 elif ann is not empty:
141 elif ann is not empty:
156 yield name, ann, True
142 yield name, ann
157 elif default is not empty:
143 elif default is not empty:
158 yield name, default, True
144 yield name, default
159 else:
145 else:
160 yield None, None, None
146 yield not_found
161 elif kind == Parameter.VAR_KEYWORD:
147 elif kind == Parameter.VAR_KEYWORD:
162 # In this case name=kwargs and we yield the items in kwargs with their keys.
148 # In this case name=kwargs and we yield the items in kwargs with their keys.
163 for k, v in kwargs.copy().items():
149 for k, v in kwargs.copy().items():
164 kwargs.pop(k)
150 kwargs.pop(k)
165 yield k, v, True
151 yield k, v
166
152
167 def _find_abbreviations(f, args, kwargs):
153 def _find_abbreviations(f, kwargs):
168 """Find the abbreviations for a function and args/kwargs passed to interact."""
154 """Find the abbreviations for a function and kwargs passed to interact."""
169 new_args = []
170 new_kwargs = []
155 new_kwargs = []
171 for param in signature(f).parameters.values():
156 for param in signature(f).parameters.values():
172 for name, value, kw in _yield_abbreviations_for_parameter(param, args, kwargs):
157 for name, value in _yield_abbreviations_for_parameter(param, kwargs):
173 if value is None:
158 if value is None:
174 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
159 raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
175 if kw:
160 new_kwargs.append((name, value))
176 new_kwargs.append((name, value))
161 return new_kwargs
177 else:
178 new_args.append((name, value))
179 return new_args, new_kwargs
180
162
181 def _widgets_from_abbreviations(seq):
163 def _widgets_from_abbreviations(seq):
182 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
164 """Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
@@ -187,41 +169,35 b' def _widgets_from_abbreviations(seq):'
187 result.append(widget)
169 result.append(widget)
188 return result
170 return result
189
171
190 def interactive(f, *args, **kwargs):
172 def interactive(__interact_f, **kwargs):
191 """Build a group of widgets to interact with a function."""
173 """Build a group of widgets to interact with a function."""
174 f = __interact_f
192 co = kwargs.pop('clear_output', True)
175 co = kwargs.pop('clear_output', True)
193 args_widgets = []
194 kwargs_widgets = []
176 kwargs_widgets = []
195 container = ContainerWidget()
177 container = ContainerWidget()
196 container.result = None
178 container.result = None
197 container.args = []
179 container.args = []
198 container.kwargs = dict()
180 container.kwargs = dict()
199 # We need this to be a list as we iteratively pop elements off it
200 args = list(args)
201 kwargs = kwargs.copy()
181 kwargs = kwargs.copy()
202
182
203 new_args, new_kwargs = _find_abbreviations(f, args, kwargs)
183 new_kwargs = _find_abbreviations(f, kwargs)
204 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
184 # Before we proceed, let's make sure that the user has passed a set of args+kwargs
205 # that will lead to a valid call of the function. This protects against unspecified
185 # that will lead to a valid call of the function. This protects against unspecified
206 # and doubly-specified arguments.
186 # and doubly-specified arguments.
207 getcallargs(f, *[v for n,v in new_args], **{n:v for n,v in new_kwargs})
187 getcallargs(f, **{n:v for n,v in new_kwargs})
208 # Now build the widgets from the abbreviations.
188 # Now build the widgets from the abbreviations.
209 args_widgets.extend(_widgets_from_abbreviations(new_args))
210 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
189 kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
211 kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
190 kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
212
191
213 # This has to be done as an assignment, not using container.children.append,
192 # This has to be done as an assignment, not using container.children.append,
214 # so that traitlets notices the update. We skip any objects (such as const) that
193 # so that traitlets notices the update. We skip any objects (such as const) that
215 # are not DOMWidgets.
194 # are not DOMWidgets.
216 c = [w for w in args_widgets+kwargs_widgets if isinstance(w, DOMWidget)]
195 c = [w for w in kwargs_widgets if isinstance(w, DOMWidget)]
217 container.children = c
196 container.children = c
218
197
219 # Build the callback
198 # Build the callback
220 def call_f(name, old, new):
199 def call_f(name, old, new):
221 container.args = []
200 container.args = []
222 for widget in args_widgets:
223 value = widget.value
224 container.args.append(value)
225 for widget in kwargs_widgets:
201 for widget in kwargs_widgets:
226 value = widget.value
202 value = widget.value
227 container.kwargs[widget.description] = value
203 container.kwargs[widget.description] = value
@@ -230,8 +206,6 b' def interactive(f, *args, **kwargs):'
230 container.result = f(*container.args, **container.kwargs)
206 container.result = f(*container.args, **container.kwargs)
231
207
232 # Wire up the widgets
208 # Wire up the widgets
233 for widget in args_widgets:
234 widget.on_trait_change(call_f, 'value')
235 for widget in kwargs_widgets:
209 for widget in kwargs_widgets:
236 widget.on_trait_change(call_f, 'value')
210 widget.on_trait_change(call_f, 'value')
237
211
@@ -239,25 +213,28 b' def interactive(f, *args, **kwargs):'
239
213
240 return container
214 return container
241
215
242 def interact(*args, **kwargs):
216 def interact(__interact_f=None, **kwargs):
243 """Interact with a function using widgets."""
217 """interact(f, **kwargs)
244 if args and callable(args[0]):
218
219 Interact with a function using widgets."""
220 # positional arg support in: https://gist.github.com/8851331
221 if __interact_f is not None:
245 # This branch handles the cases:
222 # This branch handles the cases:
246 # 1. interact(f, *args, **kwargs)
223 # 1. interact(f, **kwargs)
247 # 2. @interact
224 # 2. @interact
248 # def f(*args, **kwargs):
225 # def f(*args, **kwargs):
249 # ...
226 # ...
250 f = args[0]
227 f = __interact_f
251 w = interactive(f, *args[1:], **kwargs)
228 w = interactive(f, **kwargs)
252 f.widget = w
229 f.widget = w
253 display(w)
230 display(w)
254 else:
231 else:
255 # This branch handles the case:
232 # This branch handles the case:
256 # @interact(10, 20, a=30, b=40)
233 # @interact(a=30, b=40)
257 # def f(*args, **kwargs):
234 # def f(*args, **kwargs):
258 # ...
235 # ...
259 def dec(f):
236 def dec(f):
260 w = interactive(f, *args, **kwargs)
237 w = interactive(f, **kwargs)
261 f.widget = w
238 f.widget = w
262 display(w)
239 display(w)
263 return f
240 return f
General Comments 0
You need to be logged in to leave comments. Login now