From 1fd147b73aa3b39c3ee22440618698875a6a85c9 Mon Sep 17 00:00:00 2001 From: Peter Schutt Date: Mon, 6 May 2024 19:21:00 +1000 Subject: [PATCH] fix: catch errors from mounted app The mounted application will start a response if an exception is raised from within, and then re-raise. If the re-raised exception is allowed to propagate, a new response will be started by the litestar handler, causing an error. This PR catches any exception raised from within the mounted application and logs it. --- src/sqladmin_litestar_plugin/__init__.py | 8 ++++++-- tests/test_plugin.py | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sqladmin_litestar_plugin/__init__.py b/src/sqladmin_litestar_plugin/__init__.py index 0e8a8f5..8961443 100644 --- a/src/sqladmin_litestar_plugin/__init__.py +++ b/src/sqladmin_litestar_plugin/__init__.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING, Any import sqladmin @@ -25,6 +26,8 @@ __all__ = ("SQLAdminPlugin",) +logger = logging.getLogger(__name__) + class SQLAdminPlugin(InitPluginProtocol): def __init__( # noqa: PLR0913 @@ -90,8 +93,9 @@ async def wrapped_app(scope: Scope, receive: Receive, send: Send) -> None: app = scope["app"] try: await self.app(scope, receive, send) # type: ignore[arg-type] - finally: - scope["app"] = app + except Exception: + logger.exception("Error raised from SQLAdmin app") + scope["app"] = app app_config.route_handlers.append(wrapped_app) return app_config diff --git a/tests/test_plugin.py b/tests/test_plugin.py index db41f29..02cb135 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -40,15 +40,16 @@ def test_views_added_to_admin_app(plugin: SQLAdminPlugin, monkeypatch: pytest.Mo mock.assert_called_once_with(plugin.views[0]) +@pytest.mark.parametrize("should_raise", [True, False]) @pytest.mark.anyio() async def test_resets_app_in_scope( - plugin: SQLAdminPlugin, app: Litestar, monkeypatch: pytest.MonkeyPatch + *, should_raise: bool, plugin: SQLAdminPlugin, app: Litestar, monkeypatch: pytest.MonkeyPatch ) -> None: - mock = MagicMock() + mock = MagicMock(side_effect=RuntimeError if should_raise else None) async def fake_admin_app(scope: Scope, _: Receive, __: Send) -> None: # noqa: RUF029 - mock() scope["app"] = "admin" # type: ignore[arg-type] + mock() monkeypatch.setattr(plugin, "app", fake_admin_app) handler = app.route_handler_method_map["/"]["asgi"].fn