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

Children array error #1026

Merged
merged 6 commits into from
Nov 27, 2019
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
clientside JavaScript callbacks via inline strings.
- [#1020](https://github.com/plotly/dash/pull/1020) Allow `visit_and_snapshot` API in `dash.testing.browser` to stay on the page so you can run other checks.

### Changed
- [#1026](https://github.com/plotly/dash/pull/1026) Better error message when you forget to wrap multiple `children` in an array, and they get passed to other props.

### Fixed
- [#1018](https://github.com/plotly/dash/pull/1006) Fix the `dash.testing` **stop** API with process application runner in Python2. Use `kill()` instead of `communicate()` to avoid hanging.
- [#1027](https://github.com/plotly/dash/pull/1027) Fix bug with renderer callback lock never resolving with non-rendered async component using the asyncDecorator
Expand Down
8 changes: 8 additions & 0 deletions dash/development/base_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ def __init__(self, **kwargs):
", ".join(sorted(self._prop_names))
)
)

if k != "children" and isinstance(v, Component):
raise TypeError(
"Component detected as a prop other than `children`\n" +
"Did you forget to wrap multiple `children` in an array?\n" +
"Prop {} has value {}\n".format(k, repr(v))
)

setattr(self, k, v)

def to_plotly_json(self):
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/devtools/test_devtools_error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ def update_output(n_clicks):
dev_tools_hot_reload=False,
)

dash_duo.wait_for_element('.js-plotly-plot .main-svg')

dash_duo.find_element("#button").click()
dash_duo.wait_for_text_to_equal(dash_duo.devtools_error_count_locator, "1")
dash_duo.percy_snapshot("devtools - validation exception - closed")
Expand Down
67 changes: 42 additions & 25 deletions tests/unit/development/test_base_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from dash.development.base_component import Component
import dash_html_components as html

Component._prop_names = ("id", "a", "children", "style")
Component._type = "TestComponent"
Expand Down Expand Up @@ -36,23 +37,23 @@ def nested_tree():
return c, c1, c2, c3, c4, c5


def test_init():
def test_debc001_init():
Component(a=3)


def test_get_item_with_children():
def test_debc002_get_item_with_children():
c1 = Component(id="1")
c2 = Component(children=[c1])
assert c2["1"] == c1


def test_get_item_with_children_as_component_instead_of_list():
def test_debc003_get_item_with_children_as_component_instead_of_list():
c1 = Component(id="1")
c2 = Component(id="2", children=c1)
assert c2["1"] == c1


def test_get_item_with_nested_children_one_branch():
def test_debc004_get_item_with_nested_children_one_branch():
c1 = Component(id="1")
c2 = Component(id="2", children=[c1])
c3 = Component(children=[c2])
Expand All @@ -61,7 +62,7 @@ def test_get_item_with_nested_children_one_branch():
assert c3["1"] == c1


def test_get_item_with_nested_children_two_branches():
def test_debc005_get_item_with_nested_children_two_branches():
c1 = Component(id="1")
c2 = Component(id="2", children=[c1])
c3 = Component(id="3")
Expand All @@ -75,7 +76,7 @@ def test_get_item_with_nested_children_two_branches():
assert c5["3"] == c3


def test_get_item_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc006_get_item_with_full_tree():
c, c1, c2, c3, c4, c5 = nested_tree()
keys = [k for k in c]

Expand All @@ -90,15 +91,15 @@ def test_get_item_with_nested_children_with_mixed_strings_and_without_lists():
c["x"]


def test_len_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc007_len_with_full_tree():
c = nested_tree()[0]
assert (
len(c) == 5 + 5 + 1
), "the length of the nested children should match the total of 5 \
components, 2 strings + 2 numbers + none in c2, and 1 string in c1"


def test_set_item_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc008_set_item_anywhere_in_tree():
keys = ["0.0", "0.1", "0.1.x", "0.1.x.x", "0.1.x.x.0"]
c = nested_tree()[0]

Expand All @@ -110,7 +111,7 @@ def test_set_item_with_nested_children_with_mixed_strings_and_without_lists():
assert c[new_id] == new_component


def test_del_item_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc009_del_item_full_tree():
c = nested_tree()[0]
keys = reversed([k for k in c])
for key in keys:
Expand All @@ -120,21 +121,21 @@ def test_del_item_with_nested_children_with_mixed_strings_and_without_lists():
c[key]


def test_traverse_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc010_traverse_full_tree():
c, c1, c2, c3, c4, c5 = nested_tree()
elements = [i for i in c._traverse()]
assert elements == c.children + [c3] + [c2] + c2.children


def test_traverse_with_tuples():
def test_debc011_traverse_with_tuples():
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()]
assert elements == list(c.children) + [c3] + [c2] + list(c2.children)


def test_to_plotly_json_with_nested_children_with_mixed_strings_and_without_lists():
def test_debc012_to_plotly_json_full_tree():
c = nested_tree()[0]
Component._namespace
Component._type
Expand Down Expand Up @@ -194,7 +195,7 @@ def test_to_plotly_json_with_nested_children_with_mixed_strings_and_without_list
assert res == expected


def test_get_item_raises_key_if_id_doesnt_exist():
def test_debc013_get_item_raises_key_if_id_doesnt_exist():
c = Component()
with pytest.raises(KeyError):
c["1"]
Expand All @@ -212,7 +213,7 @@ def test_get_item_raises_key_if_id_doesnt_exist():
c3["0"]


def test_set_item():
def test_debc014_set_item():
c1a = Component(id="1", children="Hello world")
c2 = Component(id="2", children=c1a)
assert c2["1"] == c1a
Expand All @@ -222,7 +223,7 @@ def test_set_item():
assert c2["1"] == c1b


def test_set_item_with_children_as_list():
def test_debc015_set_item_with_children_as_list():
c1 = Component(id="1")
c2 = Component(id="2", children=[c1])
assert c2["1"] == c1
Expand All @@ -231,7 +232,7 @@ def test_set_item_with_children_as_list():
assert c2["3"] == c3


def test_set_item_with_nested_children():
def test_debc016_set_item_with_nested_children():
c1 = Component(id="1")
c2 = Component(id="2", children=[c1])
c3 = Component(id="3")
Expand All @@ -256,14 +257,14 @@ def test_set_item_with_nested_children():
c5["1"]


def test_set_item_raises_key_error():
def test_debc017_set_item_raises_key_error():
c1 = Component(id="1")
c2 = Component(id="2", children=[c1])
with pytest.raises(KeyError):
c2["3"] = Component(id="3")


def test_del_item_from_list():
def test_debc018_del_item_from_list():
c1 = Component(id="1")
c2 = Component(id="2")
c3 = Component(id="3", children=[c1, c2])
Expand All @@ -280,7 +281,7 @@ def test_del_item_from_list():
assert c3.children == []


def test_del_item_from_class():
def test_debc019_del_item_from_class():
c1 = Component(id="1")
c2 = Component(id="2", children=c1)
assert c2["1"] == c1
Expand All @@ -291,7 +292,7 @@ def test_del_item_from_class():
assert c2.children is None


def test_to_plotly_json_without_children():
def test_debc020_to_plotly_json_without_children():
c = Component(id="a")
c._prop_names = ("id",)
c._type = "MyComponent"
Expand All @@ -303,7 +304,7 @@ def test_to_plotly_json_without_children():
}


def test_to_plotly_json_with_null_arguments():
def test_debc021_to_plotly_json_with_null_arguments():
c = Component(id="a")
c._prop_names = ("id", "style")
c._type = "MyComponent"
Expand All @@ -325,7 +326,7 @@ def test_to_plotly_json_with_null_arguments():
}


def test_to_plotly_json_with_children():
def test_debc022_to_plotly_json_with_children():
c = Component(id="a", children="Hello World")
c._prop_names = ("id", "children")
c._type = "MyComponent"
Expand All @@ -341,7 +342,7 @@ def test_to_plotly_json_with_children():
}


def test_to_plotly_json_with_wildcards():
def test_debc023_to_plotly_json_with_wildcards():
c = Component(
id="a", **{"aria-expanded": "true", "data-toggle": "toggled", "data-none": None}
)
Expand All @@ -360,15 +361,15 @@ def test_to_plotly_json_with_wildcards():
}


def test_len():
def test_debc024_len():
assert len(Component()) == 0
assert len(Component(children="Hello World")) == 1
assert len(Component(children=Component())) == 1
assert len(Component(children=[Component(), Component()])) == 2
assert len(Component(children=[Component(children=Component()), Component()])) == 3


def test_iter():
def test_debc025_iter():
# The mixin methods from MutableMapping were cute but probably never
# used - at least not by us. Test that they're gone

Expand Down Expand Up @@ -418,3 +419,19 @@ def test_iter():
assert k in keys, "iteration produces key " + k

assert len(keys) == len(keys2), "iteration produces no extra keys"


def test_debc026_component_not_children():
children = [Component(id='a'), html.Div(id='b'), 'c', 1]
for i in range(len(children)):
# cycle through each component in each position
children = children[1:] + [children[0]]

# use html.Div because only real components accept positional args
html.Div(children)
# the first arg is children, and a single component works there
html.Div(children[0], id='x')

with pytest.raises(TypeError):
# If you forget the `[]` around children you get this:
html.Div(children[0], children[1], children[2], children[3])