From 41016c89fd87345c15c61db89a779aca89f807f4 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Mon, 4 Sep 2023 12:28:34 +0200 Subject: [PATCH 1/5] Fix #1946 --- sentry_sdk/integrations/falcon.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 1bb79428f1..6d1b8f0f34 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -100,7 +100,7 @@ def json(self): class SentryFalconMiddleware(object): """Captures exceptions in Falcon requests and send to Sentry""" - def process_request(self, req, resp, *args, **kwargs): + async def process_request(self, req, resp, *args, **kwargs): # type: (Any, Any, *Any, **Any) -> None hub = Hub.current integration = hub.get_integration(FalconIntegration) @@ -205,15 +205,13 @@ def _patch_prepare_middleware(): # type: () -> None original_prepare_middleware = falcon_helpers.prepare_middleware - def sentry_patched_prepare_middleware( - middleware=None, independent_middleware=False - ): - # type: (Any, Any) -> Any + def sentry_patched_prepare_middleware(middleware=None, *args, **kwargs): + # type: (Any, *Any, **Any) -> Any hub = Hub.current integration = hub.get_integration(FalconIntegration) if integration is not None: middleware = [SentryFalconMiddleware()] + (middleware or []) - return original_prepare_middleware(middleware, independent_middleware) + return original_prepare_middleware(middleware, *args, **kwargs) falcon_helpers.prepare_middleware = sentry_patched_prepare_middleware From 7ef5e329b37442f88ce3bad1bda120c54e2320b7 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Mon, 4 Sep 2023 16:45:23 +0200 Subject: [PATCH 2/5] Separate process_request_async function --- sentry_sdk/integrations/falcon.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 6d1b8f0f34..c5a18a1e07 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -100,7 +100,7 @@ def json(self): class SentryFalconMiddleware(object): """Captures exceptions in Falcon requests and send to Sentry""" - async def process_request(self, req, resp, *args, **kwargs): + def process_request(self, req, resp, *args, **kwargs): # type: (Any, Any, *Any, **Any) -> None hub = Hub.current integration = hub.get_integration(FalconIntegration) @@ -111,6 +111,10 @@ async def process_request(self, req, resp, *args, **kwargs): scope._name = "falcon" scope.add_event_processor(_make_request_event_processor(req, integration)) + async def process_request_async(self, req, resp, *args, **kwargs): + # type: (Any, Any, *Any, **Any) -> None + self.process_request(req, resp, *args, **kwargs) + TRANSACTION_STYLE_VALUES = ("uri_template", "path") From a6f8b3a2fa892f8a1fa68112ce5db0ba611c9044 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Tue, 5 Sep 2023 16:24:12 +0200 Subject: [PATCH 3/5] Fix Python 2.7 support --- sentry_sdk/integrations/_falcon_async_py3.py | 23 ++++++++++++++++++++ sentry_sdk/integrations/falcon.py | 15 ++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 sentry_sdk/integrations/_falcon_async_py3.py diff --git a/sentry_sdk/integrations/_falcon_async_py3.py b/sentry_sdk/integrations/_falcon_async_py3.py new file mode 100644 index 0000000000..99d41bbf63 --- /dev/null +++ b/sentry_sdk/integrations/_falcon_async_py3.py @@ -0,0 +1,23 @@ +""" +This file was created so we could have a process_request_async method on our +SentryFalconMiddleware class, so we could properly support ASGI with Falcon. +However, since process_request_async needs to be an async function, we cannot +define it in Python 2.7, as this produces a syntax error. + +So, we have split the process_request_async implementation into this separate +file, which can only be loaded in Python 3 and above. +""" + +from abc import ABC, abstractmethod +from typing import Any + + +class SentryFalconMiddlewareBase(ABC): + @abstractmethod + def process_request(self, req: Any, resp: Any, *args: Any, **kwargs: Any) -> None: + pass + + async def process_request_async( + self, req: Any, resp: Any, *args: Any, **kwargs: Any + ) -> None: + self.process_request(req, resp, *args, **kwargs) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index c5a18a1e07..06bf6765ca 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -13,6 +13,15 @@ from sentry_sdk._types import TYPE_CHECKING +from sentry_sdk._compat import PY2 + +if PY2: + SentryFalconMiddlewareBase = object +else: + # Support ASGI with a process_request_async method, which can only be loaded in Python 3, + # since async methods are invalid syntax in Python 2.7. + from sentry_sdk.integrations._falcon_async_py3 import SentryFalconMiddlewareBase + if TYPE_CHECKING: from typing import Any from typing import Dict @@ -97,7 +106,7 @@ def json(self): return self.request._media -class SentryFalconMiddleware(object): +class SentryFalconMiddleware(SentryFalconMiddlewareBase): """Captures exceptions in Falcon requests and send to Sentry""" def process_request(self, req, resp, *args, **kwargs): @@ -111,10 +120,6 @@ def process_request(self, req, resp, *args, **kwargs): scope._name = "falcon" scope.add_event_processor(_make_request_event_processor(req, integration)) - async def process_request_async(self, req, resp, *args, **kwargs): - # type: (Any, Any, *Any, **Any) -> None - self.process_request(req, resp, *args, **kwargs) - TRANSACTION_STYLE_VALUES = ("uri_template", "path") From 0c02124cc958fd47b79243f8f47c926b713d5ade Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Tue, 5 Sep 2023 17:23:46 +0200 Subject: [PATCH 4/5] Removed abstract class --- sentry_sdk/integrations/_falcon_async_py3.py | 17 ++++++++++++++--- sentry_sdk/integrations/falcon.py | 6 +++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/integrations/_falcon_async_py3.py b/sentry_sdk/integrations/_falcon_async_py3.py index 99d41bbf63..188d93e618 100644 --- a/sentry_sdk/integrations/_falcon_async_py3.py +++ b/sentry_sdk/integrations/_falcon_async_py3.py @@ -6,15 +6,26 @@ So, we have split the process_request_async implementation into this separate file, which can only be loaded in Python 3 and above. + +TODO: If we ever remove Python 2.7 support, we should delete this file and move +the process_request_async implementation to the SentryFalconMiddleware class +in the falcon.py file. """ -from abc import ABC, abstractmethod from typing import Any -class SentryFalconMiddlewareBase(ABC): - @abstractmethod +class _SentryFalconMiddlewareBase: + """ + The SentryFalconMiddleware should inherit from this class in Python 3. The only + purpose of this class is to provide an implementation of process_request_async which + simply calls process_request. + """ + def process_request(self, req: Any, resp: Any, *args: Any, **kwargs: Any) -> None: + """ + SentryFalconMiddleware will override this method and provide the actual implementation. + """ pass async def process_request_async( diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 06bf6765ca..f26d198671 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -16,11 +16,11 @@ from sentry_sdk._compat import PY2 if PY2: - SentryFalconMiddlewareBase = object + _SentryFalconMiddlewareBase = object else: # Support ASGI with a process_request_async method, which can only be loaded in Python 3, # since async methods are invalid syntax in Python 2.7. - from sentry_sdk.integrations._falcon_async_py3 import SentryFalconMiddlewareBase + from sentry_sdk.integrations._falcon_async_py3 import _SentryFalconMiddlewareBase if TYPE_CHECKING: from typing import Any @@ -106,7 +106,7 @@ def json(self): return self.request._media -class SentryFalconMiddleware(SentryFalconMiddlewareBase): +class SentryFalconMiddleware(_SentryFalconMiddlewareBase): """Captures exceptions in Falcon requests and send to Sentry""" def process_request(self, req, resp, *args, **kwargs): From 08fa03759dc2b120e38aee6cce9497d90c2a3073 Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Wed, 6 Sep 2023 12:27:08 +0200 Subject: [PATCH 5/5] Run all Falcon tests also with ASGI apps --- tests/integrations/falcon/test_falcon.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/integrations/falcon/test_falcon.py b/tests/integrations/falcon/test_falcon.py index dd7aa80dfe..fe014b8687 100644 --- a/tests/integrations/falcon/test_falcon.py +++ b/tests/integrations/falcon/test_falcon.py @@ -12,9 +12,20 @@ from sentry_sdk.integrations.falcon import FalconIntegration from sentry_sdk.integrations.logging import LoggingIntegration +try: + import falcon.asgi +except ImportError: + # Only test WSGI app + APP_CONSTRUCTORS = [falcon.API] +else: + # Test ASGI and WSGI apps + APP_CONSTRUCTORS = [falcon.App, falcon.asgi.App] + + +@pytest.fixture(params=APP_CONSTRUCTORS) +def make_app(request, sentry_init): + app_constructor = request.param -@pytest.fixture -def make_app(sentry_init): def inner(): class MessageResource: def on_get(self, req, resp): @@ -26,7 +37,7 @@ def on_get(self, req, resp, message_id): sentry_sdk.capture_message("hi") resp.media = "hi" - app = falcon.API() + app = app_constructor() app.add_route("/message", MessageResource()) app.add_route("/message/{message_id:int}", MessageByIdResource())