mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-07-02 22:30:48 +00:00
first commit
This commit is contained in:
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,737 @@
|
||||
# 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():
|
||||
try:
|
||||
# Python 3
|
||||
from collections.abc import Mapping
|
||||
except ImportError:
|
||||
# Python 2
|
||||
from collections import Mapping
|
||||
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)
|
||||
|
@ -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,40 @@
|
||||
# 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, DummyComm
|
||||
|
||||
from ..widget import Widget
|
||||
|
||||
from ..._version import __control_protocol_version__
|
||||
|
||||
# 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 == []
|
||||
|
||||
|
||||
def test_control():
|
||||
comm = DummyComm()
|
||||
Widget.close_all()
|
||||
w = SimpleWidget()
|
||||
Widget.handle_control_comm_opened(
|
||||
comm, dict(metadata={'version': __control_protocol_version__})
|
||||
)
|
||||
Widget._handle_control_comm_msg(dict(content=dict(
|
||||
data={'method': 'request_states'}
|
||||
)))
|
||||
assert comm.messages
|
@ -0,0 +1,394 @@
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
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
|
||||
|
||||
import ipywidgets
|
||||
from ipywidgets import Widget
|
||||
|
||||
|
||||
@pytest.fixture(params=[True, False])
|
||||
def echo(request):
|
||||
oldvalue = ipywidgets.widgets.widget.JUPYTER_WIDGETS_ECHO
|
||||
ipywidgets.widgets.widget.JUPYTER_WIDGETS_ECHO = request.param
|
||||
yield request.param
|
||||
ipywidgets.widgets.widget.JUPYTER_WIDGETS_ECHO = oldvalue
|
||||
|
||||
#
|
||||
# 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(echo):
|
||||
w = SimpleWidget()
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
b=[True, False, True],
|
||||
c=[False, True, False],
|
||||
))
|
||||
|
||||
assert len(w.comm.messages) == (1 if echo else 0)
|
||||
|
||||
|
||||
def test_set_state_transformer(echo):
|
||||
w = TransformerWidget()
|
||||
w.set_state(dict(
|
||||
d=[True, False, True]
|
||||
))
|
||||
# Since the deserialize step changes the state, this should send an update
|
||||
expected = []
|
||||
if echo:
|
||||
expected.append(
|
||||
((), dict(
|
||||
buffers=[],
|
||||
data=dict(
|
||||
buffer_paths=[],
|
||||
method='echo_update',
|
||||
state=dict(d=[True, False, True]),
|
||||
))))
|
||||
expected.append(
|
||||
((), dict(
|
||||
buffers=[],
|
||||
data=dict(
|
||||
buffer_paths=[],
|
||||
method='update',
|
||||
state=dict(d=[False, True, False]),
|
||||
))))
|
||||
assert w.comm.messages == expected
|
||||
|
||||
|
||||
def test_set_state_data(echo):
|
||||
w = DataWidget()
|
||||
data = memoryview(b'x'*30)
|
||||
w.set_state(dict(
|
||||
a=True,
|
||||
d={'data': data},
|
||||
))
|
||||
assert len(w.comm.messages) == (1 if echo else 0)
|
||||
|
||||
|
||||
def test_set_state_data_truncate(echo):
|
||||
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) == 2 if echo else 1 # ensure we didn't get more than expected
|
||||
msg = w.comm.messages[-1]
|
||||
# Assert that the data update (truncation) sends an update
|
||||
buffers = msg[1].pop('buffers')
|
||||
assert msg == ((), dict(
|
||||
data=dict(
|
||||
method='update',
|
||||
state=dict(d={}),
|
||||
buffer_paths=[['d', 'data']]
|
||||
)))
|
||||
|
||||
# Sanity:
|
||||
assert len(buffers) == 1
|
||||
assert buffers[0] == data[:20].tobytes()
|
||||
|
||||
|
||||
def test_set_state_numbers_int(echo):
|
||||
# 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 one update message gets produced
|
||||
assert len(w.comm.messages) == (1 if echo else 0)
|
||||
|
||||
|
||||
def test_set_state_numbers_float(echo):
|
||||
w = NumberWidget()
|
||||
# Set floats to int-like floats
|
||||
w.set_state(dict(
|
||||
f = 1.0,
|
||||
cf = 2.0,
|
||||
ci = 4.0
|
||||
))
|
||||
# Ensure one update message gets produced
|
||||
assert len(w.comm.messages) == (1 if echo else 0)
|
||||
|
||||
|
||||
def test_set_state_float_to_float(echo):
|
||||
w = NumberWidget()
|
||||
# Set floats to float
|
||||
w.set_state(dict(
|
||||
f = 1.2,
|
||||
cf = 2.6,
|
||||
))
|
||||
# Ensure one message gets produced
|
||||
assert len(w.comm.messages) == (1 if echo else 0)
|
||||
|
||||
|
||||
def test_set_state_cint_to_float(echo):
|
||||
w = NumberWidget()
|
||||
|
||||
# Set CInt to float
|
||||
w.set_state(dict(
|
||||
ci = 5.6
|
||||
))
|
||||
# Ensure an update message gets produced
|
||||
assert len(w.comm.messages) == (2 if echo else 1)
|
||||
msg = w.comm.messages[-1]
|
||||
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(echo):
|
||||
w = NumberWidget()
|
||||
|
||||
# Set Int to float
|
||||
with pytest.raises(TraitError):
|
||||
w.set_state(dict(
|
||||
i = 3.5
|
||||
))
|
||||
|
||||
def test_property_lock(echo):
|
||||
# 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
|
||||
assert widget.stop is True
|
||||
|
||||
# we expect no new state to be sent
|
||||
calls = []
|
||||
widget._send.assert_has_calls(calls)
|
||||
|
||||
|
||||
def test_hold_sync(echo):
|
||||
# when this widget's value is set to 42, it sets the value to 2, and also sets a different trait value
|
||||
class AnnoyingWidget(Widget):
|
||||
value = Float().tag(sync=True)
|
||||
other = Float().tag(sync=True)
|
||||
|
||||
@observe('value')
|
||||
def _propagate_value(self, change):
|
||||
print('_propagate_value', change.new)
|
||||
if change.new == 42:
|
||||
with self.hold_sync():
|
||||
self.value = 2
|
||||
self.other = 11
|
||||
|
||||
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 == 2
|
||||
assert widget.other == 11
|
||||
|
||||
msg = {'method': 'echo_update', 'state': {'value': 42.0}, 'buffer_paths': []}
|
||||
call42 = mock.call(msg, buffers=[])
|
||||
|
||||
msg = {'method': 'update', 'state': {'value': 2.0, 'other': 11.0}, 'buffer_paths': []}
|
||||
call2 = mock.call(msg, buffers=[])
|
||||
|
||||
calls = [call42, call2] if echo else [call2]
|
||||
widget._send.assert_has_calls(calls)
|
||||
|
||||
|
||||
def test_echo(echo):
|
||||
# we always echo values back to the frontend if configured
|
||||
class ValueWidget(Widget):
|
||||
value = Float().tag(sync=True)
|
||||
|
||||
widget = ValueWidget(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 this to be echoed
|
||||
msg = {'method': 'echo_update', 'state': {'value': 42.0}, 'buffer_paths': []}
|
||||
call42 = mock.call(msg, buffers=[])
|
||||
|
||||
calls = [call42] if echo else []
|
||||
widget._send.assert_has_calls(calls)
|
||||
|
||||
|
||||
|
||||
def test_echo_single(echo):
|
||||
# we always echo multiple changes back in 1 update
|
||||
class ValueWidget(Widget):
|
||||
value = Float().tag(sync=True)
|
||||
square = Float().tag(sync=True)
|
||||
@observe('value')
|
||||
def _square(self, change):
|
||||
self.square = self.value**2
|
||||
|
||||
widget = ValueWidget(value=1)
|
||||
assert widget.value == 1
|
||||
|
||||
widget._send = mock.MagicMock()
|
||||
# this mimics a value coming from the front end
|
||||
widget._handle_msg({
|
||||
'content': {
|
||||
'data': {
|
||||
'method': 'update',
|
||||
'state': {
|
||||
'value': 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
assert widget.value == 8
|
||||
assert widget.square == 64
|
||||
|
||||
# we expect this to be echoed
|
||||
# note that only value is echoed, not square
|
||||
msg = {'method': 'echo_update', 'state': {'value': 8.0}, 'buffer_paths': []}
|
||||
call = mock.call(msg, buffers=[])
|
||||
|
||||
msg = {'method': 'update', 'state': {'square': 64}, 'buffer_paths': []}
|
||||
call2 = mock.call(msg, buffers=[])
|
||||
|
||||
|
||||
calls = [call, call2] if echo else [call2]
|
||||
widget._send.assert_has_calls(calls)
|
||||
|
||||
|
||||
def test_no_echo(echo):
|
||||
# in cases where values coming from the frontend are 'heavy', we might want to opt out
|
||||
class ValueWidget(Widget):
|
||||
value = Float().tag(sync=True, echo_update=False)
|
||||
|
||||
widget = ValueWidget(value=1)
|
||||
assert widget.value == 1
|
||||
|
||||
widget._send = mock.MagicMock()
|
||||
# this mimics a value coming from the front end
|
||||
widget._handle_msg({
|
||||
'content': {
|
||||
'data': {
|
||||
'method': 'update',
|
||||
'state': {
|
||||
'value': 42,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
assert widget.value == 42
|
||||
|
||||
# widget._send.assert_not_called(calls)
|
||||
widget._send.assert_not_called()
|
||||
|
||||
# a regular set should sync to the frontend
|
||||
widget.value = 43
|
||||
widget._send.assert_has_calls([mock.call({'method': 'update', 'state': {'value': 43.0}, 'buffer_paths': []}, buffers=[])])
|
||||
|
||||
|
||||
|
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()
|
Reference in New Issue
Block a user