posthog 6.7.9__py3-none-any.whl → 6.7.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,13 +3,19 @@ from posthog import contexts
3
3
  from posthog.client import Client
4
4
 
5
5
  try:
6
- from asgiref.sync import iscoroutinefunction
6
+ from asgiref.sync import iscoroutinefunction, markcoroutinefunction
7
7
  except ImportError:
8
- # Fallback for older Django versions
8
+ # Fallback for older Django versions without asgiref
9
9
  import asyncio
10
10
 
11
11
  iscoroutinefunction = asyncio.iscoroutinefunction
12
12
 
13
+ # No-op fallback for markcoroutinefunction
14
+ # Older Django versions without asgiref typically don't support async middleware anyway
15
+ def markcoroutinefunction(func):
16
+ return func
17
+
18
+
13
19
  if TYPE_CHECKING:
14
20
  from django.http import HttpRequest, HttpResponse # noqa: F401
15
21
  from typing import Callable, Dict, Any, Optional, Union, Awaitable # noqa: F401
@@ -39,26 +45,24 @@ class PosthogContextMiddleware:
39
45
  See the context documentation for more information. The extracted distinct ID and session ID, if found, are used to
40
46
  associate all events captured in the middleware context with the same distinct ID and session as currently active on the
41
47
  frontend. See the documentation for `set_context_session` and `identify_context` for more details.
48
+
49
+ This middleware is hybrid-capable: it supports both WSGI (sync) and ASGI (async) Django applications. The middleware
50
+ detects at initialization whether the next middleware in the chain is async or sync, and adapts its behavior accordingly.
51
+ This ensures compatibility with both pure sync and pure async middleware chains, as well as mixed chains in ASGI mode.
42
52
  """
43
53
 
44
- # Django middleware capability flags
45
54
  sync_capable = True
46
55
  async_capable = True
47
56
 
48
57
  def __init__(self, get_response):
49
58
  # type: (Union[Callable[[HttpRequest], HttpResponse], Callable[[HttpRequest], Awaitable[HttpResponse]]]) -> None
59
+ self.get_response = get_response
50
60
  self._is_coroutine = iscoroutinefunction(get_response)
51
- self._async_get_response = None # type: Optional[Callable[[HttpRequest], Awaitable[HttpResponse]]]
52
- self._sync_get_response = None # type: Optional[Callable[[HttpRequest], HttpResponse]]
53
61
 
62
+ # Mark this instance as a coroutine function if get_response is async
63
+ # This is required for Django to correctly detect async middleware
54
64
  if self._is_coroutine:
55
- self._async_get_response = cast(
56
- "Callable[[HttpRequest], Awaitable[HttpResponse]]", get_response
57
- )
58
- else:
59
- self._sync_get_response = cast(
60
- "Callable[[HttpRequest], HttpResponse]", get_response
61
- )
65
+ markcoroutinefunction(self)
62
66
 
63
67
  from django.conf import settings
64
68
 
@@ -181,40 +185,38 @@ class PosthogContextMiddleware:
181
185
  return user_id, email
182
186
 
183
187
  def __call__(self, request):
184
- # type: (HttpRequest) -> HttpResponse
185
- # Purely defensive around django's internal sync/async handling - this should be unreachable, but if it's reached, we may
186
- # as well return something semi-meaningful
187
- if self._is_coroutine:
188
- raise RuntimeError(
189
- "PosthogContextMiddleware received sync call but get_response is async"
190
- )
188
+ # type: (HttpRequest) -> Union[HttpResponse, Awaitable[HttpResponse]]
189
+ """
190
+ Unified entry point for both sync and async request handling.
191
191
 
192
- if self.request_filter and not self.request_filter(request):
193
- assert self._sync_get_response is not None
194
- return self._sync_get_response(request)
192
+ When sync_capable and async_capable are both True, Django passes requests
193
+ without conversion. This method detects the mode and routes accordingly.
194
+ """
195
+ if self._is_coroutine:
196
+ return self.__acall__(request)
197
+ else:
198
+ # Synchronous path
199
+ if self.request_filter and not self.request_filter(request):
200
+ return self.get_response(request)
195
201
 
196
- with contexts.new_context(self.capture_exceptions, client=self.client):
197
- for k, v in self.extract_tags(request).items():
198
- contexts.tag(k, v)
202
+ with contexts.new_context(self.capture_exceptions, client=self.client):
203
+ for k, v in self.extract_tags(request).items():
204
+ contexts.tag(k, v)
199
205
 
200
- assert self._sync_get_response is not None
201
- return self._sync_get_response(request)
206
+ return self.get_response(request)
202
207
 
203
208
  async def __acall__(self, request):
204
- # type: (HttpRequest) -> HttpResponse
209
+ # type: (HttpRequest) -> Awaitable[HttpResponse]
210
+ """
211
+ Asynchronous entry point for async request handling.
212
+
213
+ This method is called when the middleware chain is async.
214
+ """
205
215
  if self.request_filter and not self.request_filter(request):
206
- if self._async_get_response is not None:
207
- return await self._async_get_response(request)
208
- else:
209
- assert self._sync_get_response is not None
210
- return self._sync_get_response(request)
216
+ return await self.get_response(request)
211
217
 
212
218
  with contexts.new_context(self.capture_exceptions, client=self.client):
213
219
  for k, v in self.extract_tags(request).items():
214
220
  contexts.tag(k, v)
215
221
 
216
- if self._async_get_response is not None:
217
- return await self._async_get_response(request)
218
- else:
219
- assert self._sync_get_response is not None
220
- return self._sync_get_response(request)
222
+ return await self.get_response(request)
posthog/version.py CHANGED
@@ -1,4 +1,4 @@
1
- VERSION = "6.7.9"
1
+ VERSION = "6.7.10"
2
2
 
3
3
  if __name__ == "__main__":
4
4
  print(VERSION, end="") # noqa: T201
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: posthog
3
- Version: 6.7.9
3
+ Version: 6.7.10
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -11,7 +11,7 @@ posthog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  posthog/request.py,sha256=CaONBN7a5RD8xiSShVMgHEd9XxKWM6ZQTLZypiqABhA,6168
12
12
  posthog/types.py,sha256=Dl3aFGX9XUR0wMmK12r2s5Hjan9jL4HpQ9GHpVcEq5U,10207
13
13
  posthog/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
14
- posthog/version.py,sha256=DV1G6YcxO_uwUg4WtwP5ybEnwJ_IQ_GSfj5EZRGd8fo,87
14
+ posthog/version.py,sha256=nZjqu-65D7o-byHYYEIUo6_Aa-szIb7lGhSsPoJkJas,88
15
15
  posthog/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  posthog/ai/sanitization.py,sha256=owipZ4eJYtd4JTI-CM_klatclXaeaIec3XJBOUfsOnQ,5770
17
17
  posthog/ai/types.py,sha256=ceubs4K9xf8vQx7wokq1NL9hPtxyS7D7sUOuT7Lx1lM,3237
@@ -32,7 +32,7 @@ posthog/ai/openai/openai_async.py,sha256=QFewU-8VBW1BhrNHkKCFuW57jqu2RSnF2VZ9mDu
32
32
  posthog/ai/openai/openai_converter.py,sha256=B38g_RgN1n7mbxbDDAZFcu50RO4UHCtJQAmpvDVry7o,20451
33
33
  posthog/ai/openai/openai_providers.py,sha256=zQIFTXHS2-dBKQX7FZxTFo7rIj5iiN7VHm9_2RzuDs8,3941
34
34
  posthog/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- posthog/integrations/django.py,sha256=2IHm9ft6iAEf_NZfJPAcNlHaH9WJ_ckVFTRpnHb8UvE,8619
35
+ posthog/integrations/django.py,sha256=7z7YsF46KTe11WfgX__qHpYau_jFt-9BacNYY9GI17A,8667
36
36
  posthog/test/__init__.py,sha256=VYgM6xPbJbvS-xhIcDiBRs0MFC9V_jT65uNeerCz_rM,299
37
37
  posthog/test/test_before_send.py,sha256=3546WKlk8rF6bhvqhwcxAsjJovDw0Hf8yTvcYGbrhyI,7912
38
38
  posthog/test/test_client.py,sha256=F-jUA0uKgHpVLOdnen2j6WSTp6whlJcpZdSecLoREFg,96273
@@ -47,8 +47,8 @@ posthog/test/test_request.py,sha256=l19WVyZQc4Iqmh_bpnAFOj4nGRpDK1iO-o5aJDQfFdo,
47
47
  posthog/test/test_size_limited_dict.py,sha256=Wom7BkzpHmusHilZy0SV3PNzhw7ucuQgqrx86jf8euo,765
48
48
  posthog/test/test_types.py,sha256=csLuBiz6RMV36cpg9LVIor4Khq6MfjjGxYXodx5VttY,7586
49
49
  posthog/test/test_utils.py,sha256=NUs2bgqrVuMdnKRq52syizgglt5_7wxxZl3dDMun-Tg,9602
50
- posthog-6.7.9.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
- posthog-6.7.9.dist-info/METADATA,sha256=F52GtCLJ4rIIFqVIp3dCImZ7O0S62EYXFkU6WowPVFg,6015
52
- posthog-6.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- posthog-6.7.9.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
54
- posthog-6.7.9.dist-info/RECORD,,
50
+ posthog-6.7.10.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
+ posthog-6.7.10.dist-info/METADATA,sha256=xXwTtZo74FQWRV5n4jsS9mG8MqlX1DrGY2qPVlUPaRo,6016
52
+ posthog-6.7.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ posthog-6.7.10.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
54
+ posthog-6.7.10.dist-info/RECORD,,