From 7f17531a12ada8e311cfe8cb2774a4387272868b Mon Sep 17 00:00:00 2001 From: Brandon Schabell Date: Fri, 2 Oct 2026 18:32:11 -0500 Subject: [PATCH] Only load the IPython and Jupyter integration modules when Dash is imported inside IPython --- CHANGELOG.md | 1 + dash/_jupyter.py | 3 +++ tests/unit/test_jupyter_import.py | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 tests/unit/test_jupyter_import.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fb24b979c..85f5996a84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - [#3646](https://github.com/plotly/dash/pull/3646) Remove React 16 support (`16.14.0` is no longer an accepted value for `REACT_VERSION` / `_set_react_version`). ### Changed +- [#4037](https://github.com/plotly/dash/pull/4037) Only load the IPython and Jupyter integration modules when Dash is imported inside IPython. Roughly halves `import dash` time in environments that have IPython installed. - [#3987](https://github.com/plotly/dash/pull/3987) Forward FastAPI reload scope options (`reload_dirs`, `reload_excludes`, and `reload_includes`) to Uvicorn when reloading. - [#3986](https://github.com/plotly/dash/pull/3986) Adjust `_run_before_hooks` in the `fastapi` backend to honor a response returned by a `before_request` function, matching the `flask` backend's behavior. diff --git a/dash/_jupyter.py b/dash/_jupyter.py index 85668127df..56ab21f28f 100644 --- a/dash/_jupyter.py +++ b/dash/_jupyter.py @@ -15,6 +15,9 @@ from werkzeug.serving import make_server try: + if "IPython" not in sys.modules: + # Not inside IPython: skip the heavy imports, they double the cost of `import dash`. + raise ImportError from IPython import get_ipython # type: ignore[attr-defined] from IPython.display import IFrame, display, Javascript # type: ignore[import-not-found] from IPython.core.display import HTML # type: ignore[import-not-found] diff --git a/tests/unit/test_jupyter_import.py b/tests/unit/test_jupyter_import.py new file mode 100644 index 0000000000..6a84685de6 --- /dev/null +++ b/tests/unit/test_jupyter_import.py @@ -0,0 +1,27 @@ +import subprocess +import sys + +import pytest + +pytest.importorskip("IPython") + + +def _run(*args): + result = subprocess.run( + [sys.executable, *args], capture_output=True, text=True, check=False + ) + assert result.returncode == 0, result.stderr + + +def test_import_dash_does_not_load_ipython(): + _run("-c", "import dash, sys; assert 'IPython' not in sys.modules") + + +def test_jupyter_support_loads_inside_ipython(): + _run( + "-m", + "IPython", + "-c", + "from dash import _jupyter; " + "assert _jupyter._dep_installed and _jupyter.jupyter_dash.in_ipython", + )