Skip to content

Commit 3dbd281

Browse files
authored
Merge pull request #3557 from jasongrout/optiondicts
Allow Mappings to be given as the options in a selection widget again.
2 parents e722a0d + 17cb4f9 commit 3dbd281

File tree

3 files changed

+47
-29
lines changed

3 files changed

+47
-29
lines changed

python/ipywidgets/ipywidgets/widgets/tests/test_interaction.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def check_widget(w, **d):
5252
te = type(expected)
5353
assert tv is te, "type({}.{}) = {!r} != {!r}".format(w.__class__.__name__, attr, tv, te)
5454

55-
def check_widgets(container, **to_check):
55+
def check_widget_children(container, **to_check):
5656
"""Check that widgets are created as expected"""
5757
# build a widget dictionary, so it matches
5858
widgets = {}
@@ -144,7 +144,7 @@ def test_list_str():
144144
_options_labels=tuple(values),
145145
_options_values=tuple(values),
146146
)
147-
check_widgets(c, lis=d)
147+
check_widget_children(c, lis=d)
148148

149149
def test_list_int():
150150
values = [3, 1, 2]
@@ -158,7 +158,7 @@ def test_list_int():
158158
_options_labels=tuple(str(v) for v in values),
159159
_options_values=tuple(values),
160160
)
161-
check_widgets(c, lis=d)
161+
check_widget_children(c, lis=d)
162162

163163
def test_list_tuple():
164164
values = [(3, 300), (1, 100), (2, 200)]
@@ -172,7 +172,7 @@ def test_list_tuple():
172172
_options_labels=("3", "1", "2"),
173173
_options_values=(300, 100, 200),
174174
)
175-
check_widgets(c, lis=d)
175+
check_widget_children(c, lis=d)
176176

177177
def test_list_tuple_invalid():
178178
for bad in [
@@ -187,17 +187,33 @@ def test_dict():
187187
dict(a=5),
188188
dict(a=5, b='b', c=dict),
189189
]:
190-
with pytest.raises(TypeError):
191-
c = interactive(f, d=d)
192-
190+
c = interactive(f, d=d)
191+
w = c.children[0]
192+
check = dict(
193+
cls=widgets.Dropdown,
194+
description='d',
195+
value=next(iter(d.values())),
196+
options=d,
197+
_options_labels=tuple(d.keys()),
198+
_options_values=tuple(d.values()),
199+
)
200+
check_widget(w, **check)
193201

194202
def test_ordereddict():
195203
from collections import OrderedDict
196204
items = [(3, 300), (1, 100), (2, 200)]
197205
first = items[0][1]
198206
values = OrderedDict(items)
199-
with pytest.raises(TypeError):
200-
c = interactive(f, lis=values)
207+
c = interactive(f, lis=values)
208+
assert len(c.children) == 2
209+
d = dict(
210+
cls=widgets.Dropdown,
211+
value=first,
212+
options=values,
213+
_options_labels=("3", "1", "2"),
214+
_options_values=(300, 100, 200),
215+
)
216+
check_widget_children(c, lis=d)
201217

202218
def test_iterable():
203219
def yield_values():
@@ -214,7 +230,7 @@ def yield_values():
214230
_options_labels=("3", "1", "2"),
215231
_options_values=(3, 1, 2),
216232
)
217-
check_widgets(c, lis=d)
233+
check_widget_children(c, lis=d)
218234

219235
def test_iterable_tuple():
220236
values = [(3, 300), (1, 100), (2, 200)]
@@ -228,7 +244,7 @@ def test_iterable_tuple():
228244
_options_labels=("3", "1", "2"),
229245
_options_values=(300, 100, 200),
230246
)
231-
check_widgets(c, lis=d)
247+
check_widget_children(c, lis=d)
232248

233249
def test_mapping():
234250
from collections.abc import Mapping
@@ -257,7 +273,7 @@ def items(self):
257273
_options_labels=("3", "1", "2"),
258274
_options_values=(300, 100, 200),
259275
)
260-
check_widgets(c, lis=d)
276+
check_widget_children(c, lis=d)
261277

262278
def test_decorator_kwarg(clear_display):
263279
with patch.object(interaction, 'display', record_display):
@@ -566,15 +582,14 @@ def test_multiple_selection():
566582
check_widget(w, value=(1, 2))
567583

568584
# dict style
569-
with pytest.raises(TypeError):
570-
w = smw(options={1: 1})
585+
w.options = {1: 1}
586+
check_widget(w, options={1:1})
571587

572588
# updating
573589
w.options = (1,)
574590
with pytest.raises(TraitError):
575591
w.value = (2,)
576-
check_widget(w, options=(1,))
577-
592+
check_widget(w, options=(1,) )
578593

579594
def test_interact_noinspect():
580595
a = 'hello'

python/ipywidgets/ipywidgets/widgets/tests/test_widget_selection.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
import inspect
55
from unittest import TestCase
66

7-
import pytest
8-
97
from traitlets import TraitError
108

119
from ipywidgets import Dropdown, SelectionSlider, Select
@@ -16,9 +14,9 @@ class TestDropdown(TestCase):
1614
def test_construction(self):
1715
Dropdown()
1816

19-
def test_raise_mapping_options(self):
20-
with pytest.raises(TypeError):
21-
Dropdown(options={'One': 1, 'Two': 2, 'Three': 3})
17+
def test_dict_mapping_options(self):
18+
d = Dropdown(options={'One': 1, 'Two': 2, 'Three': 3})
19+
assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')}
2220

2321
def test_setting_options_from_list(self):
2422
d = Dropdown()
@@ -37,8 +35,10 @@ def test_setting_options_from_list_tuples(self):
3735
def test_setting_options_from_dict(self):
3836
d = Dropdown()
3937
assert d.options == ()
40-
with pytest.raises(TypeError):
41-
d.options = {'One': 1}
38+
d.options = {'One': 1, 'Two': 2, 'Three': 3}
39+
assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')}
40+
41+
4242

4343

4444
class TestSelectionSlider(TestCase):

python/ipywidgets/ipywidgets/widgets/widget_selection.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424
_doc_snippets['selection_params'] = """
2525
options: list
2626
The options for the dropdown. This can either be a list of values, e.g.
27-
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of
27+
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, a list of
2828
(label, value) pairs, e.g.
29-
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``.
29+
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``, or a Mapping between
30+
labels and values, e.g., ``{'Galileo': 0, 'Brahe': 1, 'Hubble': 2}``.
3031
3132
index: int
3233
The index of the current selection.
@@ -55,7 +56,8 @@
5556
The options for the dropdown. This can either be a list of values, e.g.
5657
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of
5758
(label, value) pairs, e.g.
58-
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``.
59+
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``, or a Mapping between
60+
labels and values, e.g., ``{'Galileo': 0, 'Brahe': 1, 'Hubble': 2}``.
5961
The labels are the strings that will be displayed in the UI,
6062
representing the actual Python choices, and should be unique.
6163
@@ -110,9 +112,10 @@ def _make_options(x):
110112
The input can be
111113
* an iterable of (label, value) pairs
112114
* an iterable of values, and labels will be generated
115+
* a Mapping between labels and values
113116
"""
114117
if isinstance(x, Mapping):
115-
raise TypeError("options must be a iterable of values or of (label, value) pairs. If x is your Mapping, use x.items() for the options.")
118+
x = x.items()
116119

117120
# only iterate once through the options.
118121
xlist = tuple(x)
@@ -151,7 +154,7 @@ class _Selection(DescriptionWidget, ValueWidget, CoreWidget):
151154
index = Int(None, help="Selected index", allow_none=True).tag(sync=True)
152155

153156
options = Any((),
154-
help="""Iterable of values or (label, value) pairs that the user can select.
157+
help="""Iterable of values, (label, value) pairs, or Mapping between labels and values that the user can select.
155158
156159
The labels are the strings that will be displayed in the UI, representing the
157160
actual Python choices, and should be unique.
@@ -298,7 +301,7 @@ class _MultipleSelection(DescriptionWidget, ValueWidget, CoreWidget):
298301
index = TypedTuple(trait=Int(), help="Selected indices").tag(sync=True)
299302

300303
options = Any((),
301-
help="""Iterable of values or (label, value) pairs that the user can select.
304+
help="""Iterable of values, (label, value) pairs, or Mapping between labels and values that the user can select.
302305
303306
The labels are the strings that will be displayed in the UI, representing the
304307
actual Python choices, and should be unique.

0 commit comments

Comments
 (0)