From 98da5619213247b15fd0e7c77a4ff4faabc0e2b0 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 10:22:40 -0400 Subject: [PATCH 1/6] adjustments for computeGraphs to not have validation when in debug is False or `dev_tools_validate_callbacks` is False (cherry picked from commit 077a3e759afc7d1f893796163a0def78a74a5368) --- dash/dash-renderer/src/APIController.react.js | 6 +- .../dash-renderer/src/actions/dependencies.js | 51 ++++--- dash/dash-renderer/src/config.ts | 1 + dash/dash.py | 15 ++ .../integration/renderer/test_benchmarking.py | 135 ++++++++++++++++++ 5 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 tests/integration/renderer/test_benchmarking.py diff --git a/dash/dash-renderer/src/APIController.react.js b/dash/dash-renderer/src/APIController.react.js index d83f7b7415..f5a6c2381b 100644 --- a/dash/dash-renderer/src/APIController.react.js +++ b/dash/dash-renderer/src/APIController.react.js @@ -143,7 +143,8 @@ function storeEffect(props, events, setErrorLoading) { graphs, hooks, layout, - layoutRequest + layoutRequest, + config } = props; batch(() => { @@ -187,7 +188,8 @@ function storeEffect(props, events, setErrorLoading) { setGraphs( computeGraphs( dependenciesRequest.content, - dispatchError(dispatch) + dispatchError(dispatch), + config ) ) ); diff --git a/dash/dash-renderer/src/actions/dependencies.js b/dash/dash-renderer/src/actions/dependencies.js index 657647da7e..bbc17b534b 100644 --- a/dash/dash-renderer/src/actions/dependencies.js +++ b/dash/dash-renderer/src/actions/dependencies.js @@ -626,9 +626,10 @@ export function validateCallbacksToLayout(state_, dispatchError) { validatePatterns(inputPatterns, 'Input'); } -export function computeGraphs(dependencies, dispatchError) { +export function computeGraphs(dependencies, dispatchError, config) { // multiGraph is just for finding circular deps const multiGraph = new DepGraph(); + const start = performance.now(); const wildcardPlaceholders = {}; @@ -657,7 +658,9 @@ export function computeGraphs(dependencies, dispatchError) { hasError = true; dispatchError(message, lines); }; - validateDependencies(parsedDependencies, wrappedDE); + if (config.validate_callbacks) { + validateDependencies(parsedDependencies, wrappedDE); + } /* * For regular ids, outputMap and inputMap are: @@ -808,6 +811,7 @@ export function computeGraphs(dependencies, dispatchError) { const cbOut = []; function addInputToMulti(inIdProp, outIdProp, firstPass = true) { + if (!config.validate_callbacks) return multiGraph.addNode(inIdProp); multiGraph.addDependency(inIdProp, outIdProp); // only store callback inputs and outputs during the first pass @@ -825,6 +829,7 @@ export function computeGraphs(dependencies, dispatchError) { cbOut.push([]); function addOutputToMulti(outIdFinal, outIdProp) { + if (!config.validate_callbacks) return multiGraph.addNode(outIdProp); inputs.forEach(inObj => { const {id: inId, property} = inObj; @@ -859,28 +864,35 @@ export function computeGraphs(dependencies, dispatchError) { outputs.forEach(outIdProp => { const {id: outId, property} = outIdProp; // check if this output is also an input to the same callback - const alsoInput = checkInOutOverlap(outIdProp, inputs); + let alsoInput; + if (config.validate_callbacks) { + alsoInput = checkInOutOverlap(outIdProp, inputs); + } if (typeof outId === 'object') { - const outIdList = makeAllIds(outId, {}); - outIdList.forEach(id => { - const tempOutIdProp = {id, property}; - let outIdName = combineIdAndProp(tempOutIdProp); + if (config.validate_callbacks) { + const outIdList = makeAllIds(outId, {}); + outIdList.forEach(id => { + const tempOutIdProp = {id, property}; + let outIdName = combineIdAndProp(tempOutIdProp); + // if this output is also an input, add `outputTag` to the name + if (alsoInput) { + duplicateOutputs.push(tempOutIdProp); + outIdName += outputTag; + } + addOutputToMulti(id, outIdName); + }); + } + addPattern(outputPatterns, outId, property, finalDependency); + } else { + if (config.validate_callbacks) { + let outIdName = combineIdAndProp(outIdProp); // if this output is also an input, add `outputTag` to the name if (alsoInput) { - duplicateOutputs.push(tempOutIdProp); + duplicateOutputs.push(outIdProp); outIdName += outputTag; } - addOutputToMulti(id, outIdName); - }); - addPattern(outputPatterns, outId, property, finalDependency); - } else { - let outIdName = combineIdAndProp(outIdProp); - // if this output is also an input, add `outputTag` to the name - if (alsoInput) { - duplicateOutputs.push(outIdProp); - outIdName += outputTag; + addOutputToMulti({}, outIdName); } - addOutputToMulti({}, outIdName); addMap(outputMap, outId, property, finalDependency); } }); @@ -913,7 +925,8 @@ export function computeGraphs(dependencies, dispatchError) { } } }); - + const end = performance.now(); + window.dash_clientside.callbackGraphTime = (end - start).toFixed(2); return finalGraphs; } diff --git a/dash/dash-renderer/src/config.ts b/dash/dash-renderer/src/config.ts index 55a938e06e..ac18678364 100644 --- a/dash/dash-renderer/src/config.ts +++ b/dash/dash-renderer/src/config.ts @@ -21,6 +21,7 @@ export type DashConfig = { }; serve_locally?: boolean; plotlyjs_url?: string; + validate_callbacks: boolean; }; export default function getConfigFromDOM(): DashConfig { diff --git a/dash/dash.py b/dash/dash.py index 03bdec5498..122cf54dd6 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -937,6 +937,7 @@ def _config(self): "dash_version_url": DASH_VERSION_URL, "ddk_version": ddk_version, "plotly_version": plotly_version, + "validate_callbacks": self._dev_tools.validate_callbacks, } if self._plotly_cloud is None: if os.getenv("DASH_ENTERPRISE_ENV") == "WORKSPACE": @@ -1968,6 +1969,7 @@ def _setup_dev_tools(self, **kwargs): "hot_reload", "silence_routes_logging", "prune_errors", + "validate_callbacks", ): dev_tools[attr] = get_combined_config( attr, kwargs.get(attr, None), default=debug @@ -2003,6 +2005,7 @@ def enable_dev_tools( # pylint: disable=too-many-branches dev_tools_silence_routes_logging: Optional[bool] = None, dev_tools_disable_version_check: Optional[bool] = None, dev_tools_prune_errors: Optional[bool] = None, + dev_tools_validate_callbacks: Optional[bool] = None, ) -> bool: """Activate the dev tools, called by `run`. If your application is served by wsgi and you want to activate the dev tools, you can call @@ -2024,6 +2027,7 @@ def enable_dev_tools( # pylint: disable=too-many-branches - DASH_SILENCE_ROUTES_LOGGING - DASH_DISABLE_VERSION_CHECK - DASH_PRUNE_ERRORS + - DASH_VALIDATE_CALLBACKS :param debug: Enable/disable all the dev tools unless overridden by the arguments or environment variables. Default is ``True`` when @@ -2079,6 +2083,10 @@ def enable_dev_tools( # pylint: disable=too-many-branches env: ``DASH_PRUNE_ERRORS`` :type dev_tools_prune_errors: bool + :param dev_tools_validate_callbacks: Check for circular callback + dependencies and raise an error if any are found. env: ``DASH_VALIDATE_CALLBACKS`` + :type dev_tools_validate_callbacks: bool + :return: debug """ if debug is None: @@ -2096,6 +2104,7 @@ def enable_dev_tools( # pylint: disable=too-many-branches silence_routes_logging=dev_tools_silence_routes_logging, disable_version_check=dev_tools_disable_version_check, prune_errors=dev_tools_prune_errors, + validate_callbacks=dev_tools_validate_callbacks, ) if dev_tools.silence_routes_logging: @@ -2319,6 +2328,7 @@ def run( dev_tools_silence_routes_logging: Optional[bool] = None, dev_tools_disable_version_check: Optional[bool] = None, dev_tools_prune_errors: Optional[bool] = None, + dev_tools_validate_callbacks: Optional[bool] = None, **flask_run_options, ): """Start the flask server in local mode, you should not run this on a @@ -2409,6 +2419,10 @@ def run( env: ``DASH_PRUNE_ERRORS`` :type dev_tools_prune_errors: bool + :param dev_tools_validate_callbacks: Check for circular callback + dependencies and raise an error if any are found. env: ``DASH_VALIDATE_CALLBACKS`` + :type dev_tools_validate_callbacks: bool + :param jupyter_mode: How to display the application when running inside a jupyter notebook. @@ -2446,6 +2460,7 @@ def run( dev_tools_silence_routes_logging, dev_tools_disable_version_check, dev_tools_prune_errors, + dev_tools_validate_callbacks, ) # Evaluate the env variables at runtime diff --git a/tests/integration/renderer/test_benchmarking.py b/tests/integration/renderer/test_benchmarking.py new file mode 100644 index 0000000000..9ab4c7a788 --- /dev/null +++ b/tests/integration/renderer/test_benchmarking.py @@ -0,0 +1,135 @@ +from dash import Dash, html, dcc, Input, Output, State, ALL, callback +import dash.testing.wait as wait +import time +import pytest + + +def make_app(num_groups=500, items_per_group=20): + app = Dash(__name__) + + NUM_GROUPS = num_groups + ITEMS_PER_GROUP = items_per_group + + children = [] + for g in range(NUM_GROUPS): + group_children = [] + for i in range(ITEMS_PER_GROUP): + group_children.append( + html.Div( + [ + dcc.Input( + id={"type": "input", "group": g, "index": i}, + value=f"g{g}-i{i}", + ), + html.Div( + id={"type": "output", "group": g, "index": i}, + ), + ] + ) + ) + children.append( + html.Details( + [ + html.Summary(f"Group {g}"), + html.Div(group_children), + ] + ) + ) + + for g in range(NUM_GROUPS): + + @callback( + Output({"type": "output", "group": g, "index": ALL}, "children"), + Input({"type": "input", "group": g, "index": ALL}, "value"), + prevent_initial_call=True, + ) + def update(v, _g=g): + return f"Updated: {v}" + + for g in range(NUM_GROUPS - 1): + + @callback( + Output({"type": "output", "group": g + 1, "index": ALL}, "style"), + Input({"type": "input", "group": g, "index": ALL}, "value"), + prevent_initial_call=True, + ) + def cross_update(values, _g=g): + return [{"color": "blue"} for _ in values] + + for g in range(0, NUM_GROUPS, 3): + + @callback( + Output({"type": "output", "group": g, "index": ALL}, "title"), + Input({"type": "input", "group": g, "index": ALL}, "value"), + State({"type": "output", "group": g, "index": ALL}, "children"), + prevent_initial_call=True, + ) + def tooltip_update(values, current, _g=g): + return [f"{v} ({c})" for v, c in zip(values, current or [""] * len(values))] + + def layout(): + return html.Div( + [ + html.H3("Dash 4 Firefox Performance MWE"), + dcc.Input(id="input", value="initial value", type="text"), + html.Div(id="output"), + dcc.Store(id="store", data=time.time()), + html.Div(children), + ] + ) + + app.layout = layout + + app.clientside_callback( + """ + function(value, ts) { + if (!ts) return ''; + var now = Date.now() / 1000; + return (now - ts).toFixed(2); + } + """, + Output("output", "children"), + Input("input", "value"), + State("store", "data"), + ) + + return app + + +check_timing = {} + + +@pytest.mark.parametrize( + "dev_tools,store", + [ + ({"dev_tools_validate_callbacks": False}, "disabled"), + ({"dev_tools_validate_callbacks": True}, "enabled"), + ], +) +def test_compute_graph_timing(dash_duo, dev_tools, store): + app = make_app() + dash_duo.start_server(app, **dev_tools) + times = [] + for _ in range(10): + wait.until( + lambda: dash_duo.find_element("#output").text.strip() != "", timeout=4 + ) + graph_compute_time = float( + dash_duo.driver.execute_script( + "return window.dash_clientside.callbackGraphTime" + ) + ) + times.append(graph_compute_time) + dash_duo.driver.refresh() + avg_time = sum(times) / len(times) if times else 0 + check_timing[store] = avg_time + if store == "enabled": + print(f"Average time with store enabled: {avg_time:.2f} ms") + assert ( + check_timing["disabled"] < avg_time + ), "Expected faster performance with circular callback check disabled" + if store == "disabled": + print(f"Average time with store disabled: {avg_time:.2f} ms") + assert ( + avg_time < 500 + ), "Expected average time to be under 1/2 seconds with circular callback check disabled" From f6c604c066b8b6bf2078da608ceb014fed5bd6e2 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:29:22 -0400 Subject: [PATCH 2/6] adjustments for load different between dash 3 and dash 4 --- tests/integration/renderer/test_benchmarking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/renderer/test_benchmarking.py b/tests/integration/renderer/test_benchmarking.py index 9ab4c7a788..612d992d3d 100644 --- a/tests/integration/renderer/test_benchmarking.py +++ b/tests/integration/renderer/test_benchmarking.py @@ -4,7 +4,7 @@ import pytest -def make_app(num_groups=500, items_per_group=20): +def make_app(num_groups=500, items_per_group=10): app = Dash(__name__) NUM_GROUPS = num_groups From 680ad954b026242acca9e2df8b142abdaeeb76c1 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:58:57 -0400 Subject: [PATCH 3/6] adjustments to use pattern maps and then offload to the expected patterns for callbacks, great reduction in overhead --- .../dash-renderer/src/actions/dependencies.js | 78 +++++++++++++------ .../integration/renderer/test_benchmarking.py | 6 +- 2 files changed, 58 insertions(+), 26 deletions(-) diff --git a/dash/dash-renderer/src/actions/dependencies.js b/dash/dash-renderer/src/actions/dependencies.js index bbc17b534b..e26c45be8e 100644 --- a/dash/dash-renderer/src/actions/dependencies.js +++ b/dash/dash-renderer/src/actions/dependencies.js @@ -163,26 +163,52 @@ function addMap(depMap, id, prop, dependency) { callbacks.push(dependency); } -function addPattern(depMap, idSpec, prop, dependency) { +// Patterns are stored in a nested Map structure to avoid the overhead of +// stringifying ids for every callback. +function addPattern(patterns, idSpec, prop, dependency) { const keys = Object.keys(idSpec).sort(); const keyStr = keys.join(','); const values = props(keys, idSpec); - const keyCallbacks = (depMap[keyStr] = depMap[keyStr] || {}); - const propCallbacks = (keyCallbacks[prop] = keyCallbacks[prop] || []); - let valMatch = false; - for (let i = 0; i < propCallbacks.length; i++) { - if (equals(values, propCallbacks[i].values)) { - valMatch = propCallbacks[i]; - break; - } + const valuesKey = values + .map(v => + typeof v === 'object' && v !== null + ? v.wild + ? v.wild + : JSON.stringify(v) + : String(v) + ) + .join('|'); + + if (!patterns.has(keyStr)) { + patterns.set(keyStr, new Map()); + } + const propMap = patterns.get(keyStr); + if (!propMap.has(prop)) { + propMap.set(prop, new Map()); } + const valueMap = propMap.get(prop); + + let valMatch = valueMap.get(valuesKey); if (!valMatch) { valMatch = {keys, values, callbacks: []}; - propCallbacks.push(valMatch); + valueMap.set(valuesKey, valMatch); } valMatch.callbacks.push(dependency); } +// Convert the nested Map structure of patterns into the plain nested object structure +// expected by the rest of the code, with stringified id keys. +// This is only done once per pattern, at the end of graph construction, +// to minimize the overhead of stringifying ids. +function offloadPatterns(patternsMap, targetMap) { + for (const [keyStr, propMap] of patternsMap.entries()) { + targetMap[keyStr] = {}; + for (const [prop, valueMap] of propMap.entries()) { + targetMap[keyStr][prop] = Array.from(valueMap.values()); + } + } +} + function validateDependencies(parsedDependencies, dispatchError) { const outStrs = {}; const outObjs = []; @@ -686,8 +712,10 @@ export function computeGraphs(dependencies, dispatchError, config) { */ const outputMap = {}; const inputMap = {}; - const outputPatterns = {}; - const inputPatterns = {}; + const outputPatternMap = new Map(); + const inputPatternMap = new Map(); + let outputPatterns = {}; + let inputPatterns = {}; const finalGraphs = { MultiGraph: multiGraph, @@ -704,12 +732,14 @@ export function computeGraphs(dependencies, dispatchError, config) { return finalGraphs; } + // builds up wildcardPlaceholders with all the wildcard keys and values used in the callbacks, so we can generate the full list of ids that each callback depends on. parsedDependencies.forEach(dependency => { const {outputs, inputs} = dependency; - outputs.concat(inputs).forEach(item => { - const {id} = item; - if (typeof id === 'object') { + outputs + .concat(inputs) + .filter(item => typeof item.id === 'object') + .forEach(item => { forEachObjIndexed((val, key) => { if (!wildcardPlaceholders[key]) { wildcardPlaceholders[key] = { @@ -725,11 +755,11 @@ export function computeGraphs(dependencies, dispatchError, config) { } else if (keyPlaceholders.exact.indexOf(val) === -1) { keyPlaceholders.exact.push(val); } - }, id); - } - }); + }, item.id); + }); }); + // Efficiently build wildcardPlaceholders.vals arrays forEachObjIndexed(keyPlaceholders => { const {exact, expand} = keyPlaceholders; const vals = exact.slice().sort(idValSort); @@ -811,7 +841,7 @@ export function computeGraphs(dependencies, dispatchError, config) { const cbOut = []; function addInputToMulti(inIdProp, outIdProp, firstPass = true) { - if (!config.validate_callbacks) return + if (!config.validate_callbacks) return; multiGraph.addNode(inIdProp); multiGraph.addDependency(inIdProp, outIdProp); // only store callback inputs and outputs during the first pass @@ -829,7 +859,7 @@ export function computeGraphs(dependencies, dispatchError, config) { cbOut.push([]); function addOutputToMulti(outIdFinal, outIdProp) { - if (!config.validate_callbacks) return + if (!config.validate_callbacks) return; multiGraph.addNode(outIdProp); inputs.forEach(inObj => { const {id: inId, property} = inObj; @@ -866,7 +896,7 @@ export function computeGraphs(dependencies, dispatchError, config) { // check if this output is also an input to the same callback let alsoInput; if (config.validate_callbacks) { - alsoInput = checkInOutOverlap(outIdProp, inputs); + alsoInput = checkInOutOverlap(outIdProp, inputs); } if (typeof outId === 'object') { if (config.validate_callbacks) { @@ -882,7 +912,7 @@ export function computeGraphs(dependencies, dispatchError, config) { addOutputToMulti(id, outIdName); }); } - addPattern(outputPatterns, outId, property, finalDependency); + addPattern(outputPatternMap, outId, property, finalDependency); } else { if (config.validate_callbacks) { let outIdName = combineIdAndProp(outIdProp); @@ -900,12 +930,14 @@ export function computeGraphs(dependencies, dispatchError, config) { inputs.forEach(inputObject => { const {id: inId, property: inProp} = inputObject; if (typeof inId === 'object') { - addPattern(inputPatterns, inId, inProp, finalDependency); + addPattern(inputPatternMap, inId, inProp, finalDependency); } else { addMap(inputMap, inId, inProp, finalDependency); } }); }); + outputPatterns = offloadPatterns(outputPatternMap, outputPatterns); + inputPatterns = offloadPatterns(inputPatternMap, inputPatterns); // second pass for adding new output nodes as dependencies where needed duplicateOutputs.forEach(dupeOutIdProp => { diff --git a/tests/integration/renderer/test_benchmarking.py b/tests/integration/renderer/test_benchmarking.py index 612d992d3d..0a84f12981 100644 --- a/tests/integration/renderer/test_benchmarking.py +++ b/tests/integration/renderer/test_benchmarking.py @@ -4,7 +4,7 @@ import pytest -def make_app(num_groups=500, items_per_group=10): +def make_app(num_groups=500, items_per_group=20): app = Dash(__name__) NUM_GROUPS = num_groups @@ -131,5 +131,5 @@ def test_compute_graph_timing(dash_duo, dev_tools, store): if store == "disabled": print(f"Average time with store disabled: {avg_time:.2f} ms") assert ( - avg_time < 500 - ), "Expected average time to be under 1/2 seconds with circular callback check disabled" + avg_time < 100 + ), "Expected average time to be under 100 ms with circular callback check disabled" From d5145700456baaf8c85922268ae7f2cda6f43f14 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:16:20 -0400 Subject: [PATCH 4/6] adjustments based on feedback --- dash/dash-renderer/src/actions/dependencies.js | 2 +- tests/integration/renderer/test_benchmarking.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dash/dash-renderer/src/actions/dependencies.js b/dash/dash-renderer/src/actions/dependencies.js index e26c45be8e..b7bfa11367 100644 --- a/dash/dash-renderer/src/actions/dependencies.js +++ b/dash/dash-renderer/src/actions/dependencies.js @@ -958,7 +958,7 @@ export function computeGraphs(dependencies, dispatchError, config) { } }); const end = performance.now(); - window.dash_clientside.callbackGraphTime = (end - start).toFixed(2); + window.dash_component_api.callbackGraphTime = (end - start).toFixed(2); return finalGraphs; } diff --git a/tests/integration/renderer/test_benchmarking.py b/tests/integration/renderer/test_benchmarking.py index 0a84f12981..8812a96b93 100644 --- a/tests/integration/renderer/test_benchmarking.py +++ b/tests/integration/renderer/test_benchmarking.py @@ -111,12 +111,13 @@ def test_compute_graph_timing(dash_duo, dev_tools, store): dash_duo.start_server(app, **dev_tools) times = [] for _ in range(10): + dash_duo.wait_for_element("#input") wait.until( lambda: dash_duo.find_element("#output").text.strip() != "", timeout=4 ) graph_compute_time = float( dash_duo.driver.execute_script( - "return window.dash_clientside.callbackGraphTime" + "return window.dash_component_api.callbackGraphTime" ) ) times.append(graph_compute_time) From cd5534c79a9b294a286e409999cacccc4bb87587 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:33:06 -0400 Subject: [PATCH 5/6] adjustments for the component_api --- dash/dash-renderer/src/actions/dependencies.js | 4 ++++ dash/dash-renderer/src/dashApi.ts | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dash/dash-renderer/src/actions/dependencies.js b/dash/dash-renderer/src/actions/dependencies.js index b7bfa11367..2ce6d0d577 100644 --- a/dash/dash-renderer/src/actions/dependencies.js +++ b/dash/dash-renderer/src/actions/dependencies.js @@ -958,7 +958,11 @@ export function computeGraphs(dependencies, dispatchError, config) { } }); const end = performance.now(); + if (!window.dash_component_api) { + window.dash_component_api = {}; + } window.dash_component_api.callbackGraphTime = (end - start).toFixed(2); + return finalGraphs; } diff --git a/dash/dash-renderer/src/dashApi.ts b/dash/dash-renderer/src/dashApi.ts index 29b2e72ce3..a7ce17ce52 100644 --- a/dash/dash-renderer/src/dashApi.ts +++ b/dash/dash-renderer/src/dashApi.ts @@ -35,7 +35,9 @@ function getLayout(componentPathOrId: DashLayoutPath | string): any { } } -window.dash_component_api = { +window.dash_component_api = Object.assign( + window.dash_component_api || {}, + { ExternalWrapper, DashContext, useDashContext, @@ -46,4 +48,5 @@ window.dash_component_api = { useDevtool, useDevtoolMenuButtonClassName } -}; + } +); From ffff73d52f678208ac80651177a56428163cb23b Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:44:07 -0400 Subject: [PATCH 6/6] fix for lint --- dash/dash-renderer/src/dashApi.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dash/dash-renderer/src/dashApi.ts b/dash/dash-renderer/src/dashApi.ts index a7ce17ce52..10d3286fca 100644 --- a/dash/dash-renderer/src/dashApi.ts +++ b/dash/dash-renderer/src/dashApi.ts @@ -35,9 +35,7 @@ function getLayout(componentPathOrId: DashLayoutPath | string): any { } } -window.dash_component_api = Object.assign( - window.dash_component_api || {}, - { +window.dash_component_api = Object.assign(window.dash_component_api || {}, { ExternalWrapper, DashContext, useDashContext, @@ -48,5 +46,4 @@ window.dash_component_api = Object.assign( useDevtool, useDevtoolMenuButtonClassName } - } -); +});