Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions dash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -40,11 +40,51 @@
)
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]
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, 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.
pass


def _jupyter_nbextension_paths():
return [
Expand Down Expand Up @@ -74,6 +114,7 @@ def _jupyter_nbextension_paths():
"callback_context",
"set_props",
"callback",
"clientside_callback",
"get_app",
"get_asset_url",
"get_relative_path",
Expand All @@ -87,5 +128,6 @@ def _jupyter_nbextension_paths():
"page_container",
"Patch",
"jupyter_dash",
"hooks",
"ctx",
]
41 changes: 26 additions & 15 deletions dash/dash.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,25 +722,36 @@ def _setup_routes(self):
# catch-all for front-end routes, used by dcc.Location
self._add_url("<path:path>", self.index)

def _setup_plotlyjs(self):
# pylint: disable=import-outside-toplevel
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"

# pylint: disable=protected-access
dcc._js_dist.extend(
[
{
"relative_package_path": "package_data/plotly.min.js",
"external_url": url,
"namespace": "plotly",
"async": "eager",
}
]
# `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)

# 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
Expand Down Expand Up @@ -977,13 +988,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
Expand Down
6 changes: 6 additions & 0 deletions dash/dcc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Dash Core Components stub
__version__ = "2.0.0"

# Required attributes for Dash initialization
_js_dist = []
_css_dist = []
36 changes: 36 additions & 0 deletions dash/html/__init__.py
Original file line number Diff line number Diff line change
@@ -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']