refactor lazy loading
Remove the `--eager-loading/--lazy-loading` options and the `DispatchingApp` middleware. The `run` command handles loading exceptions directly. The reloader always prints out tracebacks immediately and always defers raising the error.
This commit is contained in:
parent
095651be9e
commit
5d8e35653f
6 changed files with 31 additions and 118 deletions
106
src/flask/cli.py
106
src/flask/cli.py
|
|
@ -10,8 +10,6 @@ import traceback
|
|||
import typing as t
|
||||
from functools import update_wrapper
|
||||
from operator import attrgetter
|
||||
from threading import Lock
|
||||
from threading import Thread
|
||||
|
||||
import click
|
||||
from click.core import ParameterSource
|
||||
|
|
@ -267,74 +265,6 @@ version_option = click.Option(
|
|||
)
|
||||
|
||||
|
||||
class DispatchingApp:
|
||||
"""Special application that dispatches to a Flask application which
|
||||
is imported by name in a background thread. If an error happens
|
||||
it is recorded and shown as part of the WSGI handling which in case
|
||||
of the Werkzeug debugger means that it shows up in the browser.
|
||||
"""
|
||||
|
||||
def __init__(self, loader, use_eager_loading=None):
|
||||
self.loader = loader
|
||||
self._app = None
|
||||
self._lock = Lock()
|
||||
self._bg_loading_exc = None
|
||||
|
||||
if use_eager_loading is None:
|
||||
use_eager_loading = not is_running_from_reloader()
|
||||
|
||||
if use_eager_loading:
|
||||
self._load_unlocked()
|
||||
else:
|
||||
self._load_in_background()
|
||||
|
||||
def _load_in_background(self):
|
||||
# Store the Click context and push it in the loader thread so
|
||||
# script_info is still available.
|
||||
ctx = click.get_current_context(silent=True)
|
||||
|
||||
def _load_app():
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
|
||||
with self._lock:
|
||||
if ctx is not None:
|
||||
click.globals.push_context(ctx)
|
||||
|
||||
try:
|
||||
self._load_unlocked()
|
||||
except Exception as e:
|
||||
self._bg_loading_exc = e
|
||||
|
||||
t = Thread(target=_load_app, args=())
|
||||
t.start()
|
||||
|
||||
def _flush_bg_loading_exception(self):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
exc = self._bg_loading_exc
|
||||
|
||||
if exc is not None:
|
||||
self._bg_loading_exc = None
|
||||
raise exc
|
||||
|
||||
def _load_unlocked(self):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
self._app = rv = self.loader()
|
||||
self._bg_loading_exc = None
|
||||
return rv
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
if self._app is not None:
|
||||
return self._app(environ, start_response)
|
||||
self._flush_bg_loading_exception()
|
||||
with self._lock:
|
||||
if self._app is not None:
|
||||
rv = self._app
|
||||
else:
|
||||
rv = self._load_unlocked()
|
||||
return rv(environ, start_response)
|
||||
|
||||
|
||||
class ScriptInfo:
|
||||
"""Helper object to deal with Flask applications. This is usually not
|
||||
necessary to interface with as it's used internally in the dispatching
|
||||
|
|
@ -811,7 +741,7 @@ def load_dotenv(path: str | os.PathLike | None = None) -> bool:
|
|||
return loaded # True if at least one file was located and loaded.
|
||||
|
||||
|
||||
def show_server_banner(env, debug, app_import_path, eager_loading):
|
||||
def show_server_banner(env, debug, app_import_path):
|
||||
"""Show extra startup messages the first time the server is run,
|
||||
ignoring the reloader.
|
||||
"""
|
||||
|
|
@ -819,12 +749,7 @@ def show_server_banner(env, debug, app_import_path, eager_loading):
|
|||
return
|
||||
|
||||
if app_import_path is not None:
|
||||
message = f" * Serving Flask app {app_import_path!r}"
|
||||
|
||||
if not eager_loading:
|
||||
message += " (lazy loading)"
|
||||
|
||||
click.echo(message)
|
||||
click.echo(f" * Serving Flask app '{app_import_path}'")
|
||||
|
||||
click.echo(f" * Environment: {env}")
|
||||
|
||||
|
|
@ -963,12 +888,6 @@ class SeparatedPathType(click.Path):
|
|||
help="Enable or disable the debugger. By default the debugger "
|
||||
"is active if debug is enabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--eager-loading/--lazy-loading",
|
||||
default=None,
|
||||
help="Enable or disable eager loading. By default eager "
|
||||
"loading is enabled if the reloader is disabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--with-threads/--without-threads",
|
||||
default=True,
|
||||
|
|
@ -1000,7 +919,6 @@ def run_command(
|
|||
port,
|
||||
reload,
|
||||
debugger,
|
||||
eager_loading,
|
||||
with_threads,
|
||||
cert,
|
||||
extra_files,
|
||||
|
|
@ -1014,7 +932,23 @@ def run_command(
|
|||
The reloader and debugger are enabled by default with the
|
||||
'--env development' or '--debug' options.
|
||||
"""
|
||||
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
|
||||
try:
|
||||
app = info.load_app()
|
||||
except Exception as e:
|
||||
if is_running_from_reloader():
|
||||
# When reloading, print out the error immediately, but raise
|
||||
# it later so the debugger or server can handle it.
|
||||
traceback.print_exc()
|
||||
err = e
|
||||
|
||||
def app(environ, start_response):
|
||||
raise err from None
|
||||
|
||||
else:
|
||||
# When not reloading, raise the error immediately so the
|
||||
# command fails.
|
||||
raise e from None
|
||||
|
||||
debug = get_debug_flag()
|
||||
|
||||
if reload is None:
|
||||
|
|
@ -1023,7 +957,7 @@ def run_command(
|
|||
if debugger is None:
|
||||
debugger = debug
|
||||
|
||||
show_server_banner(get_env(), debug, info.app_import_path, eager_loading)
|
||||
show_server_banner(get_env(), debug, info.app_import_path)
|
||||
|
||||
from werkzeug.serving import run_simple
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue