Skip to content

Commit c68bcc2

Browse files
authored
Merge pull request #1375 from plotly/1366-named-output-arg
support named callback args not wrapped in a list
2 parents 58eb07b + 5d44729 commit c68bcc2

File tree

3 files changed

+62
-5
lines changed

3 files changed

+62
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

55
## [UNRELEASED]
6-
### Changed
7-
- [#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.
8-
96
### Added
107
- [#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)`.
118

129
### Changed
13-
- [#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.
10+
- [#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.
11+
- [#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.
12+
1413

1514
## [1.14.0] - 2020-07-27
1615
### Added

dash/dependencies.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,13 @@ def __repr__(self):
140140
def extract_callback_args(args, kwargs, name, type_):
141141
"""Extract arguments for callback from a name and type"""
142142
parameters = kwargs.get(name, [])
143-
if not parameters:
143+
if parameters:
144+
if not isinstance(parameters, (list, tuple)):
145+
# accept a single item, not wrapped in a list, for any of the
146+
# categories as a named arg (even though previously only output
147+
# could be given unwrapped)
148+
return [parameters]
149+
else:
144150
while args and isinstance(args[0], type_):
145151
parameters.append(args.pop(0))
146152
return parameters
@@ -163,6 +169,9 @@ def handle_callback_args(args, kwargs):
163169
if len(outputs) == 1:
164170
out0 = kwargs.get("output", args[0] if args else None)
165171
if not isinstance(out0, (list, tuple)):
172+
# unless it was explicitly provided as a list, a single output
173+
# sholuld be unwrapped. That ensures the return value of the
174+
# callback is also not expected to be wrapped in a list.
166175
outputs = outputs[0]
167176

168177
inputs = extract_callback_args(flat_args, kwargs, "inputs", Input)

tests/integration/callbacks/test_validation.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,52 @@ def o2(i):
144144
dash_duo.start_server(app)
145145
dash_duo.wait_for_text_to_equal("#out1", "1: Hi")
146146
dash_duo.wait_for_text_to_equal("#out2", "2: Hi")
147+
148+
149+
@pytest.mark.parametrize("named_out", [True, False])
150+
@pytest.mark.parametrize("named_in", [True, False])
151+
@pytest.mark.parametrize("named_state", [True, False])
152+
def test_cbva004_named_args(named_out, named_in, named_state, dash_duo):
153+
app = Dash(__name__)
154+
app.layout = html.Div(
155+
[
156+
html.Div("Hi", id="in"),
157+
html.Div("gh", id="state"),
158+
html.Div(id="out1"),
159+
html.Div(id="out2"),
160+
]
161+
)
162+
163+
def make_args(*a):
164+
args = []
165+
kwargs = {}
166+
names = ["output", "inputs", "state"]
167+
flags = [named_out, named_in, named_state]
168+
for ai, name, flag in zip(a, names, flags):
169+
if flag:
170+
kwargs[name] = ai
171+
else:
172+
args.append(ai)
173+
return args, kwargs
174+
175+
args, kwargs = make_args(
176+
Output("out1", "children"), Input("in", "children"), State("state", "children")
177+
)
178+
179+
@app.callback(*args, **kwargs)
180+
def o1(i, s):
181+
return "1: " + i + s
182+
183+
args, kwargs = make_args(
184+
[Output("out2", "children")],
185+
[Input("in", "children")],
186+
[State("state", "children")],
187+
)
188+
189+
@app.callback(*args, **kwargs)
190+
def o2(i, s):
191+
return ("2: " + i + s,)
192+
193+
dash_duo.start_server(app)
194+
dash_duo.wait_for_text_to_equal("#out1", "1: High")
195+
dash_duo.wait_for_text_to_equal("#out2", "2: High")

0 commit comments

Comments
 (0)