mirror of
https://github.com/jupyter/notebook.git
synced 2024-12-21 04:10:17 +08:00
Merge pull request #5136 from minrk/interact-default
set default value from signature defaults in interact
This commit is contained in:
commit
708c30c912
@ -28,6 +28,8 @@ from IPython.display import display, clear_output
|
||||
from IPython.utils.py3compat import string_types, unicode_type
|
||||
from IPython.utils.traitlets import HasTraits, Any, Unicode
|
||||
|
||||
empty = Parameter.empty
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Classes and Functions
|
||||
#-----------------------------------------------------------------------------
|
||||
@ -108,12 +110,20 @@ def _widget_abbrev(o):
|
||||
else:
|
||||
return _widget_abbrev_single_value(o)
|
||||
|
||||
def _widget_from_abbrev(abbrev):
|
||||
"""Build a Widget intstance given an abbreviation or Widget."""
|
||||
def _widget_from_abbrev(abbrev, default=empty):
|
||||
"""Build a Widget instance given an abbreviation or Widget."""
|
||||
if isinstance(abbrev, Widget) or isinstance(abbrev, fixed):
|
||||
return abbrev
|
||||
|
||||
widget = _widget_abbrev(abbrev)
|
||||
if default is not empty and isinstance(abbrev, (list, tuple, dict)):
|
||||
# if it's not a single-value abbreviation,
|
||||
# set the initial value from the default
|
||||
try:
|
||||
widget.value = default
|
||||
except Exception:
|
||||
# ignore failure to set default
|
||||
pass
|
||||
if widget is None:
|
||||
raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
|
||||
return widget
|
||||
@ -124,50 +134,38 @@ def _yield_abbreviations_for_parameter(param, kwargs):
|
||||
kind = param.kind
|
||||
ann = param.annotation
|
||||
default = param.default
|
||||
empty = Parameter.empty
|
||||
not_found = (None, None)
|
||||
if kind == Parameter.POSITIONAL_OR_KEYWORD:
|
||||
not_found = (name, empty, empty)
|
||||
if kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
|
||||
if name in kwargs:
|
||||
yield name, kwargs.pop(name)
|
||||
value = kwargs.pop(name)
|
||||
elif ann is not empty:
|
||||
if default is empty:
|
||||
yield name, ann
|
||||
else:
|
||||
yield name, ann
|
||||
value = ann
|
||||
elif default is not empty:
|
||||
yield name, default
|
||||
else:
|
||||
yield not_found
|
||||
elif kind == Parameter.KEYWORD_ONLY:
|
||||
if name in kwargs:
|
||||
yield name, kwargs.pop(name)
|
||||
elif ann is not empty:
|
||||
yield name, ann
|
||||
elif default is not empty:
|
||||
yield name, default
|
||||
value = default
|
||||
else:
|
||||
yield not_found
|
||||
yield (name, value, default)
|
||||
elif kind == Parameter.VAR_KEYWORD:
|
||||
# In this case name=kwargs and we yield the items in kwargs with their keys.
|
||||
for k, v in kwargs.copy().items():
|
||||
kwargs.pop(k)
|
||||
yield k, v
|
||||
yield k, v, empty
|
||||
|
||||
def _find_abbreviations(f, kwargs):
|
||||
"""Find the abbreviations for a function and kwargs passed to interact."""
|
||||
new_kwargs = []
|
||||
for param in signature(f).parameters.values():
|
||||
for name, value in _yield_abbreviations_for_parameter(param, kwargs):
|
||||
if value is None:
|
||||
for name, value, default in _yield_abbreviations_for_parameter(param, kwargs):
|
||||
if value is empty:
|
||||
raise ValueError('cannot find widget or abbreviation for argument: {!r}'.format(name))
|
||||
new_kwargs.append((name, value))
|
||||
new_kwargs.append((name, value, default))
|
||||
return new_kwargs
|
||||
|
||||
def _widgets_from_abbreviations(seq):
|
||||
"""Given a sequence of (name, abbrev) tuples, return a sequence of Widgets."""
|
||||
result = []
|
||||
for name, abbrev in seq:
|
||||
widget = _widget_from_abbrev(abbrev)
|
||||
for name, abbrev, default in seq:
|
||||
widget = _widget_from_abbrev(abbrev, default)
|
||||
widget.description = name
|
||||
result.append(widget)
|
||||
return result
|
||||
@ -187,10 +185,9 @@ def interactive(__interact_f, **kwargs):
|
||||
# Before we proceed, let's make sure that the user has passed a set of args+kwargs
|
||||
# that will lead to a valid call of the function. This protects against unspecified
|
||||
# and doubly-specified arguments.
|
||||
getcallargs(f, **{n:v for n,v in new_kwargs})
|
||||
getcallargs(f, **{n:v for n,v,_ in new_kwargs})
|
||||
# Now build the widgets from the abbreviations.
|
||||
kwargs_widgets.extend(_widgets_from_abbreviations(new_kwargs))
|
||||
kwargs_widgets.extend(_widgets_from_abbreviations(sorted(kwargs.items(), key = lambda x: x[0])))
|
||||
|
||||
# This has to be done as an assignment, not using container.children.append,
|
||||
# so that traitlets notices the update. We skip any objects (such as fixed) that
|
||||
|
@ -22,7 +22,6 @@ import IPython.testing.tools as tt
|
||||
from IPython.html import widgets
|
||||
from IPython.html.widgets import interact, interactive, Widget, interaction
|
||||
from IPython.utils.py3compat import annotate
|
||||
# from IPython.utils.capture import capture_output
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Utility stuff
|
||||
@ -248,7 +247,7 @@ def test_list_tuple_invalid():
|
||||
|
||||
def test_defaults():
|
||||
@annotate(n=10)
|
||||
def f(n, f=4.5):
|
||||
def f(n, f=4.5, g=1):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
@ -261,6 +260,64 @@ def test_defaults():
|
||||
cls=widgets.FloatSliderWidget,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSliderWidget,
|
||||
value=1,
|
||||
),
|
||||
)
|
||||
|
||||
def test_default_values():
|
||||
@annotate(n=10, f=(0, 10.), g=5, h={'a': 1, 'b': 2}, j=['hi', 'there'])
|
||||
def f(n, f=4.5, g=1, h=2, j='there'):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSliderWidget,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatSliderWidget,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSliderWidget,
|
||||
value=5,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.DropdownWidget,
|
||||
values={'a': 1, 'b': 2},
|
||||
value=2
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.DropdownWidget,
|
||||
values={'hi':'hi', 'there':'there'},
|
||||
value='there'
|
||||
),
|
||||
)
|
||||
|
||||
def test_default_out_of_bounds():
|
||||
@annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there'])
|
||||
def f(f='hi', h=5, j='other'):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
f=dict(
|
||||
cls=widgets.FloatSliderWidget,
|
||||
value=5.,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.DropdownWidget,
|
||||
values={'a': 1},
|
||||
value=1,
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.DropdownWidget,
|
||||
values={'hi':'hi', 'there':'there'},
|
||||
value='hi',
|
||||
),
|
||||
)
|
||||
|
||||
def test_annotations():
|
||||
|
@ -90,6 +90,8 @@ class _SelectionWidget(DOMWidget):
|
||||
# set the selected value name
|
||||
self.value_name = k
|
||||
return
|
||||
# undo the change, and raise KeyError
|
||||
self.value = old
|
||||
raise KeyError(new)
|
||||
finally:
|
||||
self.value_lock.release()
|
||||
|
Loading…
Reference in New Issue
Block a user