Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop support for Mapping types as Selection options #2679

Merged
merged 6 commits into from
Feb 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions docs/source/examples/Widget List.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"There are several widgets that can be used to display single selection lists, and two that can be used to select multiple values. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list** (options are either (label, value) pairs, or simply values for which the labels are derived by calling `str`)."
"There are several widgets that can be used to display single selection lists, and two that can be used to select multiple values. All inherit from the same base class. You can specify the **enumeration of selectable options by passing a list** (options are either (label, value) pairs, or simply values for which the labels are derived by calling `str`).\n",
"\n",
"<div class=\"alert alert-info\">\n",
"Changes in *ipywidgets 8*:\n",
" \n",
"Selection widgets no longer accept a dictionary of options. Pass a list of key-value pairs instead.\n",
"</div>"
]
},
{
Expand Down Expand Up @@ -639,11 +645,11 @@
"outputs": [],
"source": [
"import datetime\n",
"dates = [datetime.date(2015,i,1) for i in range(1,13)]\n",
"dates = [datetime.date(2015, i, 1) for i in range(1, 13)]\n",
"options = [(i.strftime('%b'), i) for i in dates]\n",
"widgets.SelectionRangeSlider(\n",
" options=options,\n",
" index=(0,11),\n",
" index=(0, 11),\n",
" description='Months (2015)',\n",
" disabled=False\n",
")"
Expand Down
32 changes: 8 additions & 24 deletions ipywidgets/widgets/tests/test_interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,34 +187,17 @@ def test_dict():
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)
with pytest.raises(TypeError):
c = interactive(f, d=d)


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)
with pytest.raises(TypeError):
c = interactive(f, lis=values)

def test_iterable():
def yield_values():
Expand Down Expand Up @@ -583,13 +566,14 @@ def test_multiple_selection():
check_widget(w, value=(1, 2))

# dict style
w.options = {1: 1}
check_widget(w, options={1:1})
with pytest.raises(TypeError):
w = smw(options={1: 1})

# updating
w.options = (1,)
with pytest.raises(TraitError):
w.value = (2,)
check_widget(w, options={1:1})
check_widget(w, options=(1,))


def test_interact_noinspect():
Expand Down
37 changes: 24 additions & 13 deletions ipywidgets/widgets/tests/test_widget_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
# Distributed under the terms of the Modified BSD License.

import inspect
import warnings
from unittest import TestCase

import pytest

from traitlets import TraitError

from ipywidgets import Dropdown, SelectionSlider, Select
Expand All @@ -15,19 +16,29 @@ 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()

def test_raise_mapping_options(self):
with pytest.raises(TypeError):
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)

def test_setting_options_from_list(self):
d = Dropdown()
assert d.options == ()
d.options = ['One', 'Two', 'Three']
assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')}

def test_setting_options_from_list_tuples(self):
d = Dropdown()
assert d.options == ()
d.options = [('One', 1), ('Two', 2), ('Three', 3)]
assert d.get_state('_options_labels') == {'_options_labels': ('One', 'Two', 'Three')}
d.value = 2
assert d.get_state('index') == {'index': 1}

def test_setting_options_from_dict(self):
d = Dropdown()
assert d.options == ()
with pytest.raises(TypeError):
d.options = {'One': 1}


class TestSelectionSlider(TestCase):
Expand Down
28 changes: 11 additions & 17 deletions ipywidgets/widgets/widget_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,11 @@
_doc_snippets['multiple_selection_params'] = """
options: dict or list
The options for the dropdown. This can either be a list of values, e.g.
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, a list of
``['Galileo', 'Brahe', 'Hubble']`` or ``[0, 1, 2]``, or a list of
(label, value) pairs, e.g.
``[('Galileo', 0), ('Brahe', 1), ('Hubble', 2)]``,
or a dictionary mapping the labels to the 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. If this is a dictionary, the order in which they are
displayed is not guaranteed.
``[('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.

index: iterable of int
The indices of the options that are selected.
Expand Down Expand Up @@ -106,11 +103,8 @@ def _make_options(x):
* an iterable of (label, value) pairs
* an iterable of values, and labels will be generated
"""
# Check if x is a mapping of labels to values
if isinstance(x, Mapping):
import warnings
warnings.warn("Support for mapping types has been deprecated and will be dropped in a future release.", DeprecationWarning)
return tuple((str(k), v) for k, v in x.items())
raise TypeError("options must be a list of values or a list of (label, value) tuples")

# only iterate once through the options.
xlist = tuple(x)
Expand All @@ -132,10 +126,10 @@ def findvalue(array, value, compare = lambda x, y: x == y):
class _Selection(DescriptionWidget, ValueWidget, CoreWidget):
"""Base class for Selection widgets

``options`` can be specified as a list of values, list of (label, value)
tuples, or a dict of {label: value}. The labels are the strings that will be
displayed in the UI, representing the actual Python choices, and should be
unique. If labels are not specified, they are generated from the values.
``options`` can be specified as a list of values or a list of (label, value)
tuples. The labels are the strings that will be displayed in the UI,
representing the actual Python choices, and should be unique.
If labels are not specified, they are generated from the values.
jtpio marked this conversation as resolved.
Show resolved Hide resolved

When programmatically setting the value, a reverse lookup is performed
among the options to check that the value is valid. The reverse lookup uses
Expand All @@ -149,7 +143,7 @@ class _Selection(DescriptionWidget, ValueWidget, CoreWidget):
index = Int(None, help="Selected index", allow_none=True).tag(sync=True)

options = Any((),
help="""Iterable of values, (label, value) pairs, or a mapping of {label: value} pairs that the user can select.
help="""Iterable of values or (label, value) pairs 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.
Expand Down Expand Up @@ -291,7 +285,7 @@ class _MultipleSelection(DescriptionWidget, ValueWidget, CoreWidget):
index = TypedTuple(trait=Int(), help="Selected indices").tag(sync=True)

options = Any((),
help="""Iterable of values, (label, value) pairs, or a mapping of {label: value} pairs that the user can select.
help="""Iterable of values or (label, value) pairs 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.
Expand Down