From 27e9233f45dff0854c6f096a5d85d25c3f4bd0c2 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 19 Aug 2020 20:53:14 -0400 Subject: [PATCH 1/2] support named callback args not wrapped in a list --- dash/dependencies.py | 11 ++++- .../integration/callbacks/test_validation.py | 49 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/dash/dependencies.py b/dash/dependencies.py index e4ec42f86b..d891611ae9 100644 --- a/dash/dependencies.py +++ b/dash/dependencies.py @@ -140,7 +140,13 @@ def __repr__(self): def extract_callback_args(args, kwargs, name, type_): """Extract arguments for callback from a name and type""" parameters = kwargs.get(name, []) - if not parameters: + if parameters: + if not isinstance(parameters, (list, tuple)): + # accept a single item, not wrapped in a list, for any of the + # categories as a named arg (even though previously only output + # could be given unwrapped) + return [parameters] + else: while args and isinstance(args[0], type_): parameters.append(args.pop(0)) return parameters @@ -163,6 +169,9 @@ def handle_callback_args(args, kwargs): if len(outputs) == 1: out0 = kwargs.get("output", args[0] if args else None) if not isinstance(out0, (list, tuple)): + # unless it was explicitly provided as a list, a single output + # sholuld be unwrapped. That ensures the return value of the + # callback is also not expected to be wrapped in a list. outputs = outputs[0] inputs = extract_callback_args(flat_args, kwargs, "inputs", Input) diff --git a/tests/integration/callbacks/test_validation.py b/tests/integration/callbacks/test_validation.py index 249eb6172c..61acaae0f9 100644 --- a/tests/integration/callbacks/test_validation.py +++ b/tests/integration/callbacks/test_validation.py @@ -144,3 +144,52 @@ def o2(i): dash_duo.start_server(app) dash_duo.wait_for_text_to_equal("#out1", "1: Hi") dash_duo.wait_for_text_to_equal("#out2", "2: Hi") + + +@pytest.mark.parametrize("named_out", [True, False]) +@pytest.mark.parametrize("named_in", [True, False]) +@pytest.mark.parametrize("named_state", [True, False]) +def test_cbva004_named_args(named_out, named_in, named_state, dash_duo): + app = Dash(__name__) + app.layout = html.Div( + [ + html.Div("Hi", id="in"), + html.Div("gh", id="state"), + html.Div(id="out1"), + html.Div(id="out2"), + ] + ) + + def make_args(*a): + args = [] + kwargs = {} + names = ["output", "inputs", "state"] + flags = [named_out, named_in, named_state] + for ai, name, flag in zip(a, names, flags): + if flag: + kwargs[name] = ai + else: + args.append(ai) + return args, kwargs + + args, kwargs = make_args( + Output("out1", "children"), Input("in", "children"), State("state", "children") + ) + + @app.callback(*args, **kwargs) + def o1(i, s): + return "1: " + i + s + + args, kwargs = make_args( + [Output("out2", "children")], + [Input("in", "children")], + [State("state", "children")], + ) + + @app.callback(*args, **kwargs) + def o2(i, s): + return ("2: " + i + s,) + + dash_duo.start_server(app) + dash_duo.wait_for_text_to_equal("#out1", "1: High") + dash_duo.wait_for_text_to_equal("#out2", "2: High") From 5d44729015312ce5eed95f109b22896837724f6b Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 19 Aug 2020 21:11:38 -0400 Subject: [PATCH 2/2] changelog for unlisted named callback args fix --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1fabadc37..1e5ffe693d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,13 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). ## [UNRELEASED] -### Changed -- [#1368](https://github.com/plotly/dash/pull/1368) Updated pytest to v6.0.1. To avoid deprecation warnings, this also updated pytest-sugar to 0.9.4 and pytest-mock to 3.2.0. The pytest-mock update only effects python >= 3.0. Pytest-mock remains pinned at 2.0.0 for python == 2.7. - ### Added - [#1355](https://github.com/plotly/dash/pull/1355) Removed redundant log message and consolidated logger initialization. You can now control the log level - for example suppress informational messages from Dash with `app.logger.setLevel(logging.WARNING)`. ### Changed -- [#1180](https://github.com/plotly/dash/pull/1180) `Input`, `Output`, and `State` in callback definitions don't need to be in lists. You still need to provide `Output` items first, then `Input` items, then `State`, and the list form is still supported. In particular, if you want to return a single output item wrapped in a length-1 list, you should still wrap the `Output` in a list. This can be useful for procedurally-generated callbacks. +- [#1180](https://github.com/plotly/dash/pull/1180) and [#1375](https://github.com/plotly/dash/pull/1375) `Input`, `Output`, and `State` in callback definitions don't need to be in lists. You still need to provide `Output` items first, then `Input` items, then `State`, and the list form is still supported. In particular, if you want to return a single output item wrapped in a length-1 list, you should still wrap the `Output` in a list. This can be useful for procedurally-generated callbacks. +- [#1368](https://github.com/plotly/dash/pull/1368) Updated pytest to v6.0.1. To avoid deprecation warnings, this also updated pytest-sugar to 0.9.4 and pytest-mock to 3.2.0. The pytest-mock update only effects python >= 3.0. Pytest-mock remains pinned at 2.0.0 for python == 2.7. + ## [1.14.0] - 2020-07-27 ### Added