diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_interaction.py b/python/ipywidgets/ipywidgets/widgets/tests/test_interaction.py index 7038bb44ad..0821eb6c20 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_interaction.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_interaction.py @@ -52,7 +52,7 @@ def check_widget(w, **d): te = type(expected) assert tv is te, "type({}.{}) = {!r} != {!r}".format(w.__class__.__name__, attr, tv, te) -def check_widgets(container, **to_check): +def check_widget_children(container, **to_check): """Check that widgets are created as expected""" # build a widget dictionary, so it matches widgets = {} @@ -144,7 +144,7 @@ def test_list_str(): _options_labels=tuple(values), _options_values=tuple(values), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_list_int(): values = [3, 1, 2] @@ -158,7 +158,7 @@ def test_list_int(): _options_labels=tuple(str(v) for v in values), _options_values=tuple(values), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_list_tuple(): values = [(3, 300), (1, 100), (2, 200)] @@ -172,7 +172,7 @@ def test_list_tuple(): _options_labels=("3", "1", "2"), _options_values=(300, 100, 200), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_list_tuple_invalid(): for bad in [ @@ -187,17 +187,33 @@ def test_dict(): dict(a=5), dict(a=5, b='b', c=dict), ]: - with pytest.raises(TypeError): - c = interactive(f, d=d) - + 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) - with pytest.raises(TypeError): - c = interactive(f, lis=values) + 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_widget_children(c, lis=d) def test_iterable(): def yield_values(): @@ -214,7 +230,7 @@ def yield_values(): _options_labels=("3", "1", "2"), _options_values=(3, 1, 2), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_iterable_tuple(): values = [(3, 300), (1, 100), (2, 200)] @@ -228,7 +244,7 @@ def test_iterable_tuple(): _options_labels=("3", "1", "2"), _options_values=(300, 100, 200), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_mapping(): from collections.abc import Mapping @@ -257,7 +273,7 @@ def items(self): _options_labels=("3", "1", "2"), _options_values=(300, 100, 200), ) - check_widgets(c, lis=d) + check_widget_children(c, lis=d) def test_decorator_kwarg(clear_display): with patch.object(interaction, 'display', record_display): @@ -566,15 +582,14 @@ def test_multiple_selection(): check_widget(w, value=(1, 2)) # dict style - with pytest.raises(TypeError): - w = smw(options={1: 1}) + w.options = {1: 1} + check_widget(w, options={1:1}) # updating w.options = (1,) with pytest.raises(TraitError): w.value = (2,) - check_widget(w, options=(1,)) - + check_widget(w, options=(1,) ) def test_interact_noinspect(): a = 'hello' diff --git a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_selection.py b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_selection.py index 747dec1cba..3ddbdcddec 100644 --- a/python/ipywidgets/ipywidgets/widgets/tests/test_widget_selection.py +++ b/python/ipywidgets/ipywidgets/widgets/tests/test_widget_selection.py @@ -4,8 +4,6 @@ import inspect from unittest import TestCase -import pytest - from traitlets import TraitError from ipywidgets import Dropdown, SelectionSlider, Select @@ -16,9 +14,9 @@ class TestDropdown(TestCase): def test_construction(self): Dropdown() - def test_raise_mapping_options(self): - with pytest.raises(TypeError): - Dropdown(options={'One': 1, 'Two': 2, 'Three': 3}) + def test_dict_mapping_options(self): + d = Dropdown(options={'One': 1, 'Two': 2, 'Three': 3}) + assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')} def test_setting_options_from_list(self): d = Dropdown() @@ -37,8 +35,10 @@ def test_setting_options_from_list_tuples(self): def test_setting_options_from_dict(self): d = Dropdown() assert d.options == () - with pytest.raises(TypeError): - d.options = {'One': 1} + d.options = {'One': 1, 'Two': 2, 'Three': 3} + assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')} + + class TestSelectionSlider(TestCase): diff --git a/python/ipywidgets/ipywidgets/widgets/widget_selection.py b/python/ipywidgets/ipywidgets/widgets/widget_selection.py index cd572b75fd..d81a779b96 100644 --- a/python/ipywidgets/ipywidgets/widgets/widget_selection.py +++ b/python/ipywidgets/ipywidgets/widgets/widget_selection.py @@ -24,9 +24,10 @@ _doc_snippets['selection_params'] = """ options: list The options for the dropdown. This can either be a list of values, e.g. - ``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of + ``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, a list of (label, value) pairs, e.g. - ``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``. + ``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``, or a Mapping between + labels and values, e.g., ``{'Galileo': 0, 'Brahe': 1, 'Hubble': 2}``. index: int The index of the current selection. @@ -55,7 +56,8 @@ The options for the dropdown. This can either be a list of values, e.g. ``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of (label, value) pairs, e.g. - ``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``. + ``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``, or a Mapping between + labels and values, e.g., ``{'Galileo': 0, 'Brahe': 1, 'Hubble': 2}``. The labels are the strings that will be displayed in the UI, representing the actual Python choices, and should be unique. @@ -110,9 +112,10 @@ def _make_options(x): The input can be * an iterable of (label, value) pairs * an iterable of values, and labels will be generated + * a Mapping between labels and values """ if isinstance(x, Mapping): - 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.") + x = x.items() # only iterate once through the options. xlist = tuple(x) @@ -151,7 +154,7 @@ class _Selection(DescriptionWidget, ValueWidget, CoreWidget): index = Int(None, help="Selected index", allow_none=True).tag(sync=True) options = Any((), - help="""Iterable of values or (label, value) pairs that the user can select. + help="""Iterable of values, (label, value) pairs, or Mapping between labels and values that the user can select. The labels are the strings that will be displayed in the UI, representing the actual Python choices, and should be unique. @@ -298,7 +301,7 @@ class _MultipleSelection(DescriptionWidget, ValueWidget, CoreWidget): index = TypedTuple(trait=Int(), help="Selected indices").tag(sync=True) options = Any((), - help="""Iterable of values or (label, value) pairs that the user can select. + help="""Iterable of values, (label, value) pairs, or Mapping between labels and values that the user can select. The labels are the strings that will be displayed in the UI, representing the actual Python choices, and should be unique.