From 47b88db6af0a70ccdd9f593b1579bd4d6a8a2784 Mon Sep 17 00:00:00 2001 From: Lev Vereshchagin Date: Thu, 6 Feb 2025 17:03:10 +0300 Subject: [PATCH 1/4] Update litestar integration --- sentry_sdk/integrations/litestar.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index 4b04dada8a..dde9f0637e 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -1,6 +1,7 @@ +from collections.abc import Set import sentry_sdk from sentry_sdk.consts import OP -from sentry_sdk.integrations import DidNotEnable, Integration +from sentry_sdk.integrations import _DEFAULT_FAILED_REQUEST_STATUS_CODES, DidNotEnable, Integration from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.integrations.logging import ignore_logger from sentry_sdk.scope import should_send_default_pii @@ -17,6 +18,7 @@ from litestar.middleware import DefineMiddleware # type: ignore from litestar.routes.http import HTTPRoute # type: ignore from litestar.data_extractors import ConnectionDataExtractor # type: ignore + from litestar.exceptions import HTTPException # type: ignore except ImportError: raise DidNotEnable("Litestar is not installed") @@ -45,6 +47,11 @@ class LitestarIntegration(Integration): identifier = "litestar" origin = f"auto.http.{identifier}" + def __init__(self, + failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] + ) -> None: + self.failed_request_status_codes = failed_request_status_codes + @staticmethod def setup_once(): # type: () -> None @@ -277,6 +284,13 @@ def exception_handler(exc, scope): sentry_scope = sentry_sdk.get_isolation_scope() sentry_scope.set_user(user_info) + if isinstance(exc, HTTPException): + integration = sentry_sdk.get_client().get_integration( + LitestarIntegration + ) + if integration is not None and exc.status_code not in integration.failed_request_status_codes: + return + event, hint = event_from_exception( exc, client_options=sentry_sdk.get_client().options, From 6e4f3c739170d6e9d3b8d7f4efeb715e75182b31 Mon Sep 17 00:00:00 2001 From: Lev Vereshchagin Date: Thu, 6 Feb 2025 17:10:12 +0300 Subject: [PATCH 2/4] Add tests --- tests/integrations/litestar/test_litestar.py | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/integrations/litestar/test_litestar.py b/tests/integrations/litestar/test_litestar.py index 90346537a7..e82df069f6 100644 --- a/tests/integrations/litestar/test_litestar.py +++ b/tests/integrations/litestar/test_litestar.py @@ -1,6 +1,7 @@ from __future__ import annotations import functools +from litestar.exceptions import HTTPException import pytest from sentry_sdk import capture_message @@ -16,6 +17,8 @@ from litestar.middleware.session.server_side import ServerSideSessionConfig from litestar.testing import TestClient +from tests.integrations.starlette import test_starlette + def litestar_app_factory(middleware=None, debug=True, exception_handlers=None): class MyController(Controller): @@ -396,3 +399,25 @@ async def __call__(self, scope, receive, send): } else: assert "user" not in event +@test_starlette.parametrize_test_configurable_status_codes +def test_configurable_status_codes( + sentry_init, + capture_events, + failed_request_status_codes, + status_code, + expected_error, +): + integration_kwargs = {"failed_request_status_codes": failed_request_status_codes} if failed_request_status_codes is not None else {} + sentry_init(integrations=[LitestarIntegration(**integration_kwargs)]) + + events = capture_events() + + @get("/error") + async def error()-> None: + raise HTTPException(status_code=status_code) + + app = Litestar([error]) + client = TestClient(app) + client.get("/error") + + assert len(events) == int(expected_error) From 9b0c862fda2c1fae56196fab8c398b1c59806d44 Mon Sep 17 00:00:00 2001 From: Lev Vereshchagin Date: Thu, 6 Feb 2025 17:13:08 +0300 Subject: [PATCH 3/4] Move parametrization to conftest --- tests/integrations/conftest.py | 21 +++++++++++++++++ tests/integrations/fastapi/test_fastapi.py | 3 ++- tests/integrations/litestar/test_litestar.py | 4 ++-- .../integrations/starlette/test_starlette.py | 23 ++----------------- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/tests/integrations/conftest.py b/tests/integrations/conftest.py index 560155e2b5..7ac43b0efe 100644 --- a/tests/integrations/conftest.py +++ b/tests/integrations/conftest.py @@ -32,3 +32,24 @@ def capture_event_scope(self, event, hint=None, scope=None): return errors return inner + + +parametrize_test_configurable_status_codes = pytest.mark.parametrize( + ("failed_request_status_codes", "status_code", "expected_error"), + ( + (None, 500, True), + (None, 400, False), + ({500, 501}, 500, True), + ({500, 501}, 401, False), + ({*range(400, 500)}, 401, True), + ({*range(400, 500)}, 500, False), + ({*range(400, 600)}, 300, False), + ({*range(400, 600)}, 403, True), + ({*range(400, 600)}, 503, True), + ({*range(400, 403), 500, 501}, 401, True), + ({*range(400, 403), 500, 501}, 405, False), + ({*range(400, 403), 500, 501}, 501, True), + ({*range(400, 403), 500, 501}, 503, False), + (set(), 500, False), + ), +) diff --git a/tests/integrations/fastapi/test_fastapi.py b/tests/integrations/fastapi/test_fastapi.py index 97aea06344..f1c0a69305 100644 --- a/tests/integrations/fastapi/test_fastapi.py +++ b/tests/integrations/fastapi/test_fastapi.py @@ -19,6 +19,7 @@ FASTAPI_VERSION = parse_version(fastapi.__version__) +from tests.integrations.conftest import parametrize_test_configurable_status_codes from tests.integrations.starlette import test_starlette @@ -650,7 +651,7 @@ def test_transaction_http_method_custom(sentry_init, capture_events): assert event2["request"]["method"] == "HEAD" -@test_starlette.parametrize_test_configurable_status_codes +@parametrize_test_configurable_status_codes def test_configurable_status_codes( sentry_init, capture_events, diff --git a/tests/integrations/litestar/test_litestar.py b/tests/integrations/litestar/test_litestar.py index e82df069f6..14de512935 100644 --- a/tests/integrations/litestar/test_litestar.py +++ b/tests/integrations/litestar/test_litestar.py @@ -17,7 +17,7 @@ from litestar.middleware.session.server_side import ServerSideSessionConfig from litestar.testing import TestClient -from tests.integrations.starlette import test_starlette +from tests.integrations.conftest import parametrize_test_configurable_status_codes def litestar_app_factory(middleware=None, debug=True, exception_handlers=None): @@ -399,7 +399,7 @@ async def __call__(self, scope, receive, send): } else: assert "user" not in event -@test_starlette.parametrize_test_configurable_status_codes +@parametrize_test_configurable_status_codes def test_configurable_status_codes( sentry_init, capture_events, diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index fd47895f5a..93da0420aa 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -32,6 +32,8 @@ from starlette.middleware.trustedhost import TrustedHostMiddleware from starlette.testclient import TestClient +from tests.integrations.conftest import parametrize_test_configurable_status_codes + STARLETTE_VERSION = parse_version(starlette.__version__) @@ -1298,27 +1300,6 @@ def test_transaction_http_method_custom(sentry_init, capture_events): assert event2["request"]["method"] == "HEAD" -parametrize_test_configurable_status_codes = pytest.mark.parametrize( - ("failed_request_status_codes", "status_code", "expected_error"), - ( - (None, 500, True), - (None, 400, False), - ({500, 501}, 500, True), - ({500, 501}, 401, False), - ({*range(400, 500)}, 401, True), - ({*range(400, 500)}, 500, False), - ({*range(400, 600)}, 300, False), - ({*range(400, 600)}, 403, True), - ({*range(400, 600)}, 503, True), - ({*range(400, 403), 500, 501}, 401, True), - ({*range(400, 403), 500, 501}, 405, False), - ({*range(400, 403), 500, 501}, 501, True), - ({*range(400, 403), 500, 501}, 503, False), - (set(), 500, False), - ), -) - - @parametrize_test_configurable_status_codes def test_configurable_status_codes( sentry_init, From 7bdfa5015580621580f87f016480a594c25deeb0 Mon Sep 17 00:00:00 2001 From: Lev Vereshchagin Date: Thu, 6 Feb 2025 17:15:55 +0300 Subject: [PATCH 4/4] Fix formatting --- sentry_sdk/integrations/litestar.py | 24 ++++++++++++-------- tests/integrations/litestar/test_litestar.py | 10 ++++++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index dde9f0637e..841c8a5cce 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -1,7 +1,11 @@ from collections.abc import Set import sentry_sdk from sentry_sdk.consts import OP -from sentry_sdk.integrations import _DEFAULT_FAILED_REQUEST_STATUS_CODES, DidNotEnable, Integration +from sentry_sdk.integrations import ( + _DEFAULT_FAILED_REQUEST_STATUS_CODES, + DidNotEnable, + Integration, +) from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.integrations.logging import ignore_logger from sentry_sdk.scope import should_send_default_pii @@ -47,9 +51,10 @@ class LitestarIntegration(Integration): identifier = "litestar" origin = f"auto.http.{identifier}" - def __init__(self, - failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] - ) -> None: + def __init__( + self, + failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int] + ) -> None: self.failed_request_status_codes = failed_request_status_codes @staticmethod @@ -285,11 +290,12 @@ def exception_handler(exc, scope): sentry_scope.set_user(user_info) if isinstance(exc, HTTPException): - integration = sentry_sdk.get_client().get_integration( - LitestarIntegration - ) - if integration is not None and exc.status_code not in integration.failed_request_status_codes: - return + integration = sentry_sdk.get_client().get_integration(LitestarIntegration) + if ( + integration is not None + and exc.status_code not in integration.failed_request_status_codes + ): + return event, hint = event_from_exception( exc, diff --git a/tests/integrations/litestar/test_litestar.py b/tests/integrations/litestar/test_litestar.py index 14de512935..4f642479e4 100644 --- a/tests/integrations/litestar/test_litestar.py +++ b/tests/integrations/litestar/test_litestar.py @@ -399,6 +399,8 @@ async def __call__(self, scope, receive, send): } else: assert "user" not in event + + @parametrize_test_configurable_status_codes def test_configurable_status_codes( sentry_init, @@ -407,13 +409,17 @@ def test_configurable_status_codes( status_code, expected_error, ): - integration_kwargs = {"failed_request_status_codes": failed_request_status_codes} if failed_request_status_codes is not None else {} + integration_kwargs = ( + {"failed_request_status_codes": failed_request_status_codes} + if failed_request_status_codes is not None + else {} + ) sentry_init(integrations=[LitestarIntegration(**integration_kwargs)]) events = capture_events() @get("/error") - async def error()-> None: + async def error() -> None: raise HTTPException(status_code=status_code) app = Litestar([error])