Skip to content

Commit ba22c4d

Browse files
authored
Merge pull request #2770 from plotly/feat/running-cb
Add running to regular callbacks.
2 parents 44cbee3 + bf6a544 commit ba22c4d

File tree

8 files changed

+62
-20
lines changed

8 files changed

+62
-20
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ max-bool-expr=5
414414
max-branches=15
415415

416416
# Maximum number of locals for function / method body
417-
max-locals=18
417+
max-locals=20
418418

419419
# Maximum number of parents for a class (see R0901).
420420
max-parents=7

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1818
- [#2758](https://github.com/plotly/dash/pull/2758)
1919
- exposing `setProps` to `dash_clientside.clientSide_setProps` to allow for JS code to interact directly with the dash eco-system
2020
- [#2730](https://github.com/plotly/dash/pull/2721) Load script files with `.mjs` ending as js modules
21+
- [#2770](https://github.com/plotly/dash/pull/2770) Add running to regular callbacks.
2122

2223
## [2.15.0] - 2024-01-31
2324

dash/_callback.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,6 @@ def callback(
166166
"Progress and progress default needs to be of same length"
167167
)
168168

169-
if running:
170-
long_spec["running"] = coerce_to_list(running)
171-
validate_long_inputs(x[0] for x in long_spec["running"])
172-
173169
if cancel:
174170
cancel_inputs = coerce_to_list(cancel)
175171
validate_long_inputs(cancel_inputs)
@@ -188,6 +184,7 @@ def callback(
188184
**_kwargs,
189185
long=long_spec,
190186
manager=manager,
187+
running=running,
191188
)
192189

193190

@@ -227,6 +224,7 @@ def insert_callback(
227224
prevent_initial_call,
228225
long=None,
229226
manager=None,
227+
running=None,
230228
dynamic_creator=False,
231229
):
232230
if prevent_initial_call is None:
@@ -251,6 +249,8 @@ def insert_callback(
251249
},
252250
"dynamic_creator": dynamic_creator,
253251
}
252+
if running:
253+
callback_spec["running"] = running
254254

255255
callback_map[callback_id] = {
256256
"inputs": callback_spec["inputs"],
@@ -289,6 +289,14 @@ def register_callback( # pylint: disable=R0914
289289

290290
long = _kwargs.get("long")
291291
manager = _kwargs.get("manager")
292+
running = _kwargs.get("running")
293+
if running is not None:
294+
if not isinstance(running[0], (list, tuple)):
295+
running = [running]
296+
running = {
297+
"running": {str(r[0]): r[1] for r in running},
298+
"runningOff": {str(r[0]): r[2] for r in running},
299+
}
292300
allow_dynamic_callbacks = _kwargs.get("_allow_dynamic_callbacks")
293301

294302
output_indices = make_grouping_by_index(output, list(range(grouping_len(output))))
@@ -305,6 +313,7 @@ def register_callback( # pylint: disable=R0914
305313
long=long,
306314
manager=manager,
307315
dynamic_creator=allow_dynamic_callbacks,
316+
running=running,
308317
)
309318

310319
# pylint: disable=too-many-locals
@@ -389,11 +398,6 @@ def add_context(*args, **kwargs):
389398
"job": job,
390399
}
391400

392-
running = long.get("running")
393-
394-
if running:
395-
data["running"] = {str(r[0]): r[1] for r in running}
396-
data["runningOff"] = {str(r[0]): r[2] for r in running}
397401
cancel = long.get("cancel")
398402
if cancel:
399403
data["cancel"] = cancel

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,8 @@ function handleServerside(
349349
long: LongCallbackInfo | undefined,
350350
additionalArgs: [string, string, boolean?][] | undefined,
351351
getState: any,
352-
output: string
352+
output: string,
353+
running: any
353354
): Promise<CallbackResponse> {
354355
if (hooks.request_pre) {
355356
hooks.request_pre(payload);
@@ -363,6 +364,11 @@ function handleServerside(
363364
let progressDefault: any;
364365
let moreArgs = additionalArgs;
365366

367+
if (running) {
368+
sideUpdate(running.running, dispatch, paths);
369+
runningOff = running.runningOff;
370+
}
371+
366372
const fetchCallback = () => {
367373
const headers = getCSRFHeader() as any;
368374
let url = `${urlBase(config)}_dash-update-component`;
@@ -497,12 +503,6 @@ function handleServerside(
497503
if (data.progress) {
498504
sideUpdate(data.progress, dispatch, paths);
499505
}
500-
if (data.running) {
501-
sideUpdate(data.running, dispatch, paths);
502-
}
503-
if (!runningOff && data.runningOff) {
504-
runningOff = data.runningOff;
505-
}
506506
if (!progressDefault && data.progressDefault) {
507507
progressDefault = data.progressDefault;
508508
}
@@ -717,7 +717,8 @@ export function executeCallback(
717717
long,
718718
additionalArgs.length ? additionalArgs : undefined,
719719
getState,
720-
cb.callback.output
720+
cb.callback.output,
721+
cb.callback.running
721722
);
722723

723724
if (newHeaders) {

dash/dash-renderer/src/types/callbacks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface ICallbackDefinition {
1313
state: ICallbackProperty[];
1414
long?: LongCallbackInfo;
1515
dynamic_creator?: boolean;
16+
running: any;
1617
}
1718

1819
export interface ICallbackProperty {

dash/dash.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,10 @@ def interpolate_index(self, **kwargs):
11131113
)
11141114

11151115
def dependencies(self):
1116-
return flask.jsonify(self._callback_list)
1116+
return flask.Response(
1117+
to_json(self._callback_list),
1118+
content_type="application/json",
1119+
)
11171120

11181121
def clientside_callback(self, clientside_function, *args, **kwargs):
11191122
"""Create a callback that updates the output by calling a clientside

tests/integration/callbacks/test_basic_callback.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,3 +791,35 @@ def on_click(_):
791791
dash_duo.start_server(app)
792792

793793
assert dash_duo.get_logs() == []
794+
795+
796+
def test_cbsc019_callback_running(dash_duo):
797+
lock = Lock()
798+
app = Dash(__name__)
799+
800+
app.layout = html.Div(
801+
[
802+
html.Div("off", id="running"),
803+
html.Button("start", id="start"),
804+
html.Div(id="output"),
805+
]
806+
)
807+
808+
@app.callback(
809+
Output("output", "children"),
810+
Input("start", "n_clicks"),
811+
running=[[Output("running", "children"), html.B("on", id="content"), "off"]],
812+
prevent_initial_call=True,
813+
)
814+
def on_click(_):
815+
with lock:
816+
pass
817+
return "done"
818+
819+
dash_duo.start_server(app)
820+
with lock:
821+
dash_duo.find_element("#start").click()
822+
dash_duo.wait_for_text_to_equal("#content", "on")
823+
824+
dash_duo.wait_for_text_to_equal("#output", "done")
825+
dash_duo.wait_for_text_to_equal("#running", "off")

tests/integration/long_callback/test_basic_long_callback016.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
@pytest.mark.skipif(
10-
sys.version_info < (3, 7), reason="Python 3.6 long callbacks tests hangs up"
10+
sys.version_info < (3, 9), reason="Python 3.8 long callbacks tests hangs up"
1111
)
1212
def test_lcbc016_multi_page_cancel(dash_duo, manager):
1313
with setup_long_callback_app(manager, "app_page_cancel") as app:

0 commit comments

Comments
 (0)