Skip to content

Commit

Permalink
Merge pull request #2679 from jtpio/remove-mutable-dropdown
Browse files Browse the repository at this point in the history
Drop support for Mapping types as Selection options
  • Loading branch information
pbugnion authored Feb 3, 2020
2 parents 78cdfcb + 5a73074 commit 7258f03
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 57 deletions.
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.
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

0 comments on commit 7258f03

Please sign in to comment.