Skip to content

Commit 2a56742

Browse files
Align dpd callback signature to Dash one (no need to group Outputs, Inputs and States into list). (#322)
Co-authored-by: GFJ138 <sebastien.dementen@engie.com>
1 parent b9b8712 commit 2a56742

File tree

3 files changed

+79
-13
lines changed

3 files changed

+79
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
All notable changes should be documented in this file.
44

5+
## [dev] - TBD
6+
7+
Align dpd callback signature to Dash one (no need to group Outputs, Inputs and States into list).
8+
59
## [1.6.1] - 2021-02-06
610

711
Added a stub for `use_dash_dispatch` for backwards compatibility.

django_plotly_dash/dash_wrapper.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import warnings
3131

3232
import dash
33-
from dash import Dash
33+
from dash import Dash, dependencies
3434
from dash._utils import split_callback_id, inputs_to_dict
3535

3636
from flask import Flask
@@ -320,7 +320,7 @@ def get_expanded_arguments(func, inputs, state):
320320

321321
return expanded
322322

323-
def callback(self, output, inputs=None, state=None, events=None):
323+
def callback(self, *_args, **_kwargs):
324324
'''Form a callback function by wrapping, in the same way as the underlying Dash application would
325325
but handling extra arguments provided by dpd.
326326
@@ -331,10 +331,15 @@ def callback(self, output, inputs=None, state=None, events=None):
331331
Otherwise, take all arguments beyond the one provided by Dash (based on the Inputs/States provided).
332332
333333
'''
334+
335+
output, inputs, state, prevent_initial_call = dependencies.handle_callback_args(
336+
_args, _kwargs
337+
)
338+
334339
callback_set = {'output': output,
335-
'inputs': inputs or [],
336-
'state': state or [],
337-
'events': events or []}
340+
'inputs': inputs,
341+
'state': state,
342+
'prevent_initial_call': prevent_initial_call}
338343

339344
def wrap_func(func):
340345
self._callback_sets.append((callback_set, func))
@@ -348,12 +353,17 @@ def wrap_func(func):
348353

349354
expanded_callback = callback
350355

351-
def clientside_callback(self, clientside_function, output, inputs=None, state=None):
356+
def clientside_callback(self, clientside_function, *_args, **_kwargs):
352357
'Form a callback function by wrapping, in the same way as the underlying Dash application would'
358+
output, inputs, state, prevent_initial_call = dependencies.handle_callback_args(
359+
_args, _kwargs
360+
)
361+
353362
callback_set = { 'clientside_function': clientside_function,
354363
'output': output,
355-
'inputs': inputs and inputs or dict(),
356-
'state': state and state or dict() }
364+
'inputs': inputs,
365+
'state': state,
366+
'prevent_initial_call': prevent_initial_call}
357367

358368
self._clientside_callback_sets.append(callback_set)
359369

@@ -589,7 +599,7 @@ def _fix_callback_item(self, item):
589599
item.component_id = self._fix_id(item.component_id)
590600
return item
591601

592-
def callback(self, output, inputs=[], state=[], events=[]): # pylint: disable=dangerous-default-value
602+
def callback(self, output, inputs, state, prevent_initial_call):
593603
'Invoke callback, adjusting variable names as needed'
594604

595605
if isinstance(output, (list, tuple)):
@@ -599,9 +609,10 @@ def callback(self, output, inputs=[], state=[], events=[]): # pylint: disable=da
599609

600610
return super().callback(fixed_outputs,
601611
[self._fix_callback_item(x) for x in inputs],
602-
[self._fix_callback_item(x) for x in state])
612+
[self._fix_callback_item(x) for x in state],
613+
prevent_initial_call=prevent_initial_call)
603614

604-
def clientside_callback(self, clientside_function, output, inputs=[], state=[]): # pylint: disable=dangerous-default-value
615+
def clientside_callback(self, clientside_function, output, inputs, state, prevent_initial_call): # pylint: disable=dangerous-default-value
605616
'Invoke callback, adjusting variable names as needed'
606617

607618
if isinstance(output, (list, tuple)):
@@ -612,7 +623,8 @@ def clientside_callback(self, clientside_function, output, inputs=[], state=[]):
612623
return super().clientside_callback(clientside_function,
613624
fixed_outputs,
614625
[self._fix_callback_item(x) for x in inputs],
615-
[self._fix_callback_item(x) for x in state])
626+
[self._fix_callback_item(x) for x in state],
627+
prevent_initial_call=prevent_initial_call)
616628

617629
#pylint: disable=too-many-locals
618630
def dispatch_with_args(self, body, argMap):

django_plotly_dash/tests.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import pytest
3434
# pylint: disable=bare-except
35-
from dash.dependencies import Input
35+
from dash.dependencies import Input, State, Output
3636
from django.urls import reverse
3737

3838
from django_plotly_dash import DjangoDash
@@ -202,6 +202,56 @@ def test_dash_stateful_app_client_contract(client):
202202
assert DashApp.objects.get(instance_name="Some name").current_state() == final_state
203203

204204

205+
206+
def test_dash_callback_arguments():
207+
'Test the flexibility of the callback arguments order (handling of inputs/outputs/states)'
208+
209+
# create a DjangoDash
210+
ddash = DjangoDash(name="DashCallbackArguments")
211+
212+
# add a callback with the new flexible order of dependencies
213+
@ddash.callback(
214+
Output("one", "foo"),
215+
Output("two", "foo"),
216+
Input("one", "baz"),
217+
Input("two", "baz"),
218+
Input("three", "baz"),
219+
State("one", "bil"),
220+
)
221+
def new():
222+
pass
223+
224+
# add a callback with the old/classical flexible order of dependencies
225+
@ddash.callback(
226+
[Output("one", "foo"),
227+
Output("two", "foo")],
228+
[Input("one", "baz"),
229+
Input("two", "baz"),
230+
Input("three", "baz")],
231+
[State("one", "bil")]
232+
)
233+
def old():
234+
pass
235+
236+
assert ddash._callback_sets == [({'inputs': [Input("one", "baz"),
237+
Input("two", "baz"),
238+
Input("three", "baz"), ],
239+
'output': [Output("one", "foo"),
240+
Output("two", "foo")],
241+
'prevent_initial_call': None,
242+
'state': [State("one", "bil"), ]},
243+
new),
244+
({'inputs': [Input("one", "baz"),
245+
Input("two", "baz"),
246+
Input("three", "baz"), ],
247+
'output': [Output("one", "foo"),
248+
Output("two", "foo")],
249+
'prevent_initial_call': None,
250+
'state': [State("one", "bil"), ]},
251+
old)
252+
]
253+
254+
205255
def test_util_error_cases(settings):
206256
'Test handling of missing settings'
207257

0 commit comments

Comments
 (0)