test_interaction.py
590 lines
| 16.6 KiB
| text/x-python
|
PythonLexer
Brian E. Granger
|
r15140 | """Test interact and interactive.""" | |
#----------------------------------------------------------------------------- | |||
# Copyright (C) 2014 The IPython Development Team | |||
# | |||
# Distributed under the terms of the BSD License. The full license is in | |||
# the file COPYING, distributed as part of this software. | |||
#----------------------------------------------------------------------------- | |||
#----------------------------------------------------------------------------- | |||
# Imports | |||
#----------------------------------------------------------------------------- | |||
MinRK
|
r15153 | from __future__ import print_function | |
from collections import OrderedDict | |||
Brian E. Granger
|
r15140 | import nose.tools as nt | |
MinRK
|
r15153 | import IPython.testing.tools as tt | |
# from IPython.core.getipython import get_ipython | |||
from IPython.html import widgets | |||
from IPython.html.widgets import interact, interactive, Widget, interaction | |||
from IPython.utils.py3compat import annotate | |||
#----------------------------------------------------------------------------- | |||
# Utility stuff | |||
#----------------------------------------------------------------------------- | |||
class DummyComm(object): | |||
comm_id = 'a-b-c-d' | |||
def send(self, *args, **kwargs): | |||
pass | |||
def close(self, *args, **kwargs): | |||
pass | |||
_widget_attrs = {} | |||
displayed = [] | |||
def setup(): | |||
_widget_attrs['comm'] = Widget.comm | |||
Widget.comm = DummyComm() | |||
_widget_attrs['_ipython_display_'] = Widget._ipython_display_ | |||
def raise_not_implemented(*args, **kwargs): | |||
raise NotImplementedError() | |||
Widget._ipython_display_ = raise_not_implemented | |||
Brian E. Granger
|
r15140 | ||
MinRK
|
r15153 | def teardown(): | |
for attr, value in _widget_attrs.items(): | |||
setattr(Widget, attr, value) | |||
def f(**kwargs): | |||
pass | |||
def clear_display(): | |||
global displayed | |||
displayed = [] | |||
def record_display(*args): | |||
displayed.extend(args) | |||
Brian E. Granger
|
r15140 | ||
#----------------------------------------------------------------------------- | |||
MinRK
|
r15153 | # Actual tests | |
Brian E. Granger
|
r15140 | #----------------------------------------------------------------------------- | |
MinRK
|
r15153 | def check_widget(w, **d): | |
"""Check a single widget against a dict""" | |||
for attr, expected in d.items(): | |||
if attr == 'cls': | |||
nt.assert_is(w.__class__, expected) | |||
else: | |||
value = getattr(w, attr) | |||
nt.assert_equal(value, expected, | |||
"%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected) | |||
) | |||
def check_widgets(container, **to_check): | |||
"""Check that widgets are created as expected""" | |||
# build a widget dictionary, so it matches | |||
widgets = {} | |||
for w in container.children: | |||
widgets[w.description] = w | |||
for key, d in to_check.items(): | |||
nt.assert_in(key, widgets) | |||
check_widget(widgets[key], **d) | |||
def test_single_value_string(): | |||
a = u'hello' | |||
c = interactive(f, a=a) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
description='a', | |||
value=a, | |||
) | |||
def test_single_value_bool(): | |||
for a in (True, False): | |||
c = interactive(f, a=a) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.CheckboxWidget, | |||
description='a', | |||
value=a, | |||
) | |||
def test_single_value_dict(): | |||
for d in [ | |||
dict(a=5), | |||
dict(a=5, b='b', c=dict), | |||
]: | |||
c = interactive(f, d=d) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.DropdownWidget, | |||
description='d', | |||
values=d, | |||
value=next(iter(d.values())), | |||
) | |||
def test_single_value_float(): | |||
for a in (2.25, 1.0, -3.5): | |||
c = interactive(f, a=a) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.FloatSliderWidget, | |||
description='a', | |||
value=a, | |||
min= -a if a > 0 else 3*a, | |||
max= 3*a if a > 0 else -a, | |||
step=0.1, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
def test_single_value_int(): | |||
for a in (1, 5, -3): | |||
c = interactive(f, a=a) | |||
nt.assert_equal(len(c.children), 1) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.IntSliderWidget, | |||
description='a', | |||
value=a, | |||
min= -a if a > 0 else 3*a, | |||
max= 3*a if a > 0 else -a, | |||
step=1, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
def test_list_tuple_2_int(): | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,1)) | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,-1)) | |||
for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]: | |||
c = interactive(f, tup=(min, max), lis=[min, max]) | |||
nt.assert_equal(len(c.children), 2) | |||
d = dict( | |||
cls=widgets.IntSliderWidget, | |||
min=min, | |||
max=max, | |||
step=1, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
check_widgets(c, tup=d, lis=d) | |||
def test_list_tuple_3_int(): | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,2,0)) | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,2,-1)) | |||
for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]: | |||
c = interactive(f, tup=(min, max, step), lis=[min, max, step]) | |||
nt.assert_equal(len(c.children), 2) | |||
d = dict( | |||
cls=widgets.IntSliderWidget, | |||
min=min, | |||
max=max, | |||
step=step, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
check_widgets(c, tup=d, lis=d) | |||
def test_list_tuple_2_float(): | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1.0,1.0)) | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(0.5,-0.5)) | |||
for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]: | |||
c = interactive(f, tup=(min, max), lis=[min, max]) | |||
nt.assert_equal(len(c.children), 2) | |||
d = dict( | |||
cls=widgets.FloatSliderWidget, | |||
min=min, | |||
max=max, | |||
step=.1, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
check_widgets(c, tup=d, lis=d) | |||
def test_list_tuple_3_float(): | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,2,0.0)) | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(-1,-2,1.)) | |||
with nt.assert_raises(ValueError): | |||
c = interactive(f, tup=(1,2.,-1.)) | |||
for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]: | |||
c = interactive(f, tup=(min, max, step), lis=[min, max, step]) | |||
nt.assert_equal(len(c.children), 2) | |||
d = dict( | |||
cls=widgets.FloatSliderWidget, | |||
min=min, | |||
max=max, | |||
step=step, | |||
MinRK
|
r15159 | readout=True, | |
MinRK
|
r15153 | ) | |
check_widgets(c, tup=d, lis=d) | |||
def test_list_tuple_str(): | |||
values = ['hello', 'there', 'guy'] | |||
first = values[0] | |||
dvalues = OrderedDict((v,v) for v in values) | |||
c = interactive(f, tup=tuple(values), lis=list(values)) | |||
nt.assert_equal(len(c.children), 2) | |||
d = dict( | |||
cls=widgets.DropdownWidget, | |||
value=first, | |||
values=dvalues | |||
) | |||
check_widgets(c, tup=d, lis=d) | |||
def test_list_tuple_invalid(): | |||
for bad in [ | |||
(), | |||
(5, 'hi'), | |||
('hi', 5), | |||
({},), | |||
(None,), | |||
]: | |||
with nt.assert_raises(ValueError): | |||
print(bad) # because there is no custom message in assert_raises | |||
c = interactive(f, tup=bad) | |||
def test_defaults(): | |||
@annotate(n=10) | |||
MinRK
|
r15348 | def f(n, f=4.5, g=1): | |
MinRK
|
r15153 | pass | |
c = interactive(f) | |||
check_widgets(c, | |||
n=dict( | |||
cls=widgets.IntSliderWidget, | |||
value=10, | |||
), | |||
f=dict( | |||
cls=widgets.FloatSliderWidget, | |||
value=4.5, | |||
), | |||
MinRK
|
r15348 | g=dict( | |
cls=widgets.IntSliderWidget, | |||
value=1, | |||
), | |||
) | |||
def test_default_values(): | |||
MinRK
|
r15399 | @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'): | |||
MinRK
|
r15348 | 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, | |||
), | |||
MinRK
|
r15399 | 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(): | |||
MinRK
|
r15401 | @annotate(f=(0, 10.), h={'a': 1}, j=['hi', 'there']) | |
MinRK
|
r15399 | 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, | |||
MinRK
|
r15401 | values={'a': 1}, | |
value=1, | |||
MinRK
|
r15399 | ), | |
j=dict( | |||
cls=widgets.DropdownWidget, | |||
values={'hi':'hi', 'there':'there'}, | |||
MinRK
|
r15401 | value='hi', | |
MinRK
|
r15399 | ), | |
MinRK
|
r15153 | ) | |
def test_annotations(): | |||
@annotate(n=10, f=widgets.FloatTextWidget()) | |||
def f(n, f): | |||
pass | |||
c = interactive(f) | |||
check_widgets(c, | |||
n=dict( | |||
cls=widgets.IntSliderWidget, | |||
value=10, | |||
), | |||
f=dict( | |||
cls=widgets.FloatTextWidget, | |||
), | |||
) | |||
def test_priority(): | |||
@annotate(annotate='annotate', kwarg='annotate') | |||
def f(kwarg='default', annotate='default', default='default'): | |||
pass | |||
c = interactive(f, kwarg='kwarg') | |||
check_widgets(c, | |||
kwarg=dict( | |||
cls=widgets.TextWidget, | |||
value='kwarg', | |||
), | |||
annotate=dict( | |||
cls=widgets.TextWidget, | |||
value='annotate', | |||
), | |||
) | |||
@nt.with_setup(clear_display) | |||
def test_decorator_kwarg(): | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
@interact(a=5) | |||
def foo(a): | |||
pass | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.IntSliderWidget, | |||
value=5, | |||
) | |||
@nt.with_setup(clear_display) | |||
def test_decorator_no_call(): | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
@interact | |||
def foo(a='default'): | |||
pass | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='default', | |||
) | |||
@nt.with_setup(clear_display) | |||
def test_call_interact(): | |||
def foo(a='default'): | |||
pass | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
ifoo = interact(foo) | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='default', | |||
) | |||
@nt.with_setup(clear_display) | |||
def test_call_interact_kwargs(): | |||
def foo(a='default'): | |||
pass | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
ifoo = interact(foo, a=10) | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.IntSliderWidget, | |||
value=10, | |||
) | |||
@nt.with_setup(clear_display) | |||
def test_call_decorated_on_trait_change(): | |||
"""test calling @interact decorated functions""" | |||
d = {} | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
@interact | |||
def foo(a='default'): | |||
d['a'] = a | |||
return a | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='default', | |||
) | |||
# test calling the function directly | |||
a = foo('hello') | |||
nt.assert_equal(a, 'hello') | |||
nt.assert_equal(d['a'], 'hello') | |||
# test that setting trait values calls the function | |||
w.value = 'called' | |||
nt.assert_equal(d['a'], 'called') | |||
@nt.with_setup(clear_display) | |||
def test_call_decorated_kwargs_on_trait_change(): | |||
"""test calling @interact(foo=bar) decorated functions""" | |||
d = {} | |||
with tt.monkeypatch(interaction, 'display', record_display): | |||
@interact(a='kwarg') | |||
def foo(a='default'): | |||
d['a'] = a | |||
return a | |||
nt.assert_equal(len(displayed), 1) | |||
w = displayed[0].children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='kwarg', | |||
) | |||
# test calling the function directly | |||
a = foo('hello') | |||
nt.assert_equal(a, 'hello') | |||
nt.assert_equal(d['a'], 'hello') | |||
# test that setting trait values calls the function | |||
w.value = 'called' | |||
nt.assert_equal(d['a'], 'called') | |||
def test_fixed(): | |||
c = interactive(f, a=widgets.fixed(5), b='text') | |||
nt.assert_equal(len(c.children), 1) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='text', | |||
description='b', | |||
) | |||
Jessica B. Hamrick
|
r16658 | def test_default_description(): | |
c = interactive(f, b='text') | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='text', | |||
description='b', | |||
) | |||
def test_custom_description(): | |||
c = interactive(f, b=widgets.TextWidget(value='text', description='foo')) | |||
w = c.children[0] | |||
check_widget(w, | |||
cls=widgets.TextWidget, | |||
value='text', | |||
description='foo', | |||
) | |||
Gordon Ball
|
r17128 | ||
def test_int_range_logic(): | |||
irsw = widgets.IntRangeSliderWidget | |||
w = irsw(value=(2, 4), min=0, max=6) | |||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |||
w.value = (4, 2) | |||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |||
Gordon Ball
|
r17683 | w.value = (-1, 7) | |
check_widget(w, cls=irsw, value=(0, 6), min=0, max=6) | |||
Gordon Ball
|
r17128 | w.min = 3 | |
Gordon Ball
|
r17683 | check_widget(w, cls=irsw, value=(3, 6), min=3, max=6) | |
Gordon Ball
|
r17128 | w.max = 3 | |
check_widget(w, cls=irsw, value=(3, 3), min=3, max=3) | |||
Gordon Ball
|
r17683 | ||
w.min = 0 | |||
w.max = 6 | |||
w.lower = 2 | |||
w.upper = 4 | |||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |||
w.value = (0, 1) #lower non-overlapping range | |||
check_widget(w, cls=irsw, value=(0, 1), min=0, max=6) | |||
w.value = (5, 6) #upper non-overlapping range | |||
check_widget(w, cls=irsw, value=(5, 6), min=0, max=6) | |||
w.value = (-1, 4) #semi out-of-range | |||
check_widget(w, cls=irsw, value=(0, 4), min=0, max=6) | |||
w.lower = 2 | |||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6) | |||
w.value = (-2, -1) #wholly out of range | |||
check_widget(w, cls=irsw, value=(0, 0), min=0, max=6) | |||
w.value = (7, 8) | |||
check_widget(w, cls=irsw, value=(6, 6), min=0, max=6) | |||
with nt.assert_raises(ValueError): | |||
w.min = 7 | |||
with nt.assert_raises(ValueError): | |||
w.max = -1 | |||
with nt.assert_raises(ValueError): | |||
w.lower = 5 | |||
with nt.assert_raises(ValueError): | |||
w.upper = 1 | |||
w = irsw(lower=5, min=0, max=6) | |||
check_widget(w, lower=5) | |||
w = irsw(upper=1, min=0, max=6) | |||
check_widget(w, upper=1) | |||
with nt.assert_raises(ValueError): | |||
irsw(value=(2, 4), lower=3) | |||
with nt.assert_raises(ValueError): | |||
irsw(value=(2, 4), upper=3) | |||
with nt.assert_raises(ValueError): | |||
irsw(value=(2, 4), lower=3, upper=3) | |||
with nt.assert_raises(ValueError): | |||
irsw(min=2, max=1) | |||
Gordon Ball
|
r17128 | ||
def test_float_range_logic(): | |||
frsw = widgets.FloatRangeSliderWidget | |||
w = frsw(value=(.2, .4), min=0., max=.6) | |||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |||
w.value = (.4, .2) | |||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |||
Gordon Ball
|
r17683 | w.value = (-.1, .7) | |
check_widget(w, cls=frsw, value=(0., .6), min=0., max=.6) | |||
Gordon Ball
|
r17128 | w.min = .3 | |
Gordon Ball
|
r17683 | check_widget(w, cls=frsw, value=(.3, .6), min=.3, max=.6) | |
Gordon Ball
|
r17128 | w.max = .3 | |
check_widget(w, cls=frsw, value=(.3, .3), min=.3, max=.3) | |||
Gordon Ball
|
r17683 | ||
w.min = 0. | |||
w.max = .6 | |||
w.lower = .2 | |||
w.upper = .4 | |||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |||
w.value = (0., .1) #lower non-overlapping range | |||
check_widget(w, cls=frsw, value=(0., .1), min=0., max=.6) | |||
w.value = (.5, .6) #upper non-overlapping range | |||
check_widget(w, cls=frsw, value=(.5, .6), min=0., max=.6) | |||
w.value = (-.1, .4) #semi out-of-range | |||
check_widget(w, cls=frsw, value=(0., .4), min=0., max=.6) | |||
w.lower = .2 | |||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6) | |||
w.value = (-.2, -.1) #wholly out of range | |||
check_widget(w, cls=frsw, value=(0., 0.), min=0., max=.6) | |||
w.value = (.7, .8) | |||
check_widget(w, cls=frsw, value=(.6, .6), min=.0, max=.6) | |||
with nt.assert_raises(ValueError): | |||
w.min = .7 | |||
with nt.assert_raises(ValueError): | |||
w.max = -.1 | |||
with nt.assert_raises(ValueError): | |||
w.lower = .5 | |||
with nt.assert_raises(ValueError): | |||
w.upper = .1 | |||
w = frsw(lower=.5, min=0., max=.6) | |||
check_widget(w, lower=.5) | |||
w = frsw(upper=.1, min=0., max=.6) | |||
check_widget(w, upper=.1) | |||
with nt.assert_raises(ValueError): | |||
frsw(value=(2, 4), lower=3) | |||
with nt.assert_raises(ValueError): | |||
frsw(value=(2, 4), upper=3) | |||
with nt.assert_raises(ValueError): | |||
frsw(value=(2, 4), lower=3, upper=3) | |||
with nt.assert_raises(ValueError): | |||
frsw(min=.2, max=.1) |