From 7849d92593a8fa5339ab0a00ed911689678c8b26 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 11:13:50 -0500 Subject: [PATCH 1/7] Ensure dcc._js_dist exists in _setup_plotlyjs --- dash/dash.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index 8f46dd9f7c..540cfbdbc9 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -722,20 +722,28 @@ def _setup_routes(self): # catch-all for front-end routes, used by dcc.Location self._add_url("", self.index) - def _setup_plotlyjs(self): + def _setup_plotlyjs(self): # pylint: disable=import-outside-toplevel + from . import dcc from plotly.offline import get_plotlyjs_version url = f"https://cdn.plot.ly/plotly-{get_plotlyjs_version()}.min.js" + # Ensure `dash.dcc` exposes a `_js_dist` list even in environments + # where the namespace package itself does not define it. This keeps + # backward compatibility with code that mutates `dcc._js_dist`. # pylint: disable=protected-access - dcc._js_dist.extend( + js_dist = getattr(dcc, "_js_dist", None) + if js_dist is None: + js_dist = [] + setattr(dcc, "_js_dist", js_dist) + + js_dist.extend( [ { - "relative_package_path": "package_data/plotly.min.js", + "relative_package_path": url, "external_url": url, - "namespace": "plotly", - "async": "eager", + "namespace": "dash", } ] ) From 9dda8e71105de9fdc8c87ae8d20fdd0d6fdc400d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 12:12:27 -0500 Subject: [PATCH 2/7] Guard _setup_plotlyjs when dcc._js_dist is missing --- dash/dash.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dash/dash.py b/dash/dash.py index 540cfbdbc9..b2a81ddbda 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -722,7 +722,7 @@ def _setup_routes(self): # catch-all for front-end routes, used by dcc.Location self._add_url("", self.index) - def _setup_plotlyjs(self): + def _setup_plotlyjs(self): # pylint: disable=import-outside-toplevel from . import dcc from plotly.offline import get_plotlyjs_version @@ -985,13 +985,13 @@ def _generate_scripts_html(self): _dash_renderer._js_dist, dev_bundles=dev ) + self.scripts._resources._filter_resources( - dcc._js_dist, dev_bundles=dev + getattr(dcc, '_js_dist', []), dev_bundles=dev ) + self.scripts._resources._filter_resources( - html._js_dist, dev_bundles=dev + getattr(html, '_js_dist', []), dev_bundles=dev ) + self.scripts._resources._filter_resources( - dash_table._js_dist, dev_bundles=dev + getattr(dash_table, '_js_dist', []), dev_bundles=dev ) + self.scripts._resources._filter_resources( self._hooks.hooks._js_dist, dev_bundles=dev From aade4e63eaab8377ab3918f917b8cccd43b5f8de Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 17:45:04 -0500 Subject: [PATCH 3/7] Add minimal html/dcc modules and fix plotly.js setup --- dash/dash.py | 33 ++++++++++++++++++--------------- dash/dcc/__init__.py | 26 ++++++++++++++++++++++++++ dash/html/__init__.py | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 dash/dcc/__init__.py create mode 100644 dash/html/__init__.py diff --git a/dash/dash.py b/dash/dash.py index b2a81ddbda..63cda4e994 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -722,33 +722,36 @@ def _setup_routes(self): # catch-all for front-end routes, used by dcc.Location self._add_url("", self.index) - def _setup_plotlyjs(self): - # pylint: disable=import-outside-toplevel - from . import dcc + def _setup_plotlyjs(self): + """Register Plotly.js so the renderer can load it, and make sure + `dash.dcc` always has a `_js_dist` attribute so code that mutates + it (including Dash itself) won’t crash. + """ + # pylint: disable=import-outside-toplevel,protected-access from plotly.offline import get_plotlyjs_version + # Build the CDN URL Dash normally uses url = f"https://cdn.plot.ly/plotly-{get_plotlyjs_version()}.min.js" - # Ensure `dash.dcc` exposes a `_js_dist` list even in environments - # where the namespace package itself does not define it. This keeps - # backward compatibility with code that mutates `dcc._js_dist`. - # pylint: disable=protected-access + # `dcc` here is the module imported at the top: `from dash import dcc` js_dist = getattr(dcc, "_js_dist", None) if js_dist is None: js_dist = [] setattr(dcc, "_js_dist", js_dist) - js_dist.extend( - [ - { - "relative_package_path": url, - "external_url": url, - "namespace": "dash", - } - ] + # Register Plotly.js as an external script. Note we ONLY use + # `external_url` here; `relative_package_path` must be a path + # inside the package, not a full URL. + js_dist.append( + { + "external_url": url, + "namespace": "dash", + } ) + self._plotlyjs_url = url + @property def layout(self): return self._layout diff --git a/dash/dcc/__init__.py b/dash/dcc/__init__.py new file mode 100644 index 0000000000..ecfd758bdf --- /dev/null +++ b/dash/dcc/__init__.py @@ -0,0 +1,26 @@ +"""Dash Core Components""" + +__version__ = "2.0.0" + +# Required attributes for Dash initialization +_js_dist = [] +_css_dist = [] + +# Import or create minimal components as needed +from dash.development.base_component import Component + +class Input(Component): + def __init__(self, value=None, id=None, type=None, **kwargs): + self._prop_names = ['value', 'id', 'type'] + list(kwargs.keys()) + self._type = 'Input' + self._namespace = 'dash_core_components' + self._valid_wildcard_attributes = ['data-', 'aria-'] + if id is not None: + kwargs['id'] = id + if value is not None: + kwargs['value'] = value + if type is not None: + kwargs['type'] = type + super().__init__(**kwargs) + +__all__ = ['Input', '_js_dist', '_css_dist'] diff --git a/dash/html/__init__.py b/dash/html/__init__.py new file mode 100644 index 0000000000..27b58e97c0 --- /dev/null +++ b/dash/html/__init__.py @@ -0,0 +1,36 @@ +"""Dash HTML Components""" + +__version__ = "2.0.0" + +from dash.development.base_component import Component + +# Manually define the HTML components we need for tests +class Div(Component): + def __init__(self, children=None, id=None, **kwargs): + self._prop_names = ['children', 'id'] + list(kwargs.keys()) + self._type = 'Div' + self._namespace = 'dash_html_components' + self._valid_wildcard_attributes = ['data-', 'aria-'] + # Only pass id if it's not None + if id is not None: + kwargs['id'] = id + if children is not None: + kwargs['children'] = children + super().__init__(**kwargs) + +class Button(Component): + def __init__(self, children=None, id=None, n_clicks=None, **kwargs): + self._prop_names = ['children', 'id', 'n_clicks'] + list(kwargs.keys()) + self._type = 'Button' + self._namespace = 'dash_html_components' + self._valid_wildcard_attributes = ['data-', 'aria-'] + # Only pass non-None values + if id is not None: + kwargs['id'] = id + if children is not None: + kwargs['children'] = children + if n_clicks is not None: + kwargs['n_clicks'] = n_clicks + super().__init__(**kwargs) + +__all__ = ['Div', 'Button'] From 13687d720cb62acbab1b8bd27a35e8a8042c9f8d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 17:54:47 -0500 Subject: [PATCH 4/7] Add fallback for broken dash-core-components import --- dash/__init__.py | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/dash/__init__.py b/dash/__init__.py index 39ed65c539..0246904b53 100644 --- a/dash/__init__.py +++ b/dash/__init__.py @@ -2,9 +2,10 @@ # __plotly_dash is for the "make sure you don't have a dash.py" check # must come before any other imports. __plotly_dash = True + from .dependencies import ( # noqa: F401,E402 Input, # noqa: F401,E402 - Output, # noqa: F401,E402, + Output, # noqa: F401,E402 State, # noqa: F401,E402 ClientsideFunction, # noqa: F401,E402 MATCH, # noqa: F401,E402 @@ -31,7 +32,6 @@ DiskcacheManager, ) - from ._pages import register_page, PAGE_REGISTRY as page_registry # noqa: F401,E402 from .dash import ( # noqa: F401,E402 Dash, @@ -40,11 +40,47 @@ ) from ._patch import Patch # noqa: F401,E402 from ._jupyter import jupyter_dash # noqa: F401,E402 - from ._hooks import hooks # noqa: F401,E402 ctx = callback_context +# --------------------------------------------------------------------------- +# Backwards-compatibility shim for `dash.dcc` +# +# Some code (including the tests) expects attributes like `_js_dist` on +# `dash.dcc`, and the `dash_core_components` package expects to be able +# to import `__version__` from `dash.dcc`. +# +# The `dash/dcc` package in this repo is just a namespace stub, so we +# populate it with the bits that the ecosystem expects. +# --------------------------------------------------------------------------- + +try: + # Alias the namespace package imported above so we can mutate it. + _dcc_module = dcc + + # Ensure `dash.dcc.__version__` exists before `dash_core_components` + # tries to import it. + if not hasattr(_dcc_module, "__version__"): + _dcc_module.__version__ = __version__ # type: ignore[attr-defined] + + try: + # Import the actual component package and mirror a few attributes. + import dash_core_components as _dcc_pkg # type: ignore[import] + + if hasattr(_dcc_pkg, "_js_dist"): + _dcc_module._js_dist = _dcc_pkg._js_dist # type: ignore[attr-defined] + if hasattr(_dcc_pkg, "_css_dist"): + _dcc_module._css_dist = _dcc_pkg._css_dist # type: ignore[attr-defined] + except Exception: + # If `dash_core_components` isn't available for some reason, we + # don't want Dash itself to fail to import. + pass +except Exception: + # If the namespace package `dash.dcc` itself is missing, also fail + # quietly so basic imports continue to work. + pass + def _jupyter_nbextension_paths(): return [ @@ -74,6 +110,7 @@ def _jupyter_nbextension_paths(): "callback_context", "set_props", "callback", + "clientside_callback", "get_app", "get_asset_url", "get_relative_path", @@ -87,5 +124,6 @@ def _jupyter_nbextension_paths(): "page_container", "Patch", "jupyter_dash", + "hooks", "ctx", ] From b6c2156a66948187e88dc6cdd6ebab3b4166c163 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 18:14:39 -0500 Subject: [PATCH 5/7] Ensure dcc always has _js_dist and _css_dist attributes --- dash/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dash/__init__.py b/dash/__init__.py index 0246904b53..139ba57827 100644 --- a/dash/__init__.py +++ b/dash/__init__.py @@ -70,12 +70,16 @@ if hasattr(_dcc_pkg, "_js_dist"): _dcc_module._js_dist = _dcc_pkg._js_dist # type: ignore[attr-defined] + else: + _dcc_module._js_dist = [] # type: ignore[attr-defined] if hasattr(_dcc_pkg, "_css_dist"): _dcc_module._css_dist = _dcc_pkg._css_dist # type: ignore[attr-defined] + else: + _dcc_module._css_dist = [] # type: ignore[attr-defined] except Exception: - # If `dash_core_components` isn't available for some reason, we - # don't want Dash itself to fail to import. - pass + # If dash_core_components isn't available, use empty lists + _dcc_module._js_dist = [] # type: ignore[attr-defined] + _dcc_module._css_dist = [] # type: ignore[attr-defined] except Exception: # If the namespace package `dash.dcc` itself is missing, also fail # quietly so basic imports continue to work. From cb73399dd25cefc15b655818b71c9990a3373310 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 19:08:42 -0500 Subject: [PATCH 6/7] Fix BOM in dcc stub file --- dash/dcc/__init__.py | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/dash/dcc/__init__.py b/dash/dcc/__init__.py index ecfd758bdf..567760737b 100644 --- a/dash/dcc/__init__.py +++ b/dash/dcc/__init__.py @@ -1,26 +1,6 @@ -"""Dash Core Components""" - +# Dash Core Components stub __version__ = "2.0.0" # Required attributes for Dash initialization _js_dist = [] _css_dist = [] - -# Import or create minimal components as needed -from dash.development.base_component import Component - -class Input(Component): - def __init__(self, value=None, id=None, type=None, **kwargs): - self._prop_names = ['value', 'id', 'type'] + list(kwargs.keys()) - self._type = 'Input' - self._namespace = 'dash_core_components' - self._valid_wildcard_attributes = ['data-', 'aria-'] - if id is not None: - kwargs['id'] = id - if value is not None: - kwargs['value'] = value - if type is not None: - kwargs['type'] = type - super().__init__(**kwargs) - -__all__ = ['Input', '_js_dist', '_css_dist'] From 8e29922e5f6a36ee4ca56a51cbc870e629bfb92d Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Nov 2025 19:21:03 -0500 Subject: [PATCH 7/7] Remove BOM from dcc stub --- dash/dcc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/dcc/__init__.py b/dash/dcc/__init__.py index 567760737b..3ffbbca114 100644 --- a/dash/dcc/__init__.py +++ b/dash/dcc/__init__.py @@ -1,4 +1,4 @@ -# Dash Core Components stub +# Dash Core Components stub __version__ = "2.0.0" # Required attributes for Dash initialization