diff --git a/CHANGES.rst b/CHANGES.rst index 44512451..dc3f5cbf 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -17,6 +17,8 @@ Unreleased :issue:`4150` - ``jsonify`` handles ``decimal.Decimal`` by encoding to ``str``. :issue:`4157` +- Correctly handle raising deferred errors in CLI lazy loading. + :issue:`4096` Version 2.0.1 diff --git a/src/flask/cli.py b/src/flask/cli.py index d9e810da..81191a1a 100644 --- a/src/flask/cli.py +++ b/src/flask/cli.py @@ -312,7 +312,7 @@ class DispatchingApp: self.loader = loader self._app = None self._lock = Lock() - self._bg_loading_exc_info = None + self._bg_loading_exc = None if use_eager_loading is None: use_eager_loading = os.environ.get("WERKZEUG_RUN_MAIN") != "true" @@ -328,23 +328,24 @@ class DispatchingApp: with self._lock: try: self._load_unlocked() - except Exception: - self._bg_loading_exc_info = sys.exc_info() + 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_info = self._bg_loading_exc_info - if exc_info is not None: - self._bg_loading_exc_info = None - raise exc_info + 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_info = None + self._bg_loading_exc = None return rv def __call__(self, environ, start_response): diff --git a/tests/test_cli.py b/tests/test_cli.py index ebf8d1f5..5a666d8a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -17,6 +17,7 @@ from flask import Blueprint from flask import current_app from flask import Flask from flask.cli import AppGroup +from flask.cli import DispatchingApp from flask.cli import dotenv from flask.cli import find_best_app from flask.cli import FlaskGroup @@ -310,6 +311,23 @@ def test_scriptinfo(test_apps, monkeypatch): assert app.name == "testapp" +def test_lazy_load_error(monkeypatch): + """When using lazy loading, the correct exception should be + re-raised. + """ + + class BadExc(Exception): + pass + + def bad_load(): + raise BadExc + + lazy = DispatchingApp(bad_load, use_eager_loading=False) + + with pytest.raises(BadExc): + lazy._flush_bg_loading_exception() + + def test_with_appcontext(runner): @click.command() @with_appcontext