diff --git a/CHANGELOG.md b/CHANGELOG.md index bcee72d95f..344f5119b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.28.4 - 2018-10-18 +## Fixed +- The `Component.traverse()` and `Component.traverse_with_paths()` methods now work correctly for components with `children` of type `tuple` (before, this only worked for `list`s). [#430](https://github.com/plotly/dash/pull/430) + ## 0.28.3 - 2018-10-17 ## Fixed - Fix http-equiv typo [#418](https://github.com/plotly/dash/pull/418) diff --git a/dash/dash.py b/dash/dash.py index 7779371053..0a68d70bce 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -775,9 +775,10 @@ def _validate_value(val, index=None): ) # Children that are not of type Component or - # collections.MutableSequence not returned by traverse + # list/tuple not returned by traverse child = getattr(j, 'children', None) - if not isinstance(child, collections.MutableSequence): + if not isinstance(child, (tuple, + collections.MutableSequence)): if child and not _value_is_valid(child): _raise_invalid( bad_val=child, @@ -789,7 +790,7 @@ def _validate_value(val, index=None): # Also check the child of val, as it will not be returned child = getattr(val, 'children', None) - if not isinstance(child, collections.MutableSequence): + if not isinstance(child, (tuple, collections.MutableSequence)): if child and not _value_is_valid(child): _raise_invalid( bad_val=child, diff --git a/dash/development/base_component.py b/dash/development/base_component.py index 387a198ce0..7fa7aa97cd 100644 --- a/dash/development/base_component.py +++ b/dash/development/base_component.py @@ -16,7 +16,8 @@ def is_number(s): def _check_if_has_indexable_children(item): if (not hasattr(item, 'children') or (not isinstance(item.children, Component) and - not isinstance(item.children, collections.MutableSequence))): + not isinstance(item.children, (tuple, + collections.MutableSequence)))): raise KeyError @@ -145,7 +146,7 @@ def _get_set_or_delete(self, id, operation, new_item=None): pass # if children is like a list - if isinstance(self.children, collections.MutableSequence): + if isinstance(self.children, (tuple, collections.MutableSequence)): for i, item in enumerate(self.children): # If the item itself is the one we're looking for if getattr(item, 'id', None) == id: @@ -221,7 +222,7 @@ def traverse_with_paths(self): yield "\n".join(["[*] " + children_string, p]), t # children is a list of components - elif isinstance(children, collections.MutableSequence): + elif isinstance(children, (tuple, collections.MutableSequence)): for idx, i in enumerate(children): list_path = "[{:d}] {:s} {}".format( idx, @@ -254,7 +255,7 @@ def __len__(self): elif isinstance(self.children, Component): length = 1 length += len(self.children) - elif isinstance(self.children, collections.MutableSequence): + elif isinstance(self.children, (tuple, collections.MutableSequence)): for c in self.children: length += 1 if isinstance(c, Component): diff --git a/dash/version.py b/dash/version.py index a4214c68e5..dc00bd8b5a 100644 --- a/dash/version.py +++ b/dash/version.py @@ -1 +1 @@ -__version__ = '0.28.3' +__version__ = '0.28.4' diff --git a/tests/development/test_base_component.py b/tests/development/test_base_component.py index bb32cc7940..a43be1b898 100644 --- a/tests/development/test_base_component.py +++ b/tests/development/test_base_component.py @@ -156,6 +156,16 @@ def test_traverse_with_nested_children_with_mixed_strings_and_without_lists(self c.children + [c3] + [c2] + c2.children ) + def test_traverse_with_tuples(self): # noqa: E501 + c, c1, c2, c3, c4, c5 = nested_tree() + c2.children = tuple(c2.children) + c.children = tuple(c.children) + elements = [i for i in c.traverse()] + self.assertEqual( + elements, + list(c.children) + [c3] + [c2] + list(c2.children) + ) + def test_iter_with_nested_children_with_mixed_strings_and_without_lists(self): # noqa: E501 c = nested_tree()[0] keys = list(c.keys())