diff --git a/CHANGELOG.md b/CHANGELOG.md index 43200fd91a..5dc726bc16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed - [#1035](https://github.com/plotly/dash/pull/1035) Simplify our build process. +- [#1074](https://github.com/plotly/dash/pull/1045) Error messages when providing an incorrect property to a component have been improved: they now specify the component type, library, version, and ID (if available). ### Fixed - [#1037](https://github.com/plotly/dash/pull/1037) Fix no_update test to allow copies, such as those stored and retrieved from a cache. diff --git a/dash/development/base_component.py b/dash/development/base_component.py index 3318a76d86..6f0b2ee21d 100644 --- a/dash/development/base_component.py +++ b/dash/development/base_component.py @@ -86,18 +86,38 @@ def __init__(self, **kwargs): k_in_wildcards = any( [k.startswith(w) for w in self._valid_wildcard_attributes] ) + # e.g. "The dash_core_components.Dropdown component (version 1.6.0) + # with the ID "my-dropdown" + try: + error_string_prefix = 'The `{}.{}` component (version {}){}'.format( + self._namespace, + self._type, + getattr(__import__(self._namespace), '__version__', 'unknown'), + ' with the ID "{}"'.format(kwargs['id']) + if 'id' in kwargs else '' + ) + except ImportError: + # Our tests create mock components with libraries that + # aren't importable + error_string_prefix = 'The `{}` component{}'.format( + self._type, + ' with the ID "{}"'.format(kwargs['id']) + if 'id' in kwargs else '' + ) + if not k_in_propnames and not k_in_wildcards: raise TypeError( - "Unexpected keyword argument `{}`".format(k) - + "\nAllowed arguments: {}".format( - # pylint: disable=no-member + "{} received an unexpected keyword argument: `{}`".format( + error_string_prefix, k + ) + "\nAllowed arguments: {}".format( # pylint: disable=no-member ", ".join(sorted(self._prop_names)) ) ) if k != "children" and isinstance(v, Component): raise TypeError( - "Component detected as a prop other than `children`\n" + + error_string_prefix + + " detected a Component for 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)) ) diff --git a/tests/unit/development/test_base_component.py b/tests/unit/development/test_base_component.py index 6da20789e3..f585aa2e0f 100644 --- a/tests/unit/development/test_base_component.py +++ b/tests/unit/development/test_base_component.py @@ -435,3 +435,46 @@ def test_debc026_component_not_children(): with pytest.raises(TypeError): # If you forget the `[]` around children you get this: html.Div(children[0], children[1], children[2], children[3]) + + +def test_debc027_component_error_message(): + with pytest.raises(TypeError) as e: + Component(asdf=True) + assert str(e.value) == ( + "The `TestComponent` component received an unexpected " + + "keyword argument: `asdf`\nAllowed arguments: a, children, " + + "id, style" + ) + + with pytest.raises(TypeError) as e: + Component(asdf=True, id='my-component') + assert str(e.value) == ( + "The `TestComponent` component " + + "with the ID \"my-component\" received an unexpected " + + "keyword argument: `asdf`\nAllowed arguments: a, children, " + + "id, style" + ) + + with pytest.raises(TypeError) as e: + html.Div(asdf=True) + assert str(e.value) == ( + "The `dash_html_components.Div` component " + + "(version {}) ".format(html.__version__) + + "received an unexpected " + + "keyword argument: `asdf`\n" + + "Allowed arguments: {}".format( + ', '.join(sorted(html.Div()._prop_names)) + ) + ) + + with pytest.raises(TypeError) as e: + html.Div(asdf=True, id='my-component') + assert str(e.value) == ( + "The `dash_html_components.Div` component " + + "(version {}) ".format(html.__version__) + + "with the ID \"my-component\" received an unexpected " + + "keyword argument: `asdf`\n" + + "Allowed arguments: {}".format( + ', '.join(sorted(html.Div()._prop_names)) + ) + )