posthoganalytics 6.7.4__tar.gz → 6.7.6__tar.gz

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.
Files changed (62) hide show
  1. {posthoganalytics-6.7.4/posthoganalytics.egg-info → posthoganalytics-6.7.6}/PKG-INFO +1 -1
  2. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/client.py +42 -1
  3. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/feature_flags.py +1 -8
  4. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/integrations/django.py +53 -14
  5. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_client.py +43 -0
  6. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_feature_flags.py +23 -35
  7. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_module.py +0 -8
  8. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/version.py +1 -1
  9. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6/posthoganalytics.egg-info}/PKG-INFO +1 -1
  10. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/LICENSE +0 -0
  11. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/MANIFEST.in +0 -0
  12. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/README.md +0 -0
  13. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/__init__.py +0 -0
  14. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/__init__.py +0 -0
  15. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/anthropic/__init__.py +0 -0
  16. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/anthropic/anthropic.py +0 -0
  17. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/anthropic/anthropic_async.py +0 -0
  18. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/anthropic/anthropic_converter.py +0 -0
  19. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/anthropic/anthropic_providers.py +0 -0
  20. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/gemini/__init__.py +0 -0
  21. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/gemini/gemini.py +0 -0
  22. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/gemini/gemini_converter.py +0 -0
  23. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/langchain/__init__.py +0 -0
  24. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/langchain/callbacks.py +0 -0
  25. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/openai/__init__.py +0 -0
  26. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/openai/openai.py +0 -0
  27. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/openai/openai_async.py +0 -0
  28. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/openai/openai_converter.py +0 -0
  29. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/openai/openai_providers.py +0 -0
  30. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/sanitization.py +0 -0
  31. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/types.py +0 -0
  32. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/ai/utils.py +0 -0
  33. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/args.py +0 -0
  34. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/consumer.py +0 -0
  35. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/contexts.py +0 -0
  36. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/exception_capture.py +0 -0
  37. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/exception_utils.py +0 -0
  38. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/integrations/__init__.py +0 -0
  39. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/poller.py +0 -0
  40. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/py.typed +0 -0
  41. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/request.py +0 -0
  42. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/__init__.py +0 -0
  43. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_before_send.py +0 -0
  44. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_consumer.py +0 -0
  45. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_contexts.py +0 -0
  46. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_exception_capture.py +0 -0
  47. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_feature_flag.py +0 -0
  48. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_feature_flag_result.py +0 -0
  49. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_request.py +0 -0
  50. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_size_limited_dict.py +0 -0
  51. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_types.py +0 -0
  52. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/test/test_utils.py +0 -0
  53. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/types.py +0 -0
  54. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics/utils.py +0 -0
  55. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics.egg-info/SOURCES.txt +0 -0
  56. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics.egg-info/dependency_links.txt +0 -0
  57. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics.egg-info/requires.txt +0 -0
  58. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/posthoganalytics.egg-info/top_level.txt +0 -0
  59. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/pyproject.toml +0 -0
  60. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/setup.cfg +0 -0
  61. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/setup.py +0 -0
  62. {posthoganalytics-6.7.4 → posthoganalytics-6.7.6}/setup_analytics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: posthoganalytics
3
- Version: 6.7.4
3
+ Version: 6.7.6
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -3,7 +3,7 @@ import logging
3
3
  import os
4
4
  import sys
5
5
  from datetime import datetime, timedelta
6
- from typing import Any, Dict, Optional, Union
6
+ from typing import Any, Callable, Dict, Optional, Union
7
7
  from typing_extensions import Unpack
8
8
  from uuid import uuid4
9
9
 
@@ -99,6 +99,34 @@ def add_context_tags(properties):
99
99
  return properties
100
100
 
101
101
 
102
+ def no_throw(default_return=None):
103
+ """
104
+ Decorator to prevent raising exceptions from public API methods.
105
+ Note that this doesn't prevent errors from propagating via `on_error`.
106
+ Exceptions will still be raised if the debug flag is enabled.
107
+
108
+ Args:
109
+ default_return: Value to return on exception (default: None)
110
+ """
111
+
112
+ def decorator(func):
113
+ from functools import wraps
114
+
115
+ @wraps(func)
116
+ def wrapper(self, *args, **kwargs):
117
+ try:
118
+ return func(self, *args, **kwargs)
119
+ except Exception as e:
120
+ if self.debug:
121
+ raise e
122
+ self.log.exception(f"Error in {func.__name__}: {e}")
123
+ return default_return
124
+
125
+ return wrapper
126
+
127
+ return decorator
128
+
129
+
102
130
  class Client(object):
103
131
  """
104
132
  This is the SDK reference for the PostHog Python SDK.
@@ -481,6 +509,7 @@ class Client(object):
481
509
 
482
510
  return normalize_flags_response(resp_data)
483
511
 
512
+ @no_throw()
484
513
  def capture(
485
514
  self, event: str, **kwargs: Unpack[OptionalCaptureArgs]
486
515
  ) -> Optional[str]:
@@ -657,6 +686,7 @@ class Client(object):
657
686
  f"Expected bool or dict."
658
687
  )
659
688
 
689
+ @no_throw()
660
690
  def set(self, **kwargs: Unpack[OptionalSetArgs]) -> Optional[str]:
661
691
  """
662
692
  Set properties on a person profile.
@@ -690,6 +720,8 @@ class Client(object):
690
720
 
691
721
  Category:
692
722
  Identification
723
+
724
+ Note: This method will not raise exceptions. Errors are logged.
693
725
  """
694
726
  distinct_id = kwargs.get("distinct_id", None)
695
727
  properties = kwargs.get("properties", None)
@@ -716,6 +748,7 @@ class Client(object):
716
748
 
717
749
  return self._enqueue(msg, disable_geoip)
718
750
 
751
+ @no_throw()
719
752
  def set_once(self, **kwargs: Unpack[OptionalSetArgs]) -> Optional[str]:
720
753
  """
721
754
  Set properties on a person profile only if they haven't been set before.
@@ -734,6 +767,8 @@ class Client(object):
734
767
 
735
768
  Category:
736
769
  Identification
770
+
771
+ Note: This method will not raise exceptions. Errors are logged.
737
772
  """
738
773
  distinct_id = kwargs.get("distinct_id", None)
739
774
  properties = kwargs.get("properties", None)
@@ -759,6 +794,7 @@ class Client(object):
759
794
 
760
795
  return self._enqueue(msg, disable_geoip)
761
796
 
797
+ @no_throw()
762
798
  def group_identify(
763
799
  self,
764
800
  group_type: str,
@@ -791,6 +827,8 @@ class Client(object):
791
827
 
792
828
  Category:
793
829
  Identification
830
+
831
+ Note: This method will not raise exceptions. Errors are logged.
794
832
  """
795
833
  properties = properties or {}
796
834
 
@@ -815,6 +853,7 @@ class Client(object):
815
853
 
816
854
  return self._enqueue(msg, disable_geoip)
817
855
 
856
+ @no_throw()
818
857
  def alias(
819
858
  self,
820
859
  previous_id: str,
@@ -840,6 +879,8 @@ class Client(object):
840
879
 
841
880
  Category:
842
881
  Identification
882
+
883
+ Note: This method will not raise exceptions. Errors are logged.
843
884
  """
844
885
  (distinct_id, personless) = get_identity_state(distinct_id)
845
886
 
@@ -220,14 +220,7 @@ def match_feature_flag_properties(
220
220
  ) or []
221
221
  valid_variant_keys = [variant["key"] for variant in flag_variants]
222
222
 
223
- # Stable sort conditions with variant overrides to the top. This ensures that if overrides are present, they are
224
- # evaluated first, and the variant override is applied to the first matching condition.
225
- sorted_flag_conditions = sorted(
226
- flag_conditions,
227
- key=lambda condition: 0 if condition.get("variant") else 1,
228
- )
229
-
230
- for condition in sorted_flag_conditions:
223
+ for condition in flag_conditions:
231
224
  try:
232
225
  # if any one condition resolves to True, we can shortcircuit and return
233
226
  # the matching variant
@@ -1,10 +1,18 @@
1
1
  from typing import TYPE_CHECKING, cast
2
- from posthoganalytics import contexts, capture_exception
2
+ from posthoganalytics import contexts
3
3
  from posthoganalytics.client import Client
4
4
 
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
+
5
13
  if TYPE_CHECKING:
6
14
  from django.http import HttpRequest, HttpResponse # noqa: F401
7
- from typing import Callable, Dict, Any, Optional # noqa: F401
15
+ from typing import Callable, Dict, Any, Optional, Union, Awaitable # noqa: F401
8
16
 
9
17
 
10
18
  class PosthogContextMiddleware:
@@ -33,9 +41,24 @@ class PosthogContextMiddleware:
33
41
  frontend. See the documentation for `set_context_session` and `identify_context` for more details.
34
42
  """
35
43
 
44
+ # Django middleware capability flags
45
+ sync_capable = True
46
+ async_capable = True
47
+
36
48
  def __init__(self, get_response):
37
- # type: (Callable[[HttpRequest], HttpResponse]) -> None
38
- self.get_response = get_response
49
+ # type: (Union[Callable[[HttpRequest], HttpResponse], Callable[[HttpRequest], Awaitable[HttpResponse]]]) -> None
50
+ 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
+
54
+ 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
+ )
39
62
 
40
63
  from django.conf import settings
41
64
 
@@ -159,23 +182,39 @@ class PosthogContextMiddleware:
159
182
 
160
183
  def __call__(self, request):
161
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
+ )
191
+
162
192
  if self.request_filter and not self.request_filter(request):
163
- return self.get_response(request)
193
+ assert self._sync_get_response is not None
194
+ return self._sync_get_response(request)
164
195
 
165
196
  with contexts.new_context(self.capture_exceptions, client=self.client):
166
197
  for k, v in self.extract_tags(request).items():
167
198
  contexts.tag(k, v)
168
199
 
169
- return self.get_response(request)
200
+ assert self._sync_get_response is not None
201
+ return self._sync_get_response(request)
170
202
 
171
- def process_exception(self, request, exception):
203
+ async def __acall__(self, request):
204
+ # type: (HttpRequest) -> HttpResponse
172
205
  if self.request_filter and not self.request_filter(request):
173
- return
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)
174
211
 
175
- if not self.capture_exceptions:
176
- return
212
+ with contexts.new_context(self.capture_exceptions, client=self.client):
213
+ for k, v in self.extract_tags(request).items():
214
+ contexts.tag(k, v)
177
215
 
178
- if self.client:
179
- self.client.capture_exception(exception)
180
- else:
181
- capture_exception(exception)
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)
@@ -2423,3 +2423,46 @@ class TestClient(unittest.TestCase):
2423
2423
  batch_data = mock_post.call_args[1]["batch"]
2424
2424
  msg = batch_data[0]
2425
2425
  self.assertEqual(msg["properties"]["$context_tags"], ["random_tag"])
2426
+
2427
+ @mock.patch(
2428
+ "posthog.client.Client._enqueue", side_effect=Exception("Unexpected error")
2429
+ )
2430
+ def test_methods_handle_exceptions(self, mock_enqueue):
2431
+ """Test that all decorated methods handle exceptions gracefully."""
2432
+ client = Client("test-key")
2433
+
2434
+ test_cases = [
2435
+ ("capture", ["test_event"], {}),
2436
+ ("set", [], {"distinct_id": "some-id", "properties": {"a": "b"}}),
2437
+ ("set_once", [], {"distinct_id": "some-id", "properties": {"a": "b"}}),
2438
+ ("group_identify", ["group-type", "group-key"], {}),
2439
+ ("alias", ["some-id", "new-id"], {}),
2440
+ ]
2441
+
2442
+ for method_name, args, kwargs in test_cases:
2443
+ with self.subTest(method=method_name):
2444
+ method = getattr(client, method_name)
2445
+ result = method(*args, **kwargs)
2446
+ self.assertEqual(result, None)
2447
+
2448
+ @mock.patch(
2449
+ "posthog.client.Client._enqueue", side_effect=Exception("Expected error")
2450
+ )
2451
+ def test_debug_flag_re_raises_exceptions(self, mock_enqueue):
2452
+ """Test that methods re-raise exceptions when debug=True."""
2453
+ client = Client("test-key", debug=True)
2454
+
2455
+ test_cases = [
2456
+ ("capture", ["test_event"], {}),
2457
+ ("set", [], {"distinct_id": "some-id", "properties": {"a": "b"}}),
2458
+ ("set_once", [], {"distinct_id": "some-id", "properties": {"a": "b"}}),
2459
+ ("group_identify", ["group-type", "group-key"], {}),
2460
+ ("alias", ["some-id", "new-id"], {}),
2461
+ ]
2462
+
2463
+ for method_name, args, kwargs in test_cases:
2464
+ with self.subTest(method=method_name):
2465
+ method = getattr(client, method_name)
2466
+ with self.assertRaises(Exception) as cm:
2467
+ method(*args, **kwargs)
2468
+ self.assertEqual(str(cm.exception), "Expected error")
@@ -2804,73 +2804,61 @@ class TestLocalEvaluation(unittest.TestCase):
2804
2804
  self.assertEqual(patch_flags.call_count, 0)
2805
2805
 
2806
2806
  @mock.patch("posthog.client.flags")
2807
- def test_flag_with_multiple_variant_overrides(self, patch_flags):
2808
- patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}}
2807
+ def test_conditions_evaluated_in_order(self, patch_flags):
2808
+ patch_flags.return_value = {"featureFlags": {"order-test": "server-variant"}}
2809
2809
  client = Client(FAKE_TEST_API_KEY, personal_api_key="test")
2810
2810
  client.feature_flags = [
2811
2811
  {
2812
2812
  "id": 1,
2813
- "name": "Beta Feature",
2814
- "key": "beta-feature",
2813
+ "name": "Order Test Flag",
2814
+ "key": "order-test",
2815
2815
  "active": True,
2816
- "rollout_percentage": 100,
2817
2816
  "filters": {
2818
2817
  "groups": [
2819
2818
  {
2820
2819
  "rollout_percentage": 100,
2821
- # The override applies even if the first condition matches all and gives everyone their default group
2822
2820
  },
2823
2821
  {
2824
2822
  "properties": [
2825
2823
  {
2826
2824
  "key": "email",
2827
2825
  "type": "person",
2828
- "value": "test@posthog.com",
2829
- "operator": "exact",
2826
+ "value": "@vip.com",
2827
+ "operator": "icontains",
2830
2828
  }
2831
2829
  ],
2832
2830
  "rollout_percentage": 100,
2833
- "variant": "second-variant",
2831
+ "variant": "vip-variant",
2834
2832
  },
2835
- {"rollout_percentage": 50, "variant": "third-variant"},
2836
2833
  ],
2837
2834
  "multivariate": {
2838
2835
  "variants": [
2839
2836
  {
2840
- "key": "first-variant",
2841
- "name": "First Variant",
2842
- "rollout_percentage": 50,
2837
+ "key": "control",
2838
+ "name": "Control",
2839
+ "rollout_percentage": 100,
2843
2840
  },
2844
2841
  {
2845
- "key": "second-variant",
2846
- "name": "Second Variant",
2847
- "rollout_percentage": 25,
2848
- },
2849
- {
2850
- "key": "third-variant",
2851
- "name": "Third Variant",
2852
- "rollout_percentage": 25,
2842
+ "key": "vip-variant",
2843
+ "name": "VIP Variant",
2844
+ "rollout_percentage": 0,
2853
2845
  },
2854
2846
  ]
2855
2847
  },
2856
2848
  },
2857
2849
  }
2858
2850
  ]
2859
- self.assertEqual(
2860
- client.get_feature_flag(
2861
- "beta-feature",
2862
- "test_id",
2863
- person_properties={"email": "test@posthog.com"},
2864
- ),
2865
- "second-variant",
2866
- )
2867
- self.assertEqual(
2868
- client.get_feature_flag("beta-feature", "example_id"), "third-variant"
2869
- )
2870
- self.assertEqual(
2871
- client.get_feature_flag("beta-feature", "another_id"), "second-variant"
2851
+
2852
+ # Even though user@vip.com would match the second condition with variant override,
2853
+ # they should match the first condition and get control
2854
+ result = client.get_feature_flag(
2855
+ "order-test",
2856
+ "user123",
2857
+ person_properties={"email": "user@vip.com"},
2872
2858
  )
2873
- # decide not called because this can be evaluated locally
2859
+ self.assertEqual(result, "control")
2860
+
2861
+ # server not called because this can be evaluated locally
2874
2862
  self.assertEqual(patch_flags.call_count, 0)
2875
2863
 
2876
2864
  @mock.patch("posthog.client.flags")
@@ -18,14 +18,6 @@ class TestModule(unittest.TestCase):
18
18
  "testsecret", host="http://localhost:8000", on_error=self.failed
19
19
  )
20
20
 
21
- def test_no_api_key(self):
22
- self.posthog.api_key = None
23
- self.assertRaises(Exception, self.posthog.capture)
24
-
25
- def test_no_host(self):
26
- self.posthog.host = None
27
- self.assertRaises(Exception, self.posthog.capture)
28
-
29
21
  def test_track(self):
30
22
  res = self.posthog.capture("python module event", distinct_id="distinct_id")
31
23
  self._assert_enqueue_result(res)
@@ -1,4 +1,4 @@
1
- VERSION = "6.7.4"
1
+ VERSION = "6.7.6"
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: posthoganalytics
3
- Version: 6.7.4
3
+ Version: 6.7.6
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog