Skip to content

Commit 27e9233

Browse files
committed
support named callback args not wrapped in a list
1 parent 58eb07b commit 27e9233

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

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)