Skip to content

Commit 70089c1

Browse files
ahmedetefysentry-bot
and
sentry-bot
authored
fix(django): Fix middleware issue not handling async middleware functions (#1016)
* Added a test middleware function * Added test that ensures __acall__ handles middleware functions correctly not only classes * Added logic that handles the case where a middleware is a function rather a class * fix: Formatting * FIxing Mypy type errors Co-authored-by: sentry-bot <[email protected]>
1 parent 1457c4a commit 70089c1

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

sentry_sdk/integrations/django/asgi.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ def _asgi_middleware_mixin_factory(_check_middleware_span):
104104
"""
105105

106106
class SentryASGIMixin:
107+
if MYPY:
108+
_inner = None
109+
107110
def __init__(self, get_response):
108111
# type: (Callable[..., Any]) -> None
109112
self.get_response = get_response
@@ -132,7 +135,10 @@ async def __acall__(self, *args, **kwargs):
132135
# type: (*Any, **Any) -> Any
133136
f = self._acall_method
134137
if f is None:
135-
self._acall_method = f = self._inner.__acall__ # type: ignore
138+
if hasattr(self._inner, "__acall__"):
139+
self._acall_method = f = self._inner.__acall__ # type: ignore
140+
else:
141+
self._acall_method = f = self._inner
136142

137143
middleware_span = _check_middleware_span(old_method=f)
138144

tests/integrations/django/asgi/test_asgi.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,43 @@ async def test_async_views_concurrent_execution(sentry_init, capture_events, set
103103
assert end - start < 1.5
104104

105105

106+
@pytest.mark.asyncio
107+
@pytest.mark.skipif(
108+
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
109+
)
110+
async def test_async_middleware_that_is_function_concurrent_execution(
111+
sentry_init, capture_events, settings
112+
):
113+
import asyncio
114+
import time
115+
116+
settings.MIDDLEWARE = [
117+
"tests.integrations.django.myapp.middleware.simple_middleware"
118+
]
119+
asgi_application.load_middleware(is_async=True)
120+
121+
sentry_init(integrations=[DjangoIntegration()], send_default_pii=True)
122+
123+
comm = HttpCommunicator(asgi_application, "GET", "/my_async_view")
124+
comm2 = HttpCommunicator(asgi_application, "GET", "/my_async_view")
125+
126+
loop = asyncio.get_event_loop()
127+
128+
start = time.time()
129+
130+
r1 = loop.create_task(comm.get_response(timeout=5))
131+
r2 = loop.create_task(comm2.get_response(timeout=5))
132+
133+
(resp1, resp2), _ = await asyncio.wait({r1, r2})
134+
135+
end = time.time()
136+
137+
assert resp1.result()["status"] == 200
138+
assert resp2.result()["status"] == 200
139+
140+
assert end - start < 1.5
141+
142+
106143
@pytest.mark.asyncio
107144
@pytest.mark.skipif(
108145
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
from django.utils.decorators import sync_and_async_middleware
3+
4+
5+
@sync_and_async_middleware
6+
def simple_middleware(get_response):
7+
if asyncio.iscoroutinefunction(get_response):
8+
9+
async def middleware(request):
10+
response = await get_response(request)
11+
return response
12+
13+
else:
14+
15+
def middleware(request):
16+
response = get_response(request)
17+
return response
18+
19+
return middleware

0 commit comments

Comments
 (0)