Uploaded Test files
This commit is contained in:
parent
f584ad9d97
commit
2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from ipywidgets.widgets.docutils import doc_subst
|
||||
|
||||
|
||||
class TestDocSubst(TestCase):
|
||||
|
||||
def test_substitution(self):
|
||||
snippets = {'key': '62'}
|
||||
|
||||
@doc_subst(snippets)
|
||||
def f():
|
||||
""" Docstring with value {key} """
|
||||
|
||||
assert f.__doc__ == " Docstring with value 62 "
|
||||
|
||||
def test_unused_keys(self):
|
||||
snippets = {'key': '62', 'other-key': 'unused'}
|
||||
|
||||
@doc_subst(snippets)
|
||||
def f():
|
||||
""" Docstring with value {key} """
|
||||
|
||||
assert f.__doc__ == " Docstring with value 62 "
|
|
@ -0,0 +1,732 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test interact and interactive."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
from mock import patch
|
||||
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
import pytest
|
||||
|
||||
import ipywidgets as widgets
|
||||
|
||||
from traitlets import TraitError
|
||||
from ipywidgets import (interact, interact_manual, interactive,
|
||||
interaction, Output)
|
||||
from ipython_genutils.py3compat import annotate
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Utility stuff
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
def f(**kwargs):
|
||||
pass
|
||||
|
||||
displayed = []
|
||||
@pytest.fixture()
|
||||
def clear_display():
|
||||
global displayed
|
||||
displayed = []
|
||||
|
||||
def record_display(*args):
|
||||
displayed.extend(args)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Actual tests
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def check_widget(w, **d):
|
||||
"""Check a single widget against a dict"""
|
||||
for attr, expected in d.items():
|
||||
if attr == 'cls':
|
||||
assert w.__class__ is expected
|
||||
else:
|
||||
value = getattr(w, attr)
|
||||
assert value == expected, "%s.%s = %r != %r" % (w.__class__.__name__, attr, value, expected)
|
||||
|
||||
# For numeric values, the types should match too
|
||||
if isinstance(value, (int, float)):
|
||||
tv = type(value)
|
||||
te = type(expected)
|
||||
assert tv is te, "type(%s.%s) = %r != %r" % (w.__class__.__name__, attr, tv, te)
|
||||
|
||||
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:
|
||||
if not isinstance(w, Output):
|
||||
widgets[w.description] = w
|
||||
|
||||
for key, d in to_check.items():
|
||||
assert key in 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.Text,
|
||||
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.Checkbox,
|
||||
description='a',
|
||||
value=a,
|
||||
)
|
||||
|
||||
def test_single_value_float():
|
||||
for a in (2.25, 1.0, -3.5, 0.0):
|
||||
if not a:
|
||||
expected_min = 0.0
|
||||
expected_max = 1.0
|
||||
elif a > 0:
|
||||
expected_min = -a
|
||||
expected_max = 3*a
|
||||
else:
|
||||
expected_min = 3*a
|
||||
expected_max = -a
|
||||
c = interactive(f, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.FloatSlider,
|
||||
description='a',
|
||||
value=a,
|
||||
min=expected_min,
|
||||
max=expected_max,
|
||||
step=0.1,
|
||||
readout=True,
|
||||
)
|
||||
|
||||
def test_single_value_int():
|
||||
for a in (1, 5, -3, 0):
|
||||
if not a:
|
||||
expected_min = 0
|
||||
expected_max = 1
|
||||
elif a > 0:
|
||||
expected_min = -a
|
||||
expected_max = 3*a
|
||||
else:
|
||||
expected_min = 3*a
|
||||
expected_max = -a
|
||||
c = interactive(f, a=a)
|
||||
assert len(c.children) == 2
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
description='a',
|
||||
value=a,
|
||||
min=expected_min,
|
||||
max=expected_max,
|
||||
step=1,
|
||||
readout=True,
|
||||
)
|
||||
|
||||
def test_list_str():
|
||||
values = ['hello', 'there', 'guy']
|
||||
first = values[0]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=tuple(values),
|
||||
_options_values=tuple(values),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_int():
|
||||
values = [3, 1, 2]
|
||||
first = values[0]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=tuple(str(v) for v in values),
|
||||
_options_values=tuple(values),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_tuple():
|
||||
values = [(3, 300), (1, 100), (2, 200)]
|
||||
first = values[0][1]
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_list_tuple_invalid():
|
||||
for bad in [
|
||||
(),
|
||||
]:
|
||||
with pytest.raises(ValueError):
|
||||
print(bad) # because there is no custom message in assert_raises
|
||||
c = interactive(f, tup=bad)
|
||||
|
||||
def test_dict():
|
||||
for d in [
|
||||
dict(a=5),
|
||||
dict(a=5, b='b', c=dict),
|
||||
]:
|
||||
c = interactive(f, d=d)
|
||||
w = c.children[0]
|
||||
check = dict(
|
||||
cls=widgets.Dropdown,
|
||||
description='d',
|
||||
value=next(iter(d.values())),
|
||||
options=d,
|
||||
_options_labels=tuple(d.keys()),
|
||||
_options_values=tuple(d.values()),
|
||||
)
|
||||
check_widget(w, **check)
|
||||
|
||||
|
||||
def test_ordereddict():
|
||||
from collections import OrderedDict
|
||||
items = [(3, 300), (1, 100), (2, 200)]
|
||||
first = items[0][1]
|
||||
values = OrderedDict(items)
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=values,
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_iterable():
|
||||
def yield_values():
|
||||
yield 3
|
||||
yield 1
|
||||
yield 2
|
||||
first = next(yield_values())
|
||||
c = interactive(f, lis=yield_values())
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=(3, 1, 2),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(3, 1, 2),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_iterable_tuple():
|
||||
values = [(3, 300), (1, 100), (2, 200)]
|
||||
first = values[0][1]
|
||||
c = interactive(f, lis=iter(values))
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(values),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
def test_mapping():
|
||||
from collections import Mapping, OrderedDict
|
||||
class TestMapping(Mapping):
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
def __getitem__(self):
|
||||
raise NotImplementedError
|
||||
def __len__(self):
|
||||
raise NotImplementedError
|
||||
def __iter__(self):
|
||||
raise NotImplementedError
|
||||
def items(self):
|
||||
return self.values
|
||||
|
||||
items = [(3, 300), (1, 100), (2, 200)]
|
||||
first = items[0][1]
|
||||
values = TestMapping(items)
|
||||
c = interactive(f, lis=values)
|
||||
assert len(c.children) == 2
|
||||
d = dict(
|
||||
cls=widgets.Dropdown,
|
||||
value=first,
|
||||
options=tuple(items),
|
||||
_options_labels=("3", "1", "2"),
|
||||
_options_values=(300, 100, 200),
|
||||
)
|
||||
check_widgets(c, lis=d)
|
||||
|
||||
|
||||
def test_defaults():
|
||||
@annotate(n=10)
|
||||
def f(n, f=4.5, g=1):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatSlider,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=1,
|
||||
),
|
||||
)
|
||||
|
||||
def test_default_values():
|
||||
@annotate(n=10, f=(0, 10.), g=5, h=OrderedDict([('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.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatSlider,
|
||||
value=4.5,
|
||||
),
|
||||
g=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=OrderedDict([('a',1), ('b',2)]),
|
||||
value=2
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=('hi', '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.FloatSlider,
|
||||
value=5.,
|
||||
),
|
||||
h=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options={'a': 1},
|
||||
value=1,
|
||||
),
|
||||
j=dict(
|
||||
cls=widgets.Dropdown,
|
||||
options=('hi', 'there'),
|
||||
value='hi',
|
||||
),
|
||||
)
|
||||
|
||||
def test_annotations():
|
||||
@annotate(n=10, f=widgets.FloatText())
|
||||
def f(n, f):
|
||||
pass
|
||||
|
||||
c = interactive(f)
|
||||
check_widgets(c,
|
||||
n=dict(
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
),
|
||||
f=dict(
|
||||
cls=widgets.FloatText,
|
||||
),
|
||||
)
|
||||
|
||||
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.Text,
|
||||
value='kwarg',
|
||||
),
|
||||
annotate=dict(
|
||||
cls=widgets.Text,
|
||||
value='annotate',
|
||||
),
|
||||
)
|
||||
|
||||
def test_decorator_kwarg(clear_display):
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact(a=5)
|
||||
def foo(a):
|
||||
pass
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
)
|
||||
|
||||
def test_interact_instancemethod(clear_display):
|
||||
class Foo(object):
|
||||
def show(self, x):
|
||||
print(x)
|
||||
|
||||
f = Foo()
|
||||
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
g = interact(f.show, x=(1,10))
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=5,
|
||||
)
|
||||
|
||||
def test_decorator_no_call(clear_display):
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact
|
||||
def foo(a='default'):
|
||||
pass
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
|
||||
def test_call_interact(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
|
||||
def test_call_interact_on_trait_changed_none_return(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert len(displayed) == 1
|
||||
|
||||
def test_call_interact_kwargs(clear_display):
|
||||
def foo(a='default'):
|
||||
pass
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
ifoo = interact(foo, a=10)
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.IntSlider,
|
||||
value=10,
|
||||
)
|
||||
|
||||
def test_call_decorated_on_trait_change(clear_display):
|
||||
"""test calling @interact decorated functions"""
|
||||
d = {}
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact
|
||||
def foo(a='default'):
|
||||
d['a'] = a
|
||||
return a
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='default',
|
||||
)
|
||||
# test calling the function directly
|
||||
a = foo('hello')
|
||||
assert a == 'hello'
|
||||
assert d['a'] == 'hello'
|
||||
|
||||
# test that setting trait values calls the function
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert d['a'] == 'called'
|
||||
assert len(displayed) == 2
|
||||
assert w.value == displayed[-1]
|
||||
|
||||
def test_call_decorated_kwargs_on_trait_change(clear_display):
|
||||
"""test calling @interact(foo=bar) decorated functions"""
|
||||
d = {}
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
@interact(a='kwarg')
|
||||
def foo(a='default'):
|
||||
d['a'] = a
|
||||
return a
|
||||
assert len(displayed) == 1
|
||||
w = displayed[0].children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='kwarg',
|
||||
)
|
||||
# test calling the function directly
|
||||
a = foo('hello')
|
||||
assert a == 'hello'
|
||||
assert d['a'] == 'hello'
|
||||
|
||||
# test that setting trait values calls the function
|
||||
with patch.object(interaction, 'display', record_display):
|
||||
w.value = 'called'
|
||||
assert d['a'] == 'called'
|
||||
assert len(displayed) == 2
|
||||
assert w.value == displayed[-1]
|
||||
|
||||
|
||||
|
||||
def test_fixed():
|
||||
c = interactive(f, a=widgets.fixed(5), b='text')
|
||||
assert len(c.children) == 2
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='b',
|
||||
)
|
||||
|
||||
def test_default_description():
|
||||
c = interactive(f, b='text')
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='b',
|
||||
)
|
||||
|
||||
def test_custom_description():
|
||||
d = {}
|
||||
def record_kwargs(**kwargs):
|
||||
d.clear()
|
||||
d.update(kwargs)
|
||||
|
||||
c = interactive(record_kwargs, b=widgets.Text(value='text', description='foo'))
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
value='text',
|
||||
description='foo',
|
||||
)
|
||||
w.value = 'different text'
|
||||
assert d == {'b': 'different text'}
|
||||
|
||||
def test_interact_manual_button():
|
||||
c = interact.options(manual=True).widget(f)
|
||||
w = c.children[0]
|
||||
check_widget(w, cls=widgets.Button)
|
||||
|
||||
def test_interact_manual_nocall():
|
||||
callcount = 0
|
||||
def calltest(testarg):
|
||||
callcount += 1
|
||||
c = interact.options(manual=True)(calltest, testarg=5).widget
|
||||
c.children[0].value = 10
|
||||
assert callcount == 0
|
||||
|
||||
def test_interact_call():
|
||||
w = interact.widget(f)
|
||||
w.update()
|
||||
|
||||
w = interact_manual.widget(f)
|
||||
w.update()
|
||||
|
||||
def test_interact_options():
|
||||
def f(x):
|
||||
return x
|
||||
w = interact.options(manual=False).options(manual=True)(f, x=21).widget
|
||||
assert w.manual == True
|
||||
|
||||
w = interact_manual.options(manual=False).options()(x=21).widget(f)
|
||||
assert w.manual == False
|
||||
|
||||
w = interact(x=21)().options(manual=True)(f).widget
|
||||
assert w.manual == True
|
||||
|
||||
def test_interact_options_bad():
|
||||
with pytest.raises(ValueError):
|
||||
interact.options(bad="foo")
|
||||
|
||||
def test_int_range_logic():
|
||||
irsw = widgets.IntRangeSlider
|
||||
w = irsw(value=(2, 4), min=0, max=6)
|
||||
check_widget(w, cls=irsw, value=(2, 4), min=0, max=6)
|
||||
w.upper = 3
|
||||
w.max = 3
|
||||
check_widget(w, cls=irsw, value=(2, 3), min=0, max=3)
|
||||
|
||||
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.lower = 2
|
||||
check_widget(w, cls=irsw, value=(2, 6), min=0, max=6)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
w.min = 7
|
||||
with pytest.raises(TraitError):
|
||||
w.max = -1
|
||||
|
||||
w = irsw(min=2, max=3, value=(2, 3))
|
||||
check_widget(w, min=2, max=3, value=(2, 3))
|
||||
w = irsw(min=100, max=200, value=(125, 175))
|
||||
check_widget(w, value=(125, 175))
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
irsw(min=2, max=1)
|
||||
|
||||
|
||||
def test_float_range_logic():
|
||||
frsw = widgets.FloatRangeSlider
|
||||
w = frsw(value=(.2, .4), min=0., max=.6)
|
||||
check_widget(w, cls=frsw, value=(.2, .4), min=0., max=.6)
|
||||
|
||||
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.lower = .2
|
||||
check_widget(w, cls=frsw, value=(.2, .6), min=0., max=.6)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
w.min = .7
|
||||
with pytest.raises(TraitError):
|
||||
w.max = -.1
|
||||
|
||||
w = frsw(min=2, max=3, value=(2.2, 2.5))
|
||||
check_widget(w, min=2., max=3.)
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
frsw(min=.2, max=.1)
|
||||
|
||||
|
||||
def test_multiple_selection():
|
||||
smw = widgets.SelectMultiple
|
||||
|
||||
# degenerate multiple select
|
||||
w = smw()
|
||||
check_widget(w, value=tuple())
|
||||
|
||||
# don't accept random other value when no options
|
||||
with pytest.raises(TraitError):
|
||||
w.value = (2,)
|
||||
check_widget(w, value=tuple())
|
||||
|
||||
# basic multiple select
|
||||
w = smw(options=[(1, 1)], value=[1])
|
||||
check_widget(w, cls=smw, value=(1,), options=((1, 1),))
|
||||
|
||||
# don't accept random other value
|
||||
with pytest.raises(TraitError):
|
||||
w.value = w.value + (2,)
|
||||
check_widget(w, value=(1,))
|
||||
|
||||
# change options, which resets value
|
||||
w.options = w.options + ((2, 2),)
|
||||
check_widget(w, options=((1, 1), (2,2)), value=())
|
||||
|
||||
# change value
|
||||
w.value = (1,2)
|
||||
check_widget(w, value=(1, 2))
|
||||
|
||||
# dict style
|
||||
w.options = {1: 1}
|
||||
check_widget(w, options={1:1})
|
||||
|
||||
# updating
|
||||
with pytest.raises(TraitError):
|
||||
w.value = (2,)
|
||||
check_widget(w, options={1:1})
|
||||
|
||||
|
||||
def test_interact_noinspect():
|
||||
a = u'hello'
|
||||
c = interactive(print, a=a)
|
||||
w = c.children[0]
|
||||
check_widget(w,
|
||||
cls=widgets.Text,
|
||||
description='a',
|
||||
value=a,
|
||||
)
|
||||
|
||||
|
||||
def test_get_interact_value():
|
||||
from ipywidgets.widgets import ValueWidget
|
||||
from traitlets import Unicode
|
||||
class TheAnswer(ValueWidget):
|
||||
_model_name = Unicode('TheAnswer')
|
||||
description = Unicode()
|
||||
def get_interact_value(self):
|
||||
return 42
|
||||
w = TheAnswer()
|
||||
c = interactive(lambda v: v, v=w)
|
||||
c.update()
|
||||
assert c.result == 42
|
||||
|
||||
def test_state_schema():
|
||||
from ipywidgets.widgets import IntSlider, Widget
|
||||
import json
|
||||
import jsonschema
|
||||
s = IntSlider()
|
||||
state = Widget.get_manager_state(drop_defaults=True)
|
||||
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../', 'state.schema.json')) as f:
|
||||
schema = json.load(f)
|
||||
jsonschema.validate(state, schema)
|
||||
|
39
venv/Lib/site-packages/ipywidgets/widgets/tests/test_link.py
Normal file
39
venv/Lib/site-packages/ipywidgets/widgets/tests/test_link.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import jslink, jsdlink, ToggleButton
|
||||
from .utils import setup, teardown
|
||||
|
||||
def test_jslink_args():
|
||||
with pytest.raises(TypeError):
|
||||
jslink()
|
||||
w1 = ToggleButton()
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'))
|
||||
|
||||
w2 = ToggleButton()
|
||||
jslink((w1, 'value'), (w2, 'value'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'), (w2, 'nosuchtrait'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jslink((w1, 'value'), (w2, 'traits'))
|
||||
|
||||
def test_jsdlink_args():
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink()
|
||||
w1 = ToggleButton()
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'))
|
||||
|
||||
w2 = ToggleButton()
|
||||
jsdlink((w1, 'value'), (w2, 'value'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'), (w2, 'nosuchtrait'))
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
jsdlink((w1, 'value'), (w2, 'traits'))
|
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets.widgets import Accordion, HTML
|
||||
|
||||
|
||||
class TestAccordion(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.children = [HTML('0'), HTML('1')]
|
||||
|
||||
def test_selected_index_none(self):
|
||||
accordion = Accordion(self.children, selected_index=None)
|
||||
state = accordion.get_state()
|
||||
assert state['selected_index'] is None
|
||||
|
||||
def test_selected_index(self):
|
||||
accordion = Accordion(self.children, selected_index=1)
|
||||
state = accordion.get_state()
|
||||
assert state['selected_index'] == 1
|
||||
|
||||
def test_selected_index_out_of_bounds(self):
|
||||
with self.assertRaises(TraitError):
|
||||
Accordion(self.children, selected_index=-1)
|
|
@ -0,0 +1,25 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from traitlets import Bool, Tuple, List
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
from ..widget import Widget
|
||||
|
||||
# A widget with simple traits
|
||||
class SimpleWidget(Widget):
|
||||
a = Bool().tag(sync=True)
|
||||
b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag(sync=True)
|
||||
c = List(Bool()).tag(sync=True)
|
||||
|
||||
def test_empty_send_state():
|
||||
w = SimpleWidget()
|
||||
w.send_state([])
|
||||
assert w.comm.messages == []
|
||||
|
||||
def test_empty_hold_sync():
|
||||
w = SimpleWidget()
|
||||
with w.hold_sync():
|
||||
pass
|
||||
assert w.comm.messages == []
|
|
@ -0,0 +1,253 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ipython_genutils.py3compat import PY3
|
||||
|
||||
import pytest
|
||||
try:
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
|
||||
from traitlets import Bool, Tuple, List, Instance, CFloat, CInt, Float, Int, TraitError, observe
|
||||
|
||||
from .utils import setup, teardown
|
||||
|
||||
from ..widget import Widget
|
||||
|
||||
#
|
||||
# First some widgets to test on:
|
||||
#
|
||||
|
||||
# A widget with simple traits (list + tuple to ensure both are handled)
|
||||
class SimpleWidget(Widget):
|
||||
a = Bool().tag(sync=True)
|
||||
b = Tuple(Bool(), Bool(), Bool(), default_value=(False, False, False)).tag(sync=True)
|
||||
c = List(Bool()).tag(sync=True)
|
||||
|
||||
|
||||
# A widget with various kinds of number traits
|
||||
class NumberWidget(Widget):
|
||||
f = Float().tag(sync=True)
|
||||
cf = CFloat().tag(sync=True)
|
||||
i = Int().tag(sync=True)
|
||||
ci = CInt().tag(sync=True)
|
||||
|
||||
|
||||
|
||||
# A widget where the data might be changed on reception:
|
||||
def transform_fromjson(data, widget):
|
||||
# Switch the two last elements when setting from json, if the first element is True
|
||||
# and always set first element to False
|
||||
if not data[0]:
|
||||
return data
|
||||
return [False] + data[1:-2] + [data[-1], data[-2]]
|
||||
|
||||
class TransformerWidget(Widget):
|
||||
d = List(Bool()).tag(sync=True, from_json=transform_fromjson)
|
||||
|
||||
|
||||
|
||||
# A widget that has a buffer:
|
||||
class DataInstance():
|
||||
def __init__(self, data=None):
|
||||
self.data = data
|
||||
|
||||
def mview_serializer(instance, widget):
|
||||
return { 'data': memoryview(instance.data) if instance.data else None }
|
||||
|
||||
def bytes_serializer(instance, widget):
|
||||
return { 'data': bytearray(memoryview(instance.data).tobytes()) if instance.data else None }
|
||||
|
||||
def deserializer(json_data, widget):
|
||||
return DataInstance( memoryview(json_data['data']).tobytes() if json_data else None )
|
||||
|
||||
class DataWidget(SimpleWidget):
|
||||
d = Instance(DataInstance).tag(sync=True, to_json=mview_serializer, from_json=deserializer)
|
||||
|
||||
# A widget that has a buffer that might be changed on reception:
|
||||
def truncate_deserializer(json_data, widget):
|
||||
return DataInstance( json_data['data'][:20].tobytes() if json_data else None )
|
||||
|
||||
class TruncateDataWidget(SimpleWidget):
|
||||
d = Instance(DataInstance).tag(sync=True, to_json=bytes_serializer, from_json=truncate_deserializer)
|
||||
|
||||
|
||||
#
|
||||
# Actual tests:
|
||||
#
|
||||
|
||||
def test_set_state_simple():
|
||||
w = SimpleWidget()
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
b=[True, False, True],
|
||||
c=[False, True, False],
|
||||
))
|
||||
|
||||
assert w.comm.messages == []
|
||||
|
||||
|
||||
def test_set_state_transformer():
|
||||
w = TransformerWidget()
|
||||
w.set_state(dict(
|
||||
d=[True, False, True]
|
||||
))
|
||||
# Since the deserialize step changes the state, this should send an update
|
||||
assert w.comm.messages == [((), dict(
|
||||
buffers=[],
|
||||
data=dict(
|
||||
buffer_paths=[],
|
||||
method='update',
|
||||
state=dict(d=[False, True, False])
|
||||
)))]
|
||||
|
||||
|
||||
def test_set_state_data():
|
||||
w = DataWidget()
|
||||
data = memoryview(b'x'*30)
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
d={'data': data},
|
||||
))
|
||||
assert w.comm.messages == []
|
||||
|
||||
|
||||
def test_set_state_data_truncate():
|
||||
w = TruncateDataWidget()
|
||||
data = memoryview(b'x'*30)
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
d={'data': data},
|
||||
))
|
||||
# Get message for checking
|
||||
assert len(w.comm.messages) == 1 # ensure we didn't get more than expected
|
||||
msg = w.comm.messages[0]
|
||||
# Assert that the data update (truncation) sends an update
|
||||
buffers = msg[1].pop('buffers')
|
||||
assert msg == ((), dict(
|
||||
data=dict(
|
||||
buffer_paths=[['d', 'data']],
|
||||
method='update',
|
||||
state=dict(d={})
|
||||
)))
|
||||
|
||||
# Sanity:
|
||||
assert len(buffers) == 1
|
||||
assert buffers[0] == data[:20].tobytes()
|
||||
|
||||
|
||||
def test_set_state_numbers_int():
|
||||
# JS does not differentiate between float/int.
|
||||
# Instead, it formats exact floats as ints in JSON (1.0 -> '1').
|
||||
|
||||
w = NumberWidget()
|
||||
# Set everything with ints
|
||||
w.set_state(dict(
|
||||
f = 1,
|
||||
cf = 2,
|
||||
i = 3,
|
||||
ci = 4,
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_numbers_float():
|
||||
w = NumberWidget()
|
||||
# Set floats to int-like floats
|
||||
w.set_state(dict(
|
||||
f = 1.0,
|
||||
cf = 2.0,
|
||||
ci = 4.0
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_float_to_float():
|
||||
w = NumberWidget()
|
||||
# Set floats to float
|
||||
w.set_state(dict(
|
||||
f = 1.2,
|
||||
cf = 2.6,
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_cint_to_float():
|
||||
w = NumberWidget()
|
||||
|
||||
# Set CInt to float
|
||||
w.set_state(dict(
|
||||
ci = 5.6
|
||||
))
|
||||
# Ensure an update message gets produced
|
||||
assert len(w.comm.messages) == 1
|
||||
msg = w.comm.messages[0]
|
||||
data = msg[1]['data']
|
||||
assert data['method'] == 'update'
|
||||
assert data['state'] == {'ci': 5}
|
||||
|
||||
|
||||
# This test is disabled, meaning ipywidgets REQUIRES
|
||||
# any JSON received to format int-like numbers as ints
|
||||
def _x_test_set_state_int_to_int_like():
|
||||
# Note: Setting i to an int-like float will produce an
|
||||
# error, so if JSON producer were to always create
|
||||
# float formatted numbers, this would fail!
|
||||
|
||||
w = NumberWidget()
|
||||
# Set floats to int-like floats
|
||||
w.set_state(dict(
|
||||
i = 3.0
|
||||
))
|
||||
# Ensure no update message gets produced
|
||||
assert len(w.comm.messages) == 0
|
||||
|
||||
|
||||
def test_set_state_int_to_float():
|
||||
w = NumberWidget()
|
||||
|
||||
# Set Int to float
|
||||
with pytest.raises(TraitError):
|
||||
w.set_state(dict(
|
||||
i = 3.5
|
||||
))
|
||||
|
||||
def test_property_lock():
|
||||
# when this widget's value is set to 42, it sets itself to 2, and then back to 42 again (and then stops)
|
||||
class AnnoyingWidget(Widget):
|
||||
value = Float().tag(sync=True)
|
||||
stop = Bool(False)
|
||||
|
||||
@observe('value')
|
||||
def _propagate_value(self, change):
|
||||
print('_propagate_value', change.new)
|
||||
if self.stop:
|
||||
return
|
||||
if change.new == 42:
|
||||
self.value = 2
|
||||
if change.new == 2:
|
||||
self.stop = True
|
||||
self.value = 42
|
||||
|
||||
widget = AnnoyingWidget(value=1)
|
||||
assert widget.value == 1
|
||||
|
||||
widget._send = mock.MagicMock()
|
||||
# this mimics a value coming from the front end
|
||||
widget.set_state({'value': 42})
|
||||
assert widget.value == 42
|
||||
|
||||
# we expect first the {'value': 2.0} state to be send, followed by the {'value': 42.0} state
|
||||
msg = {'method': 'update', 'state': {'value': 2.0}, 'buffer_paths': []}
|
||||
call2 = mock.call(msg, buffers=[])
|
||||
|
||||
msg = {'method': 'update', 'state': {'value': 42.0}, 'buffer_paths': []}
|
||||
call42 = mock.call(msg, buffers=[])
|
||||
|
||||
calls = [call2, call42]
|
||||
widget._send.assert_has_calls(calls)
|
245
venv/Lib/site-packages/ipywidgets/widgets/tests/test_traits.py
Normal file
245
venv/Lib/site-packages/ipywidgets/widgets/tests/test_traits.py
Normal file
|
@ -0,0 +1,245 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test trait types of the widget packages."""
|
||||
import array
|
||||
import datetime as dt
|
||||
|
||||
import pytest
|
||||
|
||||
from unittest import TestCase
|
||||
from traitlets import HasTraits, Int, TraitError
|
||||
from traitlets.tests.test_traitlets import TraitTestBase
|
||||
|
||||
from ipywidgets import Color, NumberFormat
|
||||
from ipywidgets.widgets.widget import _remove_buffers, _put_buffers
|
||||
from ipywidgets.widgets.trait_types import date_serialization, TypedTuple
|
||||
|
||||
|
||||
class NumberFormatTrait(HasTraits):
|
||||
value = NumberFormat(".3f")
|
||||
|
||||
|
||||
class TestNumberFormat(TraitTestBase):
|
||||
obj = NumberFormatTrait()
|
||||
|
||||
_good_values = [
|
||||
'.2f', '.0%', '($.2f', '+20', '.^20', '.2s', '#x', ',.2r',
|
||||
' .2f', '.2', ''
|
||||
]
|
||||
_bad_values = [52, False, 'broken', '..2f', '.2a']
|
||||
|
||||
|
||||
class ColorTrait(HasTraits):
|
||||
value = Color("black")
|
||||
|
||||
|
||||
class TestColor(TraitTestBase):
|
||||
obj = ColorTrait()
|
||||
|
||||
|
||||
_good_values = [
|
||||
"blue", # valid color name
|
||||
"#AA0", # single digit hex
|
||||
"#FFFFFF", # double digit hex
|
||||
"transparent", # special color name
|
||||
'#aaaa', # single digit hex with alpha
|
||||
'#ffffffff', # double digit hex with alpha
|
||||
'rgb(0, 0, 0)', # rgb
|
||||
'rgb( 20,70,50 )', # rgb with spaces
|
||||
'rgba(10,10,10, 0.5)', # rgba with float alpha
|
||||
'rgba(255, 255, 255, 255)', # out of bounds alpha (spec says clamp to 1)
|
||||
'hsl(0.0, .0, 0)', # hsl
|
||||
'hsl( 0.5,0.3,0 )', # hsl with spaces
|
||||
'hsla(10,10,10, 0.5)', # rgba with float alpha
|
||||
]
|
||||
_bad_values = [
|
||||
"vanilla", "blues", # Invald color names
|
||||
1.2, 0.0, # Should fail with float input
|
||||
0, 1, 2, # Should fail with int input
|
||||
'rgb(0.4, 512, -40)',
|
||||
'hsl(0.4, 512, -40)',
|
||||
'rgba(0, 0, 0)',
|
||||
'hsla(0, 0, 0)',
|
||||
None,
|
||||
]
|
||||
|
||||
|
||||
class ColorTraitWithNone(HasTraits):
|
||||
value = Color("black", allow_none=True)
|
||||
|
||||
|
||||
class TestColorWithNone(TraitTestBase):
|
||||
obj = ColorTraitWithNone()
|
||||
|
||||
_good_values = TestColor._good_values + [None]
|
||||
_bad_values = list(filter(lambda v: v is not None, TestColor._bad_values))
|
||||
|
||||
|
||||
class TestDateSerialization(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.to_json = date_serialization['to_json']
|
||||
self.dummy_manager = None
|
||||
|
||||
def test_serialize_none(self):
|
||||
self.assertIs(self.to_json(None, self.dummy_manager), None)
|
||||
|
||||
def test_serialize_date(self):
|
||||
date = dt.date(1900, 2, 18)
|
||||
expected = {
|
||||
'year': 1900,
|
||||
'month': 1,
|
||||
'date': 18
|
||||
}
|
||||
self.assertEqual(self.to_json(date, self.dummy_manager), expected)
|
||||
|
||||
|
||||
class TestDateDeserialization(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.from_json = date_serialization['from_json']
|
||||
self.dummy_manager = None
|
||||
|
||||
def test_deserialize_none(self):
|
||||
self.assertIs(self.from_json(None, self.dummy_manager), None)
|
||||
|
||||
def test_deserialize_date(self):
|
||||
serialized_date = {
|
||||
'year': 1900,
|
||||
'month': 1,
|
||||
'date': 18
|
||||
}
|
||||
expected = dt.date(1900, 2, 18)
|
||||
self.assertEqual(
|
||||
self.from_json(serialized_date, self.dummy_manager),
|
||||
expected
|
||||
)
|
||||
|
||||
|
||||
class TestBuffers(TestCase):
|
||||
def test_remove_and_put_buffers(self):
|
||||
mv1 = memoryview(b'test1')
|
||||
mv2 = memoryview(b'test2')
|
||||
state = {'plain': [0, 'text'], # should not get removed
|
||||
'x': {'ar': mv1}, # should result in an empty dict
|
||||
'y': {'shape': (10, 10), 'data': mv1},
|
||||
'z': (mv1, mv2), # tests tuple assigment
|
||||
'top': mv1, # test a top level removal
|
||||
'deep': {'a': 1, 'b':[0,{'deeper':mv2}]}} # deeply nested
|
||||
plain = state['plain']
|
||||
x = state['x']
|
||||
y = state['y']
|
||||
y_shape = y['shape']
|
||||
state_before = state
|
||||
state, buffer_paths, buffers = _remove_buffers(state)
|
||||
|
||||
# check if buffers are removed
|
||||
self.assertIn('plain', state)
|
||||
self.assertIn('shape', state['y'])
|
||||
self.assertNotIn('ar', state['x'])
|
||||
self.assertEqual(state['x'], {})
|
||||
self.assertNotIn('data', state['y'])
|
||||
self.assertNotIn(mv1, state['z'])
|
||||
self.assertNotIn(mv1, state['z'])
|
||||
self.assertNotIn('top', state)
|
||||
self.assertIn('deep', state)
|
||||
self.assertIn('b', state['deep'])
|
||||
self.assertNotIn('deeper', state['deep']['b'][1])
|
||||
|
||||
# check that items that didn't need change aren't touched
|
||||
self.assertIsNot(state, state_before)
|
||||
self.assertIs(state['plain'], plain)
|
||||
self.assertIsNot(state['x'], x)
|
||||
self.assertIsNot(state['y'], y)
|
||||
self.assertIs(state['y']['shape'], y_shape)
|
||||
|
||||
# check that the buffer paths really point to the right buffer
|
||||
for path, buffer in [(['x', 'ar'], mv1), (['y', 'data'], mv1), (['z', 0], mv1), (['z', 1], mv2),\
|
||||
(['top'], mv1), (['deep', 'b', 1, 'deeper'], mv2)]:
|
||||
self.assertIn(path, buffer_paths, "%r not in path" % path)
|
||||
index = buffer_paths.index(path)
|
||||
self.assertEqual(buffer, buffers[index])
|
||||
|
||||
# and check that we can put it back together again
|
||||
_put_buffers(state, buffer_paths, buffers)
|
||||
# we know that tuples get converted to list, so help the comparison by changing the tuple to a list
|
||||
state_before['z'] = list(state_before['z'])
|
||||
self.assertEqual(state_before, state)
|
||||
|
||||
|
||||
|
||||
def test_typed_tuple_uninitialized_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == ()
|
||||
|
||||
|
||||
def test_typed_tuple_init_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase(value=(1, 2, 3))
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_set_ints():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
obj.value = (1, 2, 3)
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(default_value=(1, 2, 3))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_mixed_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(default_value=(1, 2, 'foobar'))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 'foobar')
|
||||
|
||||
|
||||
def test_typed_tuple_bad_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int(), default_value=(1, 2, 'foobar'))
|
||||
|
||||
|
||||
with pytest.raises(TraitError):
|
||||
obj = TestCase()
|
||||
a = obj.value # a read might be needed to trigger default validation
|
||||
|
||||
|
||||
def test_typed_tuple_bad_set():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(trait=Int())
|
||||
|
||||
obj = TestCase()
|
||||
with pytest.raises(TraitError):
|
||||
obj.value = (1, 2, 'foobar')
|
||||
|
||||
|
||||
def test_typed_tuple_positional_trait():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple(Int())
|
||||
|
||||
obj = TestCase(value=(1, 2, 3))
|
||||
assert obj.value == (1, 2, 3)
|
||||
|
||||
|
||||
def test_typed_tuple_positional_default():
|
||||
class TestCase(HasTraits):
|
||||
value = TypedTuple((1, 2, 3))
|
||||
|
||||
obj = TestCase()
|
||||
assert obj.value == (1, 2, 3)
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test Widget."""
|
||||
|
||||
from IPython.core.interactiveshell import InteractiveShell
|
||||
from IPython.display import display
|
||||
from IPython.utils.capture import capture_output
|
||||
|
||||
from ..widget import Widget
|
||||
from ..widget_button import Button
|
||||
|
||||
|
||||
def test_no_widget_view():
|
||||
# ensure IPython shell is instantiated
|
||||
# otherwise display() just calls print
|
||||
shell = InteractiveShell.instance()
|
||||
|
||||
with capture_output() as cap:
|
||||
w = Widget()
|
||||
display(w)
|
||||
|
||||
assert len(cap.outputs) == 1, "expect 1 output"
|
||||
mime_bundle = cap.outputs[0].data
|
||||
assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
|
||||
assert 'application/vnd.jupyter.widget-view+json' not in mime_bundle, "widget has no view"
|
||||
assert cap.stdout == '', repr(cap.stdout)
|
||||
assert cap.stderr == '', repr(cap.stderr)
|
||||
|
||||
|
||||
def test_widget_view():
|
||||
# ensure IPython shell is instantiated
|
||||
# otherwise display() just calls print
|
||||
shell = InteractiveShell.instance()
|
||||
|
||||
with capture_output() as cap:
|
||||
w = Button()
|
||||
display(w)
|
||||
|
||||
assert len(cap.outputs) == 1, "expect 1 output"
|
||||
mime_bundle = cap.outputs[0].data
|
||||
assert mime_bundle['text/plain'] == repr(w), "expected plain text output"
|
||||
assert 'application/vnd.jupyter.widget-view+json' in mime_bundle, "widget should have have a view"
|
||||
assert cap.stdout == '', repr(cap.stdout)
|
||||
assert cap.stderr == '', repr(cap.stderr)
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
import ipywidgets as widgets
|
||||
|
||||
|
||||
class TestBox(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
box = widgets.Box()
|
||||
assert box.get_state()['children'] == []
|
||||
|
||||
def test_construction_with_children(self):
|
||||
html = widgets.HTML('some html')
|
||||
slider = widgets.IntSlider()
|
||||
box = widgets.Box([html, slider])
|
||||
children_state = box.get_state()['children']
|
||||
assert children_state == [
|
||||
widgets.widget._widget_to_json(html, None),
|
||||
widgets.widget._widget_to_json(slider, None),
|
||||
]
|
||||
|
||||
def test_construction_style(self):
|
||||
box = widgets.Box(box_style='warning')
|
||||
assert box.get_state()['box_style'] == 'warning'
|
||||
|
||||
def test_construction_invalid_style(self):
|
||||
with self.assertRaises(TraitError):
|
||||
widgets.Box(box_style='invalid')
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import FloatSlider
|
||||
|
||||
|
||||
class TestFloatSlider(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
FloatSlider()
|
||||
|
||||
def test_construction_readout_format(self):
|
||||
slider = FloatSlider(readout_format='$.1f')
|
||||
assert slider.get_state()['readout_format'] == '$.1f'
|
||||
|
||||
def test_construction_invalid_readout_format(self):
|
||||
with self.assertRaises(TraitError):
|
||||
FloatSlider(readout_format='broken')
|
|
@ -0,0 +1,172 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
"""Test Image widget"""
|
||||
|
||||
import io
|
||||
import os
|
||||
|
||||
from ipywidgets import Image
|
||||
|
||||
import hashlib
|
||||
|
||||
import pkgutil
|
||||
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
|
||||
# Data
|
||||
@contextmanager
|
||||
def get_logo_png():
|
||||
# Once the tests are not in the package, this context manager can be
|
||||
# replaced with the location of the actual file
|
||||
LOGO_DATA = pkgutil.get_data('ipywidgets.widgets.tests',
|
||||
'data/jupyter-logo-transparent.png')
|
||||
handle, fname = tempfile.mkstemp()
|
||||
os.close(handle)
|
||||
with open(fname, 'wb') as f:
|
||||
f.write(LOGO_DATA)
|
||||
|
||||
yield fname
|
||||
|
||||
os.remove(fname)
|
||||
|
||||
LOGO_PNG_DIGEST = '3ff9eafd7197083153e83339a72e7a335539bae189c33554c680e4382c98af02'
|
||||
|
||||
|
||||
def test_empty_image():
|
||||
# Empty images shouldn't raise any errors
|
||||
Image()
|
||||
|
||||
|
||||
def test_image_value():
|
||||
random_bytes = b'\x0ee\xca\x80\xcd\x9ak#\x7f\x07\x03\xa7'
|
||||
|
||||
Image(value=random_bytes)
|
||||
|
||||
|
||||
def test_image_format():
|
||||
# Test that these format names don't throw an error
|
||||
Image(format='png')
|
||||
|
||||
Image(format='jpeg')
|
||||
|
||||
Image(format='url')
|
||||
|
||||
|
||||
def test_from_filename():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
img = Image.from_file(LOGO_PNG)
|
||||
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_set_from_filename():
|
||||
img = Image()
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
img.set_value_from_file(LOGO_PNG)
|
||||
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_from_file():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img = Image.from_file(f)
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_set_value_from_file():
|
||||
img = Image()
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img.set_value_from_file(f)
|
||||
assert_equal_hash(img.value, LOGO_PNG_DIGEST)
|
||||
|
||||
|
||||
def test_from_url_unicode():
|
||||
img = Image.from_url(u'https://jupyter.org/assets/main-logo.svg')
|
||||
assert img.value == b'https://jupyter.org/assets/main-logo.svg'
|
||||
|
||||
|
||||
def test_from_url_bytes():
|
||||
img = Image.from_url(b'https://jupyter.org/assets/main-logo.svg')
|
||||
|
||||
assert img.value == b'https://jupyter.org/assets/main-logo.svg'
|
||||
|
||||
|
||||
def test_format_inference_filename():
|
||||
with tempfile.NamedTemporaryFile(suffix='.svg', delete=False) as f:
|
||||
name = f.name
|
||||
f.close() # Allow tests to run on Windows
|
||||
img = Image.from_file(name)
|
||||
|
||||
assert img.format == 'svg+xml'
|
||||
|
||||
|
||||
def test_format_inference_file():
|
||||
with tempfile.NamedTemporaryFile(suffix='.gif', delete=False) as f:
|
||||
img = Image.from_file(f)
|
||||
|
||||
assert img.format == 'gif'
|
||||
|
||||
|
||||
def test_format_inference_stream():
|
||||
# There's no way to infer the format, so it should default to png
|
||||
fstream = io.BytesIO(b'')
|
||||
img = Image.from_file(fstream)
|
||||
|
||||
assert img.format == 'png'
|
||||
|
||||
|
||||
def test_serialize():
|
||||
fstream = io.BytesIO(b'123')
|
||||
img = Image.from_file(fstream)
|
||||
|
||||
img_state = img.get_state()
|
||||
|
||||
# for python27 it is a memoryview
|
||||
assert isinstance(img_state['value'], (bytes, memoryview))
|
||||
# make sure it is (for python 3), since that is what it will be once it comes off the wire
|
||||
img_state['value'] = memoryview(img_state['value'])
|
||||
|
||||
# check that we can deserialize it and get back the original value
|
||||
img_copy = Image()
|
||||
img_copy.set_state(img_state)
|
||||
assert img.value == img_copy.value
|
||||
|
||||
|
||||
def test_format_inference_overridable():
|
||||
with tempfile.NamedTemporaryFile(suffix='.svg', delete=False) as f:
|
||||
name = f.name
|
||||
f.close() # Allow tests to run on Windows
|
||||
img = Image.from_file(name, format='gif')
|
||||
|
||||
assert img.format == 'gif'
|
||||
|
||||
|
||||
def test_value_repr_length():
|
||||
with get_logo_png() as LOGO_PNG:
|
||||
with open(LOGO_PNG, 'rb') as f:
|
||||
img = Image.from_file(f)
|
||||
assert len(img.__repr__()) < 120
|
||||
assert img.__repr__().endswith("...')")
|
||||
|
||||
|
||||
def test_value_repr_url():
|
||||
img = Image.from_url(b'https://jupyter.org/assets/main-logo.svg')
|
||||
|
||||
assert 'https://jupyter.org/assets/main-logo.svg' in img.__repr__()
|
||||
|
||||
|
||||
# Helper functions
|
||||
def get_hash_hex(byte_str):
|
||||
m = hashlib.new('sha256')
|
||||
|
||||
m.update(byte_str)
|
||||
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def assert_equal_hash(byte_str, digest):
|
||||
assert get_hash_hex(byte_str) == digest
|
|
@ -0,0 +1,223 @@
|
|||
import sys
|
||||
from unittest import TestCase
|
||||
from contextlib import contextmanager
|
||||
|
||||
from IPython.display import Markdown, Image
|
||||
from ipywidgets import widget_output
|
||||
|
||||
|
||||
class TestOutputWidget(TestCase):
|
||||
|
||||
@contextmanager
|
||||
def _mocked_ipython(self, get_ipython, clear_output):
|
||||
""" Context manager that monkeypatches get_ipython and clear_output """
|
||||
original_clear_output = widget_output.clear_output
|
||||
original_get_ipython = widget_output.get_ipython
|
||||
widget_output.get_ipython = get_ipython
|
||||
widget_output.clear_output = clear_output
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
widget_output.clear_output = original_clear_output
|
||||
widget_output.get_ipython = original_get_ipython
|
||||
|
||||
def _mock_get_ipython(self, msg_id):
|
||||
""" Returns a mock IPython application with a mocked kernel """
|
||||
kernel = type(
|
||||
'mock_kernel',
|
||||
(object, ),
|
||||
{'_parent_header': {'header': {'msg_id': msg_id}}}
|
||||
)
|
||||
|
||||
# Specifically override this so the traceback
|
||||
# is still printed to screen
|
||||
def showtraceback(self_, exc_tuple, *args, **kwargs):
|
||||
etype, evalue, tb = exc_tuple
|
||||
raise etype(evalue)
|
||||
|
||||
ipython = type(
|
||||
'mock_ipython',
|
||||
(object, ),
|
||||
{'kernel': kernel, 'showtraceback': showtraceback}
|
||||
)
|
||||
return ipython
|
||||
|
||||
def _mock_clear_output(self):
|
||||
""" Mock function that records calls to it """
|
||||
calls = []
|
||||
|
||||
def clear_output(*args, **kwargs):
|
||||
calls.append((args, kwargs))
|
||||
clear_output.calls = calls
|
||||
|
||||
return clear_output
|
||||
|
||||
def test_set_msg_id_when_capturing(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
assert widget.msg_id == ''
|
||||
with widget:
|
||||
assert widget.msg_id == msg_id
|
||||
assert widget.msg_id == ''
|
||||
|
||||
def test_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
widget.clear_output(wait=True)
|
||||
|
||||
assert len(clear_output.calls) == 1
|
||||
assert clear_output.calls[0] == ((), {'wait': True})
|
||||
|
||||
def test_capture_decorator(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
expected_argument = 'arg'
|
||||
expected_keyword_argument = True
|
||||
captee_calls = []
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
assert widget.msg_id == ''
|
||||
|
||||
@widget.capture()
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
# Check that arguments are passed correctly
|
||||
captee_calls.append((args, kwargs))
|
||||
|
||||
captee(
|
||||
expected_argument, keyword_argument=expected_keyword_argument)
|
||||
assert widget.msg_id == ''
|
||||
captee()
|
||||
|
||||
assert len(captee_calls) == 2
|
||||
assert captee_calls[0] == (
|
||||
(expected_argument, ),
|
||||
{'keyword_argument': expected_keyword_argument}
|
||||
)
|
||||
assert captee_calls[1] == ((), {})
|
||||
|
||||
def test_capture_decorator_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
|
||||
@widget.capture(clear_output=True, wait=True)
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
captee()
|
||||
captee()
|
||||
|
||||
assert len(clear_output.calls) == 2
|
||||
assert clear_output.calls[0] == clear_output.calls[1] == \
|
||||
((), {'wait': True})
|
||||
|
||||
def test_capture_decorator_no_clear_output(self):
|
||||
msg_id = 'msg-id'
|
||||
get_ipython = self._mock_get_ipython(msg_id)
|
||||
clear_output = self._mock_clear_output()
|
||||
|
||||
with self._mocked_ipython(get_ipython, clear_output):
|
||||
widget = widget_output.Output()
|
||||
|
||||
@widget.capture(clear_output=False)
|
||||
def captee(*args, **kwargs):
|
||||
# Check that we are capturing output
|
||||
assert widget.msg_id == msg_id
|
||||
|
||||
captee()
|
||||
captee()
|
||||
|
||||
assert len(clear_output.calls) == 0
|
||||
|
||||
|
||||
def _make_stream_output(text, name):
|
||||
return {
|
||||
'output_type': 'stream',
|
||||
'name': name,
|
||||
'text': text
|
||||
}
|
||||
|
||||
|
||||
def test_append_stdout():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a message to stdout.
|
||||
widget.append_stdout("snakes!")
|
||||
expected = (_make_stream_output("snakes!", "stdout"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Try appending a second message.
|
||||
widget.append_stdout("more snakes!")
|
||||
expected += (_make_stream_output("more snakes!", "stdout"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
|
||||
def test_append_stderr():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a message to stderr.
|
||||
widget.append_stderr("snakes!")
|
||||
expected = (_make_stream_output("snakes!", "stderr"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Try appending a second message.
|
||||
widget.append_stderr("more snakes!")
|
||||
expected += (_make_stream_output("more snakes!", "stderr"),)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
|
||||
def test_append_display_data():
|
||||
widget = widget_output.Output()
|
||||
|
||||
# Try appending a Markdown object.
|
||||
widget.append_display_data(Markdown("# snakes!"))
|
||||
expected = (
|
||||
{
|
||||
'output_type': 'display_data',
|
||||
'data': {
|
||||
'text/plain': '<IPython.core.display.Markdown object>',
|
||||
'text/markdown': '# snakes!'
|
||||
},
|
||||
'metadata': {}
|
||||
},
|
||||
)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
||||
|
||||
# Now try appending an Image.
|
||||
image_data = b"foobar"
|
||||
image_data_b64 = image_data if sys.version_info[0] < 3 else 'Zm9vYmFy\n'
|
||||
|
||||
widget.append_display_data(Image(image_data, width=123, height=456))
|
||||
expected += (
|
||||
{
|
||||
'output_type': 'display_data',
|
||||
'data': {
|
||||
'image/png': image_data_b64,
|
||||
'text/plain': '<IPython.core.display.Image object>'
|
||||
},
|
||||
'metadata': {
|
||||
'image/png': {
|
||||
'width': 123,
|
||||
'height': 456
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
assert widget.outputs == expected, repr(widget.outputs)
|
|
@ -0,0 +1,96 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
import inspect
|
||||
import warnings
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import Dropdown, SelectionSlider, Select
|
||||
|
||||
|
||||
class TestDropdown(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
Dropdown()
|
||||
|
||||
def test_deprecation_warning_mapping_options(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
|
||||
# Clearing the internal __warningregistry__ seems to be required for
|
||||
# Python 2 (but not for Python 3)
|
||||
module = inspect.getmodule(Dropdown)
|
||||
getattr(module, '__warningregistry__', {}).clear()
|
||||
|
||||
Dropdown(options={'One': 1, 'Two': 2, 'Three': 3})
|
||||
assert len(w) > 0
|
||||
assert issubclass(w[-1].category, DeprecationWarning)
|
||||
assert "Support for mapping types has been deprecated" in str(w[-1].message)
|
||||
|
||||
|
||||
class TestSelectionSlider(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
SelectionSlider(options=['a', 'b', 'c'])
|
||||
|
||||
def test_index_trigger(self):
|
||||
slider = SelectionSlider(options=['a', 'b', 'c'])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
slider.observe(f, 'index')
|
||||
assert slider.index == 0
|
||||
slider.options = [4, 5, 6]
|
||||
assert slider.index == 0
|
||||
assert slider.value == 4
|
||||
assert slider.label == '4'
|
||||
assert observations == [0]
|
||||
|
||||
class TestSelection(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
select = Select(options=['a', 'b', 'c'])
|
||||
|
||||
def test_index_trigger(self):
|
||||
select = Select(options=[1, 2, 3])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
select.observe(f, 'index')
|
||||
assert select.index == 0
|
||||
select.options = [4, 5, 6]
|
||||
assert select.index == 0
|
||||
assert select.value == 4
|
||||
assert select.label == '4'
|
||||
assert observations == [0]
|
||||
|
||||
def test_duplicate(self):
|
||||
select = Select(options=['first', 1, 'dup', 'dup'])
|
||||
observations = []
|
||||
def f(change):
|
||||
observations.append(change.new)
|
||||
select.observe(f, 'index')
|
||||
select.index = 3
|
||||
assert select.index == 3
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3]
|
||||
select.index = 2
|
||||
assert select.index == 2
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3, 2]
|
||||
select.index = 0
|
||||
assert select.index == 0
|
||||
assert select.value == 'first'
|
||||
assert select.label == 'first'
|
||||
assert observations == [3, 2, 0]
|
||||
|
||||
# picks the first matching value
|
||||
select.value = 'dup'
|
||||
assert select.index == 2
|
||||
assert select.value == 'dup'
|
||||
assert select.label == 'dup'
|
||||
assert observations == [3, 2, 0, 2]
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ..widget_string import Combobox
|
||||
|
||||
|
||||
def test_combobox_creation_blank():
|
||||
w = Combobox()
|
||||
assert w.value == ''
|
||||
assert w.options == ()
|
||||
assert w.ensure_option == False
|
||||
|
||||
|
||||
def test_combobox_creation_kwargs():
|
||||
w = Combobox(
|
||||
value='Chocolate',
|
||||
options=[
|
||||
"Chocolate",
|
||||
"Coconut",
|
||||
"Mint",
|
||||
"Strawberry",
|
||||
"Vanilla",
|
||||
],
|
||||
ensure_option=True
|
||||
)
|
||||
assert w.value == 'Chocolate'
|
||||
assert w.options == (
|
||||
"Chocolate",
|
||||
"Coconut",
|
||||
"Mint",
|
||||
"Strawberry",
|
||||
"Vanilla",
|
||||
)
|
||||
assert w.ensure_option == True
|
|
@ -0,0 +1,718 @@
|
|||
"Testing widget layout templates"
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
try:
|
||||
from unittest import mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import traitlets
|
||||
import ipywidgets as widgets
|
||||
from ipywidgets.widgets.widget_templates import LayoutProperties
|
||||
|
||||
class TestTwoByTwoLayout(TestCase):
|
||||
"""test layout templates"""
|
||||
|
||||
def test_merge_cells(self): #pylint: disable=no-self-use
|
||||
"""test merging cells with missing widgets"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=button2,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=button2,
|
||||
bottom_left=None,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button2,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"bottom-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left is None
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button2,
|
||||
bottom_left=None,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-right top-right"\n' +
|
||||
'"bottom-right bottom-right"')
|
||||
assert box.top_left is None
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 2
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=None,
|
||||
bottom_left=button3,
|
||||
bottom_right=button4)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left bottom-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left.layout.grid_area == 'bottom-left'
|
||||
assert box.bottom_right.layout.grid_area == 'bottom-right'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1,
|
||||
top_right=None,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-left"\n' +
|
||||
'"top-left top-left"')
|
||||
|
||||
assert box.top_left is button1
|
||||
assert box.top_left.layout.grid_area == 'top-left'
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button1,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-right top-right"\n' +
|
||||
'"top-right top-right"')
|
||||
|
||||
assert box.top_right is button1
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.top_left is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=None,
|
||||
bottom_left=None,
|
||||
bottom_right=None)
|
||||
|
||||
assert box.layout.grid_template_areas is None
|
||||
assert box.top_left is None
|
||||
assert box.top_right is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert not box.get_state()['children']
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=None,
|
||||
top_right=button1,
|
||||
bottom_left=None,
|
||||
bottom_right=None,
|
||||
merge=False)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
|
||||
assert box.top_right is button1
|
||||
assert box.top_right.layout.grid_area == 'top-right'
|
||||
assert box.top_left is None
|
||||
assert box.bottom_left is None
|
||||
assert box.bottom_right is None
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
def test_keep_layout_options(self): #pylint: disable=no-self-use
|
||||
"""test whether layout options are passed down to GridBox"""
|
||||
|
||||
layout = widgets.Layout(align_items="center")
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout)
|
||||
|
||||
assert box.layout.align_items == 'center'
|
||||
|
||||
def test_pass_layout_options(self): #pylint: disable=no-self-use
|
||||
"""test whether the extra layout options of the template class are
|
||||
passed down to Layout object"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
|
||||
assert box.layout.grid_gap == "10px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
# we still should be able to pass them through layout
|
||||
layout = widgets.Layout(grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout
|
||||
)
|
||||
|
||||
assert box.layout.grid_gap == "10px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
# values passed directly in the constructor should overwite layout options
|
||||
layout = widgets.Layout(grid_gap="10px", justify_content="center",
|
||||
align_items="center")
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button2,
|
||||
bottom_left=button3, bottom_right=button4,
|
||||
layout=layout, grid_gap="30px"
|
||||
)
|
||||
|
||||
assert box.layout.grid_gap == "30px"
|
||||
assert box.layout.justify_content == "center"
|
||||
assert box.layout.align_items == "center"
|
||||
|
||||
|
||||
@mock.patch("ipywidgets.Layout.send_state")
|
||||
def test_update_dynamically(self, send_state): #pylint: disable=no-self-use
|
||||
"""test whether it's possible to add widget outside __init__"""
|
||||
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button3,
|
||||
bottom_left=None, bottom_right=button4)
|
||||
from ipykernel.kernelbase import Kernel
|
||||
|
||||
state = box.get_state()
|
||||
assert len(state['children']) == 3
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
|
||||
box.layout.comm.kernel = mock.MagicMock(spec=Kernel) #for mocking purposes
|
||||
send_state.reset_mock()
|
||||
box.bottom_left = button2
|
||||
|
||||
state = box.get_state()
|
||||
assert len(state['children']) == 4
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
# check whether frontend was informed
|
||||
send_state.assert_called_once_with(key="grid_template_areas")
|
||||
|
||||
box = widgets.TwoByTwoLayout(top_left=button1, top_right=button3,
|
||||
bottom_left=None, bottom_right=button4)
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"top-left bottom-right"')
|
||||
box.layout.comm.kernel = mock.MagicMock(spec=Kernel) #for mocking purposes
|
||||
send_state.reset_mock()
|
||||
box.merge = False
|
||||
assert box.layout.grid_template_areas == ('"top-left top-right"\n' +
|
||||
'"bottom-left bottom-right"')
|
||||
send_state.assert_called_once_with(key="grid_template_areas")
|
||||
|
||||
|
||||
class TestAppLayout(TestCase):
|
||||
"""test layout templates"""
|
||||
|
||||
def test_create_with_defaults(self):
|
||||
"test creating with default values"
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
box = widgets.AppLayout(
|
||||
footer=footer,
|
||||
header=header,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
|
||||
assert len(box.get_state()['children']) == 5
|
||||
|
||||
# empty layout should produce no effects
|
||||
|
||||
box = widgets.AppLayout()
|
||||
assert box.layout.grid_template_areas is None
|
||||
assert box.layout.grid_template_columns is None
|
||||
assert box.layout.grid_template_rows is None
|
||||
assert len(box.get_state()['children']) == 0
|
||||
|
||||
|
||||
def test_merge_empty_cells(self):
|
||||
"test if cells are correctly merged"
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
# merge all if only one widget
|
||||
box = widgets.AppLayout(
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"center center center"\n' +
|
||||
'"center center center"\n' +
|
||||
'"center center center"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
box = widgets.AppLayout(
|
||||
left_sidebar=left_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"left-sidebar left-sidebar left-sidebar"\n' +
|
||||
'"left-sidebar left-sidebar left-sidebar"\n' +
|
||||
'"left-sidebar left-sidebar left-sidebar"')
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
# merge left and right sidebars with center
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
left_sidebar=left_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center center"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
right_sidebar=right_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"center center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"center center center"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# if only center missing, remove it from view
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header"\n' +
|
||||
'"left-sidebar right-sidebar"\n' +
|
||||
'"footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.center is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
# center and one sidebar missing -> 3 row arrangement
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=None,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header"\n' +
|
||||
'"right-sidebar right-sidebar"\n' +
|
||||
'"footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.center is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
|
||||
# remove middle row is both sidebars and center missing
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=None,
|
||||
left_sidebar=None,
|
||||
right_sidebar=None
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header"\n' +
|
||||
'"footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center is None
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar is None
|
||||
assert len(box.get_state()['children']) == 2
|
||||
|
||||
|
||||
|
||||
# do not merge if merge=False
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
center=center,
|
||||
merge=False
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar is None
|
||||
assert box.right_sidebar is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# merge header and footer simply removes it from view
|
||||
box = widgets.AppLayout(
|
||||
footer=footer,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"left-sidebar center right-sidebar"\n' +
|
||||
'"footer footer footer"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.footer.layout.grid_area == 'footer'
|
||||
assert box.header is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"header header header"\n' +
|
||||
'"left-sidebar center right-sidebar"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.header.layout.grid_area == 'header'
|
||||
assert box.footer is None
|
||||
assert len(box.get_state()['children']) == 4
|
||||
|
||||
box = widgets.AppLayout(
|
||||
center=center,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=right_sidebar
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == '"left-sidebar center right-sidebar"'
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
assert box.left_sidebar.layout.grid_area == 'left-sidebar'
|
||||
assert box.right_sidebar.layout.grid_area == 'right-sidebar'
|
||||
assert box.footer is None
|
||||
assert box.header is None
|
||||
assert len(box.get_state()['children']) == 3
|
||||
|
||||
# merge all if only one widget
|
||||
box = widgets.AppLayout(
|
||||
center=center
|
||||
)
|
||||
|
||||
assert box.layout.grid_template_areas == ('"center center center"\n' +
|
||||
'"center center center"\n' +
|
||||
'"center center center"')
|
||||
assert box.center.layout.grid_area == 'center'
|
||||
|
||||
assert len(box.get_state()['children']) == 1
|
||||
|
||||
def test_size_to_css(self):
|
||||
|
||||
box = widgets.AppLayout()
|
||||
assert box._size_to_css("100px") == '100px'
|
||||
assert box._size_to_css("1fr") == '1fr'
|
||||
assert box._size_to_css("2.5fr") == '2.5fr'
|
||||
assert box._size_to_css('2.5') == '2.5fr'
|
||||
assert box._size_to_css('25%') == '25%'
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
box._size_to_css('this is not correct size')
|
||||
|
||||
|
||||
def test_set_pane_widths_heights(self):
|
||||
|
||||
footer = widgets.Button()
|
||||
header = widgets.Button()
|
||||
center = widgets.Button()
|
||||
left_sidebar = widgets.Button()
|
||||
right_sidebar = widgets.Button()
|
||||
|
||||
box = widgets.AppLayout(
|
||||
header=header,
|
||||
footer=footer,
|
||||
left_sidebar=left_sidebar,
|
||||
right_sidebar=left_sidebar,
|
||||
center=center
|
||||
)
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_widths = ['1fx', '1fx', '1fx', '1fx']
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_widths = ['1fx', '1fx']
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_heights = ['1fx', '1fx', '1fx', '1fx']
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box.pane_heights = ['1fx', '1fx']
|
||||
|
||||
assert box.layout.grid_template_rows == "1fr 3fr 1fr"
|
||||
assert box.layout.grid_template_columns == "1fr 2fr 1fr"
|
||||
|
||||
box.pane_heights = ['3fr', '100px', 20]
|
||||
assert box.layout.grid_template_rows == "3fr 100px 20fr"
|
||||
assert box.layout.grid_template_columns == "1fr 2fr 1fr"
|
||||
|
||||
box.pane_widths = [3, 3, 1]
|
||||
assert box.layout.grid_template_rows == "3fr 100px 20fr"
|
||||
assert box.layout.grid_template_columns == "3fr 3fr 1fr"
|
||||
|
||||
class TestGridspecLayout(TestCase):
|
||||
"test GridspecLayout"
|
||||
|
||||
def test_init(self):
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout()
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout(n_rows=-1, n_columns=1)
|
||||
|
||||
box = widgets.GridspecLayout(n_rows=5, n_columns=3)
|
||||
assert box.n_rows == 5
|
||||
assert box.n_columns == 3
|
||||
assert len(box._grid_template_areas) == 5
|
||||
assert len(box._grid_template_areas[0]) == 3
|
||||
|
||||
box = widgets.GridspecLayout(1, 2)
|
||||
assert box.n_rows == 1
|
||||
assert box.n_columns == 2
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
box = widgets.GridspecLayout(0, 0)
|
||||
|
||||
def test_setitem_index(self):
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
button3 = widgets.Button()
|
||||
button4 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
button1_label = button1.layout.grid_area
|
||||
assert button1 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{} . ."\n". . ."'''.format(button1_label)
|
||||
|
||||
box[-1, -1] = button2
|
||||
button2_label = button2.layout.grid_area
|
||||
assert button1_label != button2_label
|
||||
assert button2 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{} . ."\n". . {}"'''.format(button1_label,
|
||||
button2_label)
|
||||
|
||||
box[1, 0] = button3
|
||||
button3_label = button3.layout.grid_area
|
||||
assert button1_label != button3_label
|
||||
assert button2_label != button3_label
|
||||
assert button3 in box.children
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b3} . {b2}"'''.format(b1=button1_label,
|
||||
b2=button2_label,
|
||||
b3=button3_label)
|
||||
|
||||
#replace widget
|
||||
box[1, 0] = button4
|
||||
button4_label = button4.layout.grid_area
|
||||
assert button1_label != button4_label
|
||||
assert button2_label != button4_label
|
||||
assert button4 in box.children
|
||||
assert button3 not in box.children
|
||||
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b4} . {b2}"'''.format(b1=button1_label,
|
||||
b2=button2_label,
|
||||
b4=button4_label)
|
||||
|
||||
def test_setitem_slices(self):
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[:2, 0] = button1
|
||||
assert len(box.children) == 1
|
||||
assert button1 in box.children
|
||||
button1_label = button1.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''"{b1} . ."\n"{b1} . ."'''.format(b1=button1_label)
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
|
||||
box[:2, 1:] = button1
|
||||
assert len(box.children) == 1
|
||||
assert button1 in box.children
|
||||
button1_label = button1.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''". {b1} {b1}"\n". {b1} {b1}"'''.format(b1=button1_label)
|
||||
|
||||
# replace button
|
||||
box[:2, 1:] = button2
|
||||
assert len(box.children) == 1
|
||||
assert button2 in box.children
|
||||
button2_label = button2.layout.grid_area
|
||||
|
||||
assert box.layout.grid_template_areas == '''". {b1} {b1}"\n". {b1} {b1}"'''.format(b1=button2_label)
|
||||
|
||||
def test_getitem_index(self):
|
||||
"test retrieving widget"
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
|
||||
assert box[0, 0] is button1
|
||||
|
||||
def test_getitem_slices(self):
|
||||
"test retrieving widgets with slices"
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
|
||||
box[:2, 0] = button1
|
||||
assert box[:2, 0] is button1
|
||||
|
||||
box = widgets.GridspecLayout(2, 3)
|
||||
button1 = widgets.Button()
|
||||
button2 = widgets.Button()
|
||||
|
||||
box[0, 0] = button1
|
||||
box[1, 0] = button2
|
||||
assert box[0, 0] is button1
|
||||
assert box[1, 0] is button2
|
||||
|
||||
with pytest.raises(TypeError, match="The slice spans"):
|
||||
button = box[:2, 0]
|
||||
|
||||
|
||||
class TestLayoutProperties(TestCase):
|
||||
"""test mixin with layout properties"""
|
||||
|
||||
class DummyTemplate(widgets.GridBox, LayoutProperties):
|
||||
location = traitlets.Instance(widgets.Widget, allow_none=True)
|
||||
|
||||
def test_layout_updated_on_trait_change(self):
|
||||
"test whether respective layout traits are updated when traits change"
|
||||
|
||||
template = self.DummyTemplate(width="100%")
|
||||
assert template.width == '100%'
|
||||
assert template.layout.width == '100%'
|
||||
|
||||
template.width = 'auto'
|
||||
assert template.width == 'auto'
|
||||
assert template.layout.width == 'auto'
|
||||
|
||||
def test_align_items_extra_options(self):
|
||||
|
||||
template = self.DummyTemplate(align_items='top')
|
||||
assert template.align_items == 'top'
|
||||
assert template.layout.align_items == 'flex-start'
|
||||
|
||||
template.align_items = 'bottom'
|
||||
assert template.align_items == 'bottom'
|
||||
assert template.layout.align_items == 'flex-end'
|
||||
|
||||
def test_validate_properties(self):
|
||||
|
||||
prop_obj = self.DummyTemplate()
|
||||
for prop in LayoutProperties.align_items.values:
|
||||
prop_obj.align_items = prop
|
||||
assert prop_obj.align_items == prop
|
||||
|
||||
with pytest.raises(traitlets.TraitError):
|
||||
prop_obj.align_items = 'any default position'
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from traitlets import TraitError
|
||||
|
||||
from ipywidgets import FileUpload
|
||||
|
||||
|
||||
class TestFileUpload(TestCase):
|
||||
|
||||
def test_construction(self):
|
||||
uploader = FileUpload()
|
||||
# Default
|
||||
assert uploader.accept == ''
|
||||
assert not uploader.multiple
|
||||
assert not uploader.disabled
|
||||
|
||||
def test_construction_with_params(self):
|
||||
uploader = FileUpload(accept='.txt',
|
||||
multiple=True,
|
||||
disabled=True)
|
||||
assert uploader.accept == '.txt'
|
||||
assert uploader.multiple
|
||||
assert uploader.disabled
|
47
venv/Lib/site-packages/ipywidgets/widgets/tests/utils.py
Normal file
47
venv/Lib/site-packages/ipywidgets/widgets/tests/utils.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from ipykernel.comm import Comm
|
||||
from ipywidgets import Widget
|
||||
|
||||
class DummyComm(Comm):
|
||||
comm_id = 'a-b-c-d'
|
||||
kernel = 'Truthy'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DummyComm, self).__init__(*args, **kwargs)
|
||||
self.messages = []
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def send(self, *args, **kwargs):
|
||||
self.messages.append((args, kwargs))
|
||||
|
||||
def close(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
_widget_attrs = {}
|
||||
undefined = object()
|
||||
|
||||
def setup_test_comm():
|
||||
_widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined)
|
||||
Widget._comm_default = lambda self: DummyComm()
|
||||
_widget_attrs['_ipython_display_'] = Widget._ipython_display_
|
||||
def raise_not_implemented(*args, **kwargs):
|
||||
raise NotImplementedError()
|
||||
Widget._ipython_display_ = raise_not_implemented
|
||||
|
||||
def teardown_test_comm():
|
||||
for attr, value in _widget_attrs.items():
|
||||
if value is undefined:
|
||||
delattr(Widget, attr)
|
||||
else:
|
||||
setattr(Widget, attr, value)
|
||||
_widget_attrs.clear()
|
||||
|
||||
def setup():
|
||||
setup_test_comm()
|
||||
|
||||
def teardown():
|
||||
teardown_test_comm()
|
Loading…
Add table
Add a link
Reference in a new issue