11from typing import TYPE_CHECKING , cast
2- from posthog import contexts , capture_exception
2+ from posthog import contexts
33from posthog .client import Client
44
5+ try :
6+ from asgiref .sync import iscoroutinefunction
7+ except ImportError :
8+ # Fallback for older Django versions
9+ import asyncio
10+
11+ iscoroutinefunction = asyncio .iscoroutinefunction
12+
513if TYPE_CHECKING :
614 from django .http import HttpRequest , HttpResponse # noqa: F401
715 from typing import Callable , Dict , Any , Optional # noqa: F401
@@ -33,9 +41,14 @@ class PosthogContextMiddleware:
3341 frontend. See the documentation for `set_context_session` and `identify_context` for more details.
3442 """
3543
44+ # Django middleware capability flags
45+ sync_capable = True
46+ async_capable = True
47+
3648 def __init__ (self , get_response ):
3749 # type: (Callable[[HttpRequest], HttpResponse]) -> None
3850 self .get_response = get_response
51+ self ._is_coroutine = iscoroutinefunction (get_response )
3952
4053 from django .conf import settings
4154
@@ -159,6 +172,13 @@ def extract_request_user(self, request):
159172
160173 def __call__ (self , request ):
161174 # type: (HttpRequest) -> HttpResponse
175+ # Purely defensive around django's internal sync/async handling - this should be unreachable, but if it's reached, we may
176+ # as well return something semi-meaningful
177+ if self ._is_coroutine :
178+ raise RuntimeError (
179+ "PosthogContextMiddleware received sync call but get_response is async"
180+ )
181+
162182 if self .request_filter and not self .request_filter (request ):
163183 return self .get_response (request )
164184
@@ -168,14 +188,19 @@ def __call__(self, request):
168188
169189 return self .get_response (request )
170190
171- def process_exception (self , request , exception ):
191+ async def __acall__ (self , request ):
192+ # type: (HttpRequest) -> HttpResponse
172193 if self .request_filter and not self .request_filter (request ):
173- return
194+ if self ._is_coroutine :
195+ return await self .get_response (request ) # type: ignore
196+ else :
197+ return self .get_response (request ) # type: ignore
174198
175- if not self .capture_exceptions :
176- return
199+ with contexts .new_context (self .capture_exceptions , client = self .client ):
200+ for k , v in self .extract_tags (request ).items ():
201+ contexts .tag (k , v )
177202
178- if self .client :
179- self .client . capture_exception ( exception )
180- else :
181- capture_exception ( exception )
203+ if self ._is_coroutine :
204+ return await self .get_response ( request ) # type: ignore
205+ else :
206+ return self . get_response ( request ) # type: ignore
0 commit comments