From 8e63f767c5e3affed7483f67557fdaa425a77d8d Mon Sep 17 00:00:00 2001 From: Ahmed Etefy Date: Fri, 12 Feb 2021 13:33:29 +0100 Subject: [PATCH 1/5] Added a test middleware function --- tests/integrations/django/myapp/middleware.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/integrations/django/myapp/middleware.py diff --git a/tests/integrations/django/myapp/middleware.py b/tests/integrations/django/myapp/middleware.py new file mode 100644 index 0000000000..f70fd6eda3 --- /dev/null +++ b/tests/integrations/django/myapp/middleware.py @@ -0,0 +1,15 @@ +import asyncio +from django.utils.decorators import sync_and_async_middleware + + +@sync_and_async_middleware +def simple_middleware(get_response): + if asyncio.iscoroutinefunction(get_response): + async def middleware(request): + response = await get_response(request) + return response + else: + def middleware(request): + response = get_response(request) + return response + return middleware \ No newline at end of file From 539c148bbe09ca6b4aa7d7d374c08252e3255864 Mon Sep 17 00:00:00 2001 From: Ahmed Etefy Date: Fri, 12 Feb 2021 13:34:22 +0100 Subject: [PATCH 2/5] Added test that ensures __acall__ handles middleware functions correctly not only classes --- tests/integrations/django/asgi/test_asgi.py | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 920918415d..6238848874 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -103,6 +103,39 @@ async def test_async_views_concurrent_execution(sentry_init, capture_events, set assert end - start < 1.5 +@pytest.mark.asyncio +@pytest.mark.skipif( + django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" +) +async def test_async_middleware_that_is_function_concurrent_execution(sentry_init, capture_events, settings): + import asyncio + import time + + settings.MIDDLEWARE = ["tests.integrations.django.myapp.middleware.simple_middleware"] + asgi_application.load_middleware(is_async=True) + + sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) + + comm = HttpCommunicator(asgi_application, "GET", "/my_async_view") + comm2 = HttpCommunicator(asgi_application, "GET", "/my_async_view") + + loop = asyncio.get_event_loop() + + start = time.time() + + r1 = loop.create_task(comm.get_response(timeout=5)) + r2 = loop.create_task(comm2.get_response(timeout=5)) + + (resp1, resp2), _ = await asyncio.wait({r1, r2}) + + end = time.time() + + assert resp1.result()["status"] == 200 + assert resp2.result()["status"] == 200 + + assert end - start < 1.5 + + @pytest.mark.asyncio @pytest.mark.skipif( django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" From c8fdde6c2ddce8538548e51e024c9b415c3dacf7 Mon Sep 17 00:00:00 2001 From: Ahmed Etefy Date: Fri, 12 Feb 2021 13:34:53 +0100 Subject: [PATCH 3/5] Added logic that handles the case where a middleware is a function rather a class --- sentry_sdk/integrations/django/asgi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index b533a33e47..606290d3dd 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -132,7 +132,10 @@ async def __acall__(self, *args, **kwargs): # type: (*Any, **Any) -> Any f = self._acall_method if f is None: - self._acall_method = f = self._inner.__acall__ # type: ignore + if hasattr(self._inner, '__acall__'): + self._acall_method = f = self._inner.__acall__ # type: ignore + else: + self._acall_method = f = self._inner middleware_span = _check_middleware_span(old_method=f) From 716e3050fbdb109933fcf675a1a8b27c39041c56 Mon Sep 17 00:00:00 2001 From: sentry-bot Date: Fri, 12 Feb 2021 12:36:29 +0000 Subject: [PATCH 4/5] fix: Formatting --- sentry_sdk/integrations/django/asgi.py | 2 +- tests/integrations/django/asgi/test_asgi.py | 8 ++++++-- tests/integrations/django/myapp/middleware.py | 6 +++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index 606290d3dd..244479fd5a 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -132,7 +132,7 @@ async def __acall__(self, *args, **kwargs): # type: (*Any, **Any) -> Any f = self._acall_method if f is None: - if hasattr(self._inner, '__acall__'): + if hasattr(self._inner, "__acall__"): self._acall_method = f = self._inner.__acall__ # type: ignore else: self._acall_method = f = self._inner diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 6238848874..0e6dd4f9ff 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -107,11 +107,15 @@ async def test_async_views_concurrent_execution(sentry_init, capture_events, set @pytest.mark.skipif( django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1" ) -async def test_async_middleware_that_is_function_concurrent_execution(sentry_init, capture_events, settings): +async def test_async_middleware_that_is_function_concurrent_execution( + sentry_init, capture_events, settings +): import asyncio import time - settings.MIDDLEWARE = ["tests.integrations.django.myapp.middleware.simple_middleware"] + settings.MIDDLEWARE = [ + "tests.integrations.django.myapp.middleware.simple_middleware" + ] asgi_application.load_middleware(is_async=True) sentry_init(integrations=[DjangoIntegration()], send_default_pii=True) diff --git a/tests/integrations/django/myapp/middleware.py b/tests/integrations/django/myapp/middleware.py index f70fd6eda3..b4c1145390 100644 --- a/tests/integrations/django/myapp/middleware.py +++ b/tests/integrations/django/myapp/middleware.py @@ -5,11 +5,15 @@ @sync_and_async_middleware def simple_middleware(get_response): if asyncio.iscoroutinefunction(get_response): + async def middleware(request): response = await get_response(request) return response + else: + def middleware(request): response = get_response(request) return response - return middleware \ No newline at end of file + + return middleware From a2651addbb1d6a1848da12bc33570716c0b2041c Mon Sep 17 00:00:00 2001 From: Ahmed Etefy Date: Fri, 12 Feb 2021 13:45:42 +0100 Subject: [PATCH 5/5] FIxing Mypy type errors --- sentry_sdk/integrations/django/asgi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index 244479fd5a..79916e94fb 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -104,6 +104,9 @@ def _asgi_middleware_mixin_factory(_check_middleware_span): """ class SentryASGIMixin: + if MYPY: + _inner = None + def __init__(self, get_response): # type: (Callable[..., Any]) -> None self.get_response = get_response