Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set default value from signature defaults in interact #5136

Merged
merged 5 commits into from
Feb 22, 2014
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 26 additions & 29 deletions IPython/html/widgets/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
ContainerWidget, DOMWidget)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type
from IPython.utils.traitlets import HasTraits, Any, Unicode
from IPython.utils.traitlets import HasTraits, TraitError, Any, Unicode

empty = Parameter.empty

#-----------------------------------------------------------------------------
# Classes and Functions
Expand Down Expand Up @@ -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)):
# if it's not a single-value abbreviation,
# set the initial value from the default
try:
widget.value = default
except TraitError:
# warn?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes... I think it would be good to have an advice here...

pass
if widget is None:
raise ValueError("%r cannot be transformed to a Widget" % (abbrev,))
return widget
Expand All @@ -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:
if name in kwargs:
yield name, kwargs.pop(name)
elif ann is not empty:
if default is empty:
yield name, ann
else:
yield name, ann
elif default is not empty:
yield name, default
else:
yield not_found
elif kind == Parameter.KEYWORD_ONLY:
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:
yield name, ann
value = 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
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the sorting? Is the idea to follow the order of arguments defined in the function itself?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not removing the sorting, it's removing the line altogether. This line did nothing because kwargs is always empty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, great.

Expand Down
28 changes: 26 additions & 2 deletions IPython/html/widgets/tests/test_interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
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
Expand Down Expand Up @@ -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)
Expand All @@ -261,6 +260,31 @@ def f(n, f=4.5):
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)
def f(n, f=4.5, g=1):
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,
),
)

def test_annotations():
Expand Down