Skip to content

Commit afcbc0d

Browse files
authored
Merge pull request #3626 from plotly/fix-global-backend
remove global backends.backend
2 parents c4ac323 + 1539c85 commit afcbc0d

7 files changed

Lines changed: 33 additions & 30 deletions

File tree

dash/_callback.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
BackgroundCallbackError,
2121
ImportedInsideCallbackError,
2222
)
23-
23+
from ._get_app import get_app
2424
from ._grouping import (
2525
flatten_grouping,
2626
make_grouping_by_index,
@@ -39,7 +39,6 @@
3939
from ._callback_context import context_value
4040
from ._no_update import NoUpdate
4141
from . import _validate
42-
from . import backends
4342

4443

4544
async def _async_invoke_callback(
@@ -373,7 +372,7 @@ def _get_callback_manager(
373372
" and store results on redis.\n"
374373
)
375374

376-
adapter = backends.backend.request_adapter()
375+
adapter = get_app().backend.request_adapter()
377376
old_job = adapter.args.getlist("oldJob") if hasattr(adapter.args, "getlist") else []
378377

379378
if old_job:
@@ -433,7 +432,7 @@ def _setup_background_callback(
433432

434433
def _progress_background_callback(response, callback_manager, background):
435434
progress_outputs = background.get("progress")
436-
adapter = backends.backend.request_adapter()
435+
adapter = get_app().backend.request_adapter()
437436
cache_key = adapter.args.get("cacheKey")
438437

439438
if progress_outputs:
@@ -451,7 +450,7 @@ def _update_background_callback(
451450
"""Set up the background callback and manage jobs."""
452451
callback_manager = _get_callback_manager(kwargs, background)
453452

454-
adapter = backends.backend.request_adapter()
453+
adapter = get_app().backend.request_adapter()
455454
cache_key = adapter.args.get("cacheKey") if adapter else None
456455
job_id = adapter.args.get("job") if adapter else None
457456

@@ -473,7 +472,7 @@ def _handle_rest_background_callback(
473472
multi,
474473
has_update=False,
475474
):
476-
adapter = backends.backend.request_adapter()
475+
adapter = get_app().backend.request_adapter()
477476
cache_key = adapter.args.get("cacheKey") if adapter else None
478477
job_id = adapter.args.get("job") if adapter else None
479478
# Must get job_running after get_result since get_results terminates it.
@@ -691,7 +690,7 @@ def add_context(*args, **kwargs):
691690
jsonResponse: Optional[str] = None
692691
try:
693692
if background is not None:
694-
adapter = backends.backend.request_adapter()
693+
adapter = get_app().backend.request_adapter()
695694
if not (adapter and adapter.args.get("cacheKey")):
696695
return _setup_background_callback(
697696
kwargs,
@@ -763,7 +762,7 @@ async def async_add_context(*args, **kwargs):
763762

764763
try:
765764
if background is not None:
766-
adapter = backends.backend.request_adapter()
765+
adapter = get_app().backend.request_adapter()
767766
if not (adapter and adapter.args.get("cacheKey")):
768767
return _setup_background_callback(
769768
kwargs,

dash/_callback_context.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import typing
66

77
from . import exceptions
8-
from . import backends
8+
from ._get_app import get_app
99
from ._utils import AttributeDict, stringify_id
1010

1111

@@ -221,7 +221,7 @@ def record_timing(name, duration, description=None):
221221
:param description: A description of the resource.
222222
:type description: string or None
223223
"""
224-
request = backends.backend.request_adapter()
224+
request = get_app().backend.request_adapter()
225225
timing_information = getattr(request.context, "timing_information", {})
226226

227227
if name in timing_information:
@@ -252,7 +252,7 @@ def using_outputs_grouping(self):
252252
@property
253253
@has_context
254254
def timing_information(self):
255-
request = backends.backend.request_adapter()
255+
request = get_app().backend.request_adapter()
256256
return getattr(request.context, "timing_information", {})
257257

258258
@has_context

dash/_get_app.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from textwrap import dedent
55
from typing import Any, Optional
66

7+
from dash.exceptions import AppNotFoundError
8+
79
APP: Optional[Any] = None
810

911
app_context: ContextVar[Any] = ContextVar("dash_app_context")
@@ -55,7 +57,7 @@ def get_app():
5557
pass
5658

5759
if APP is None:
58-
raise Exception(
60+
raise AppNotFoundError(
5961
dedent(
6062
"""
6163
App object is not yet defined. `app = dash.Dash()` needs to be run

dash/_validate.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .development.base_component import Component
1010
from . import backends
1111
from . import exceptions
12+
from ._get_app import get_app
1213
from ._utils import (
1314
patch_collections_abc,
1415
stringify_id,
@@ -510,13 +511,17 @@ def validate_use_pages(config):
510511
"`dash.register_page()` must be called after app instantiation"
511512
)
512513

513-
if backends.backend.has_request_context():
514-
raise exceptions.PageError(
515-
"""
516-
dash.register_page() can’t be called within a callback as it updates dash.page_registry, which is a global variable.
517-
For more details, see https://dash.plotly.com/sharing-data-between-callbacks#why-global-variables-will-break-your-app
518-
"""
519-
)
514+
try:
515+
if get_app().backend.has_request_context():
516+
raise exceptions.PageError(
517+
"""
518+
dash.register_page() can’t be called within a callback as it updates dash.page_registry, which is a global variable.
519+
For more details, see https://dash.plotly.com/sharing-data-between-callbacks#why-global-variables-will-break-your-app
520+
"""
521+
)
522+
except exceptions.AppNotFoundError:
523+
# If the app is not found we can add pages since before instantiation.
524+
pass
520525

521526

522527
def validate_module_name(module):

dash/backends/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
from .base_server import BaseDashServer
55

66

7-
backend: BaseDashServer
8-
9-
107
_backend_imports = {
118
"flask": ("dash.backends._flask", "FlaskDashServer"),
129
"fastapi": ("dash.backends._fastapi", "FastAPIDashServer"),
@@ -74,6 +71,5 @@ def get_server_type(server):
7471

7572
__all__ = [
7673
"get_backend",
77-
"backend",
7874
"get_server_type",
7975
]

dash/dash.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -501,14 +501,10 @@ def __init__( # pylint: disable=too-many-statements, too-many-branches
501501

502502
self.backend = backend_cls(server)
503503
self.server = server
504-
backends.backend = self.backend # type: ignore
505-
backends.request_adapter = self.backend.request_adapter # type: ignore
506504
else:
507505
# No server instance provided, create backend and let backend create server
508506
self.server = backend_cls.create_app(caller_name) # type: ignore
509507
self.backend = backend_cls(self.server)
510-
backends.backend = self.backend
511-
backends.request_adapter = self.backend.request_adapter # type: ignore
512508

513509
base_prefix, routes_prefix, requests_prefix = pathname_configs(
514510
url_base_pathname, routes_pathname_prefix, requests_pathname_prefix
@@ -641,6 +637,7 @@ def __init__( # pylint: disable=too-many-statements, too-many-branches
641637
self._got_first_request = {"pages": False, "setup_server": False}
642638

643639
if server:
640+
print(f"init app from server {server}")
644641
self.init_app()
645642

646643
self.logger.setLevel(logging.INFO)
@@ -1179,7 +1176,7 @@ def index(self, *_args, **_kwargs):
11791176
renderer = self._generate_renderer()
11801177
title = self.title
11811178
# Refactored: direct access to global request adapter
1182-
request = backends.backend.request_adapter()
1179+
request = self.backend.request_adapter()
11831180

11841181
if self.use_pages and self.config.include_pages_meta and request:
11851182
metas = _page_meta_tags(self, request) + metas
@@ -1395,7 +1392,7 @@ def _inputs_to_vals(self, inputs):
13951392
# pylint: disable=R0915
13961393
def _initialize_context(self, body):
13971394
"""Initialize the global context for the request."""
1398-
adapter = backends.backend.request_adapter()
1395+
adapter = self.backend.request_adapter()
13991396
g = AttributeDict({})
14001397
g.inputs_list = body.get("inputs", [])
14011398
g.states_list = body.get("state", [])
@@ -2382,7 +2379,7 @@ def verify_url_part(served_part, url_part, part_name):
23822379
server_url=jupyter_server_url,
23832380
)
23842381
else:
2385-
backends.backend.run(
2382+
self.backend.run(
23862383
dash_app=self, host=host, port=port, debug=debug, **flask_run_options
23872384
)
23882385

dash/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,7 @@ class ImportedInsideCallbackError(DashException):
109109

110110
class HookError(DashException):
111111
pass
112+
113+
114+
class AppNotFoundError(DashException):
115+
pass

0 commit comments

Comments
 (0)