From 8d38b017f39435314b2e00e355b859bd7ab41f26 Mon Sep 17 00:00:00 2001 From: Emil Haldrup Eriksen Date: Mon, 15 Jul 2024 16:01:04 +0200 Subject: [PATCH] Preparing 1.0.18 release --- .gitignore | 2 ++ CHANGELOG.md | 6 ++++++ dash_extensions/enrich.py | 15 ++++++++++++++- package-lock.json | 6 +++--- package.json | 4 ++-- pyproject.toml | 2 +- tests/test_enrich.py | 39 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 67 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 5712abb..70baa11 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,5 @@ NAMESPACE DESCRIPTION jsconfig.json tmp**.py + +nodesource_setup.sh \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2a662..ea07ee8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## [1.0.18] - 15-07-24 + +### Changed + +- Fix bug in `BlockingCallbackTransform` which occurred when a multi-output was targeted (i.e. using the `ALL` wildcard) + ## [1.0.17] - 28-06-24 ### Added diff --git a/dash_extensions/enrich.py b/dash_extensions/enrich.py index 4f4d4cf..f939bd0 100644 --- a/dash_extensions/enrich.py +++ b/dash_extensions/enrich.py @@ -685,7 +685,7 @@ def decorated_function(*args, **kwargs): outputs = f(*args, **kwargs) except Exception: logging.exception(f"Exception raised in blocking callback [{f.__name__}]") - outputs = no_update if single_output else [no_update] * num_outputs + outputs = _determine_outputs(single_output) return _append_output(outputs, datetime.utcnow().timestamp(), single_output, out_flex_key) @@ -694,6 +694,19 @@ def decorated_function(*args, **kwargs): return wrapper +def _determine_outputs(single_output: bool): + output_spec = dash.callback_context.outputs_list[:-1] + if single_output: + return [no_update] * len(output_spec[0]) if isinstance(output_spec[0], list) else no_update + outputs = [] + for entry in output_spec: + if isinstance(entry, list): + outputs.append([dash.no_update] * len(entry)) + else: + outputs.append(dash.no_update) + return outputs + + # endregion # region Log transform diff --git a/package-lock.json b/package-lock.json index 075eb9f..2a16dbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dash-extensions", - "version": "1.0.17", + "version": "1.0.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dash-extensions", - "version": "1.0.17", + "version": "1.0.18", "license": "MIT", "dependencies": { "@img-comparison-slider/react": "^7.7.0", @@ -11860,4 +11860,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 91b4ef9..0656975 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dash-extensions", - "version": "1.0.17", + "version": "1.0.18", "description": "Extensions for Plotly Dash.", "main": "build/index.js", "scripts": { @@ -62,4 +62,4 @@ "node": ">=8.11.0", "npm": ">=6.1.0" } -} \ No newline at end of file +} diff --git a/pyproject.toml b/pyproject.toml index 3854dda..42ad883 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dash-extensions" -version = "1.0.17" +version = "1.0.18" description = "Extensions for Plotly Dash." authors = ["emher "] license = "MIT" diff --git a/tests/test_enrich.py b/tests/test_enrich.py index 61db81e..379d7f6 100644 --- a/tests/test_enrich.py +++ b/tests/test_enrich.py @@ -565,6 +565,45 @@ def update(tick): assert dash_duo.find_element("#log").text == msg +@pytest.mark.parametrize( + "args, kwargs, port", + [ + ([Output(dict(type="log", index=ALL), "children"), Input("trigger", "n_intervals")], dict(), 4757), + ( + [], + dict( + output=[Output(dict(type="log", index=ALL), "children")], + inputs=dict(tick=Input("trigger", "n_intervals")), + ), + 4758, + ), + ], +) +def test_blocking_callback_wildcard(dash_duo, args, kwargs, port): + app = DashProxy(transforms=[BlockingCallbackTransform(timeout=5)]) + app.layout = html.Div( + [html.Div(id=dict(type="log", index=i)) for i in range(5)] + [dcc.Interval(id="trigger", interval=500)] + ) + msg = "Hello world!" + + @app.callback(*args, **kwargs, blocking=True) + def update(tick): + print(tick) + if tick is None: + raise PreventUpdate + time.sleep(1) + return [msg] * 5 + + # Check that stuff works. It doesn't using a normal Dash object. + dash_duo.start_server(app, port=port) + selector = _css_selector(dict(type="log", index=0)) + dash_duo.wait_for_text_to_equal(selector, msg, timeout=5) + assert dash_duo.find_element(selector).text == msg + # Check that we didn't get any errors. + logs = [entry for entry in dash_duo.driver.get_log("browser") if entry["timestamp"] > dash_duo._last_ts] + assert len([l for l in logs if "INTERNAL SERVER ERROR" in l["message"]]) == 0 + + def test_blocking_callback_transform_final_invocation(dash_duo): app = DashProxy(transforms=[BlockingCallbackTransform(timeout=5)]) app.layout = html.Div([html.Div(id="log"), dcc.Input(id="input")])