Skip to content

Commit f02a947

Browse files
committed
fix blueprint registering and double init
1 parent ce6d0f0 commit f02a947

5 files changed

Lines changed: 56 additions & 1 deletion

File tree

dash/backends/_fastapi.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ def create_app(name: str = "__main__", config: Dict[str, Any] | None = None):
272272
def register_assets_blueprint(
273273
self, blueprint_name: str, assets_url_path: str, assets_folder: str
274274
):
275+
# Check if route is already mounted to avoid duplicate registration
276+
# This can happen when init_app() is called multiple times
277+
for route in self.server.routes:
278+
if getattr(route, "name", None) == blueprint_name:
279+
return
275280
try:
276281
self.server.mount(
277282
assets_url_path,

dash/backends/_flask.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ def create_app(name: str = "__main__", config: Dict[str, Any] | None = None):
8686
def register_assets_blueprint(
8787
self, blueprint_name: str, assets_url_path: str, assets_folder: str
8888
):
89+
# Check if blueprint is already registered to avoid duplicate registration
90+
# This can happen when init_app() is called multiple times (e.g., with flask run)
91+
if blueprint_name in self.server.blueprints:
92+
return
8993
bp = Blueprint(
9094
blueprint_name,
9195
__name__,

dash/backends/_quart.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ def create_app(
122122
def register_assets_blueprint(
123123
self, blueprint_name: str, assets_url_path: str, assets_folder: str # type: ignore[name-defined]
124124
):
125-
125+
# Check if blueprint is already registered to avoid duplicate registration
126+
# This can happen when init_app() is called multiple times (e.g., with quart run)
127+
if blueprint_name in self.server.blueprints:
128+
return
126129
bp = Blueprint(
127130
blueprint_name,
128131
__name__,

dash/dash.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,9 @@ def init_app(self, app: Optional[Any] = None, **kwargs) -> None:
731731
)
732732
if app is not None:
733733
self.server = app
734+
# Also update the backend's server reference so routes are registered
735+
# on the correct server (important when using server=False pattern)
736+
self.backend.server = app
734737
bp_prefix = config.routes_pathname_prefix.replace("/", "_").replace(".", "_")
735738
assets_blueprint_name = f"{bp_prefix}dash_assets"
736739
self.backend.register_assets_blueprint(

tests/unit/test_configs.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,3 +524,43 @@ def test_csrf_config_read_only():
524524
app.config.csrf_token_name = "something_else"
525525
with pytest.raises(AttributeError):
526526
app.config.csrf_header_name = "something_else"
527+
528+
529+
def test_init_app_with_flask_run_pattern():
530+
"""Test that init_app works correctly with the flask run pattern.
531+
532+
This tests the fix for https://github.com/plotly/dash/issues/3787
533+
where using flask run would cause a ValueError about duplicate blueprint
534+
registration because init_app was called twice (once automatically in
535+
__init__ with server=True, and once by the user in their create_app factory).
536+
"""
537+
# Simulate the flask run pattern where:
538+
# 1. Dash app is created with server=True (default)
539+
# 2. User's create_app factory calls init_app with their Flask server
540+
external_server = Flask("external_test")
541+
app = Dash(__name__)
542+
543+
# This should NOT raise "ValueError: The name '_dash_assets' is already registered"
544+
app.init_app(external_server)
545+
546+
# Verify the backend now uses the external server
547+
assert app.server is external_server
548+
assert app.backend.server is external_server
549+
550+
551+
def test_init_app_server_false_pattern():
552+
"""Test that init_app works correctly when server=False is used.
553+
554+
This tests the fix for https://github.com/plotly/dash/issues/3787
555+
where using server=False and then calling init_app would result in
556+
404 errors because the backend's server reference was not updated.
557+
"""
558+
external_server = Flask("external_test_false")
559+
app = Dash(__name__, server=False)
560+
561+
# Call init_app with the external server
562+
app.init_app(external_server)
563+
564+
# Verify both the app and backend use the external server
565+
assert app.server is external_server
566+
assert app.backend.server is external_server

0 commit comments

Comments
 (0)