posthoganalytics 6.1.0__py3-none-any.whl → 6.1.1__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.
@@ -71,7 +71,9 @@ def _get_current_context() -> Optional[ContextScope]:
71
71
 
72
72
  @contextmanager
73
73
  def new_context(
74
- fresh=False, capture_exceptions=True, client: Optional["Client"] = None
74
+ fresh: bool = False,
75
+ capture_exceptions: bool = True,
76
+ client: Optional["Client"] = None,
75
77
  ):
76
78
  """
77
79
  Create a new context scope that will be active for the duration of the with block.
@@ -94,20 +96,25 @@ def new_context(
94
96
  the global one, in the case of `posthog.capture`)
95
97
 
96
98
  Examples:
99
+ ```python
97
100
  # Inherit parent context tags
98
101
  with posthog.new_context():
99
102
  posthog.tag("request_id", "123")
100
103
  # Both this event and the exception will be tagged with the context tags
101
104
  posthog.capture("event_name", {"property": "value"})
102
105
  raise ValueError("Something went wrong")
103
-
106
+ ```
107
+ ```python
104
108
  # Start with fresh context (no inherited tags)
105
109
  with posthog.new_context(fresh=True):
106
110
  posthog.tag("request_id", "123")
107
111
  # Both this event and the exception will be tagged with the context tags
108
112
  posthog.capture("event_name", {"property": "value"})
109
113
  raise ValueError("Something went wrong")
114
+ ```
110
115
 
116
+ Category:
117
+ Contexts
111
118
  """
112
119
  from posthoganalytics import capture_exception
113
120
 
@@ -138,7 +145,12 @@ def tag(key: str, value: Any) -> None:
138
145
  value: The tag value
139
146
 
140
147
  Example:
148
+ ```python
141
149
  posthog.tag("user_id", "123")
150
+ ```
151
+
152
+ Category:
153
+ Contexts
142
154
  """
143
155
  current_context = _get_current_context()
144
156
  if current_context:
@@ -152,6 +164,9 @@ def get_tags() -> Dict[str, Any]:
152
164
 
153
165
  Returns:
154
166
  Dict of all tags in the current context
167
+
168
+ Category:
169
+ Contexts
155
170
  """
156
171
  current_context = _get_current_context()
157
172
  if current_context:
@@ -170,6 +185,9 @@ def identify_context(distinct_id: str) -> None:
170
185
 
171
186
  Args:
172
187
  distinct_id: The distinct ID to associate with the current context and its children.
188
+
189
+ Category:
190
+ Contexts
173
191
  """
174
192
  current_context = _get_current_context()
175
193
  if current_context:
@@ -184,6 +202,9 @@ def set_context_session(session_id: str) -> None:
184
202
 
185
203
  Args:
186
204
  session_id: The session ID to associate with the current context and its children. See https://posthog.com/docs/data/sessions
205
+
206
+ Category:
207
+ Contexts
187
208
  """
188
209
  current_context = _get_current_context()
189
210
  if current_context:
@@ -196,6 +217,9 @@ def get_context_session_id() -> Optional[str]:
196
217
 
197
218
  Returns:
198
219
  The session ID if set, None otherwise
220
+
221
+ Category:
222
+ Contexts
199
223
  """
200
224
  current_context = _get_current_context()
201
225
  if current_context:
@@ -209,6 +233,9 @@ def get_context_distinct_id() -> Optional[str]:
209
233
 
210
234
  Returns:
211
235
  The distinct ID if set, None otherwise
236
+
237
+ Category:
238
+ Contexts
212
239
  """
213
240
  current_context = _get_current_context()
214
241
  if current_context:
@@ -219,7 +246,7 @@ def get_context_distinct_id() -> Optional[str]:
219
246
  F = TypeVar("F", bound=Callable[..., Any])
220
247
 
221
248
 
222
- def scoped(fresh=False, capture_exceptions=True):
249
+ def scoped(fresh: bool = False, capture_exceptions: bool = True):
223
250
  """
224
251
  Decorator that creates a new context for the function. Simply wraps
225
252
  the function in a with posthog.new_context(): block.
@@ -239,6 +266,9 @@ def scoped(fresh=False, capture_exceptions=True):
239
266
  # If this raises an exception, it will be captured with tags
240
267
  # and then re-raised
241
268
  some_risky_function()
269
+
270
+ Category:
271
+ Contexts
242
272
  """
243
273
 
244
274
  def decorator(func: F) -> F:
@@ -109,6 +109,14 @@ def is_condition_match(
109
109
  property_type = prop.get("type")
110
110
  if property_type == "cohort":
111
111
  matches = match_cohort(prop, properties, cohort_properties)
112
+ elif property_type == "flag":
113
+ log.warning(
114
+ "Flag dependency filters are not supported in local evaluation. "
115
+ "Skipping condition for flag '%s' with dependency on flag '%s'",
116
+ feature_flag.get("key", "unknown"),
117
+ prop.get("key", "unknown"),
118
+ )
119
+ continue
112
120
  else:
113
121
  matches = match_property(prop, properties)
114
122
  if not matches:
@@ -317,6 +325,13 @@ def match_property_group(property_group, property_values, cohort_properties) ->
317
325
  try:
318
326
  if prop.get("type") == "cohort":
319
327
  matches = match_cohort(prop, property_values, cohort_properties)
328
+ elif prop.get("type") == "flag":
329
+ log.warning(
330
+ "Flag dependency filters are not supported in local evaluation. "
331
+ "Skipping condition with dependency on flag '%s'",
332
+ prop.get("key", "unknown"),
333
+ )
334
+ continue
320
335
  else:
321
336
  matches = match_property(prop, property_values)
322
337
 
@@ -1,5 +1,5 @@
1
1
  from typing import TYPE_CHECKING, cast
2
- from posthoganalytics import contexts
2
+ from posthoganalytics import contexts, capture_exception
3
3
  from posthoganalytics.client import Client
4
4
 
5
5
  if TYPE_CHECKING:
@@ -167,3 +167,15 @@ class PosthogContextMiddleware:
167
167
  contexts.tag(k, v)
168
168
 
169
169
  return self.get_response(request)
170
+
171
+ def process_exception(self, request, exception):
172
+ if self.request_filter and not self.request_filter(request):
173
+ return
174
+
175
+ if not self.capture_exceptions:
176
+ return
177
+
178
+ if self.client:
179
+ self.client.capture_exception(exception)
180
+ else:
181
+ capture_exception(exception)
@@ -1355,6 +1355,77 @@ class TestLocalEvaluation(unittest.TestCase):
1355
1355
  self.assertEqual(patch_flags.call_count, 0)
1356
1356
  self.assertEqual(patch_get.call_count, 0)
1357
1357
 
1358
+ @mock.patch("posthog.feature_flags.log")
1359
+ @mock.patch("posthog.client.flags")
1360
+ @mock.patch("posthog.client.get")
1361
+ def test_feature_flags_with_flag_dependencies(
1362
+ self, patch_get, patch_flags, mock_log
1363
+ ):
1364
+ client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
1365
+ client.feature_flags = [
1366
+ {
1367
+ "id": 1,
1368
+ "name": "Flag with Dependencies",
1369
+ "key": "flag-with-dependencies",
1370
+ "active": True,
1371
+ "filters": {
1372
+ "groups": [
1373
+ {
1374
+ "properties": [
1375
+ {
1376
+ "key": "beta-feature",
1377
+ "operator": "exact",
1378
+ "value": True,
1379
+ "type": "flag",
1380
+ },
1381
+ {
1382
+ "key": "email",
1383
+ "operator": "icontains",
1384
+ "value": "@example.com",
1385
+ "type": "person",
1386
+ },
1387
+ ],
1388
+ "rollout_percentage": 100,
1389
+ }
1390
+ ],
1391
+ },
1392
+ }
1393
+ ]
1394
+
1395
+ # Test that flag evaluation doesn't fail when encountering a flag dependency
1396
+ # The flag should evaluate based on other conditions (email contains @example.com)
1397
+ # Since flag dependencies aren't implemented, it should skip the flag condition
1398
+ # and evaluate based on the email condition only
1399
+ feature_flag_match = client.get_feature_flag(
1400
+ "flag-with-dependencies",
1401
+ "test-user",
1402
+ person_properties={"email": "test@example.com"},
1403
+ )
1404
+ self.assertEqual(feature_flag_match, True)
1405
+ self.assertEqual(patch_flags.call_count, 0)
1406
+ self.assertEqual(patch_get.call_count, 0)
1407
+
1408
+ # Verify warning was logged for flag dependency
1409
+ mock_log.warning.assert_called_with(
1410
+ "Flag dependency filters are not supported in local evaluation. "
1411
+ "Skipping condition for flag '%s' with dependency on flag '%s'",
1412
+ "flag-with-dependencies",
1413
+ "beta-feature",
1414
+ )
1415
+
1416
+ # Test with email that doesn't match
1417
+ feature_flag_match = client.get_feature_flag(
1418
+ "flag-with-dependencies",
1419
+ "test-user-2",
1420
+ person_properties={"email": "test@other.com"},
1421
+ )
1422
+ self.assertEqual(feature_flag_match, False)
1423
+ self.assertEqual(patch_flags.call_count, 0)
1424
+ self.assertEqual(patch_get.call_count, 0)
1425
+
1426
+ # Verify warning was logged again for the second evaluation
1427
+ self.assertEqual(mock_log.warning.call_count, 2)
1428
+
1358
1429
  @mock.patch("posthog.client.Poller")
1359
1430
  @mock.patch("posthog.client.get")
1360
1431
  def test_load_feature_flags(self, patch_get, patch_poll):
@@ -1,4 +1,4 @@
1
- VERSION = "6.1.0"
1
+ VERSION = "6.1.1"
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.1.0
3
+ Version: 6.1.1
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -1,17 +1,17 @@
1
- posthoganalytics/__init__.py,sha256=U8EmLKYL0N-k-kPb-R0c36MXtaGkA7PmFnhek3Xga5k,17070
1
+ posthoganalytics/__init__.py,sha256=TSi-Hq6mKCAiq6VAjaHPG5g8Pb8FTkK8De_l69O7G74,24096
2
2
  posthoganalytics/args.py,sha256=hRKPQ3cPGyDn4S7Ay9t2NlgoZg1cJ0GeN_Mb6OKtmfo,3145
3
- posthoganalytics/client.py,sha256=w2br3pz_1sV3IF39SZavukXowbDG8phrx9sHlltdgmc,50971
3
+ posthoganalytics/client.py,sha256=L_Ive-qlIvflqjLrote0TP2r9jBqbNsQE_Xs7gDEe4Y,65265
4
4
  posthoganalytics/consumer.py,sha256=CiNbJBdyW9jER3ZYCKbX-JFmEDXlE1lbDy1MSl43-a0,4617
5
- posthoganalytics/contexts.py,sha256=B3Y62sX7w-MCqNqgguUceQnKn5RCBFIqen3VeR3qems,9020
5
+ posthoganalytics/contexts.py,sha256=LFSFIYpUFWKTBnGMjV9n1aYHWbAzz5zLJGr2qG34PoE,9405
6
6
  posthoganalytics/exception_capture.py,sha256=1VHBfffrXXrkK0PT8iVgKPpj_R1pGAzG5f3Qw0WF79w,1783
7
7
  posthoganalytics/exception_utils.py,sha256=P_75873Y2jayqlLiIkbxCNE7Bc8cM6J9kfrdZ5ZSnA0,26696
8
- posthoganalytics/feature_flags.py,sha256=s87jlo515OV2A0GOtMezwImt5vFUAn8kqsMwzdoOURo,13652
8
+ posthoganalytics/feature_flags.py,sha256=O_kXmw3goB2E9XMBosdPeBAuo9MsnsH8PyNWq95a0qo,14391
9
9
  posthoganalytics/poller.py,sha256=jBz5rfH_kn_bBz7wCB46Fpvso4ttx4uzqIZWvXBCFmQ,595
10
10
  posthoganalytics/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  posthoganalytics/request.py,sha256=TaeySYpcvHMf5Ftf5KqqlO0VPJpirKBCRrThlS04Kew,6124
12
12
  posthoganalytics/types.py,sha256=INxWBOEQc0xgPcap6FdQNSU7zuQBmKShYaGzyuHKql8,9128
13
13
  posthoganalytics/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
14
- posthoganalytics/version.py,sha256=t8G8Y2aHP3Z3Ej7UE-_LT8EK7Gi6rurkCbuJWBqvhXE,87
14
+ posthoganalytics/version.py,sha256=xZhcgW3hzlFW0J_tV0DpBWXhz8E1uxHP1x46lky-cjo,87
15
15
  posthoganalytics/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  posthoganalytics/ai/utils.py,sha256=5-2XfmetCs0v9otBoux7-IEG933wAnKLSGS6oYLqCkw,19529
17
17
  posthoganalytics/ai/anthropic/__init__.py,sha256=fFhDOiRzTXzGQlgnrRDL-4yKC8EYIl8NW4a2QNR6xRU,368
@@ -27,7 +27,7 @@ posthoganalytics/ai/openai/openai.py,sha256=iL_cwctaAhPdXNo4EpIZooOWGyjNj0W-OUEo
27
27
  posthoganalytics/ai/openai/openai_async.py,sha256=KxPCd5imF5iZ9VkJ12HjCO2skaF1tHsHveAknIqV93g,23769
28
28
  posthoganalytics/ai/openai/openai_providers.py,sha256=EMuEvdHSOFbrhmfuU0is7pBVWS3ReAUT0PZqgMXdyjk,3884
29
29
  posthoganalytics/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- posthoganalytics/integrations/django.py,sha256=BdBo6nvY6cjKMYPpKTzvwiICCLIhCOyslgqwFCObPzo,6441
30
+ posthoganalytics/integrations/django.py,sha256=KYtBr7CkiZQynRc2TCWWYHe-J3ie8iSUa42WPshYZdc,6795
31
31
  posthoganalytics/test/__init__.py,sha256=VYgM6xPbJbvS-xhIcDiBRs0MFC9V_jT65uNeerCz_rM,299
32
32
  posthoganalytics/test/test_before_send.py,sha256=A1_UVMewhHAvO39rZDWfS606vG_X-q0KNXvh5DAKiB8,7930
33
33
  posthoganalytics/test/test_client.py,sha256=fghypNGUqOY9OmycsdNK5SsyCoimxYVisFfFq2nkTCU,77840
@@ -36,14 +36,14 @@ posthoganalytics/test/test_contexts.py,sha256=c--hNUIEf6SHQ7H9vdPhU1oLCN0SnD4wDb
36
36
  posthoganalytics/test/test_exception_capture.py,sha256=al37Kg6wjzL_IBCFUUXRvkP6nVrqS6IZRCOKSo29Nh8,1063
37
37
  posthoganalytics/test/test_feature_flag.py,sha256=9RQwB5eCvVAGrrO7UnR3Z1OidP_YoL4iBl3A83fuAig,6824
38
38
  posthoganalytics/test/test_feature_flag_result.py,sha256=z2OgD97r85LKMqCnoCqAs74WjUMucayAtC3qWaITGCA,15898
39
- posthoganalytics/test/test_feature_flags.py,sha256=2CW_E1TrqHC-BXAP68LEBi8YleM7J82XQc_gHX1Bxtc,169056
39
+ posthoganalytics/test/test_feature_flags.py,sha256=vx33vM_loTnlY4T7iLGO5X5htjkiTQGqT01LeocVbLo,171971
40
40
  posthoganalytics/test/test_module.py,sha256=viqaAWA_uHt8r20fHIeME6IQkeXmQ8ZyrJTtPGQAb1E,1070
41
41
  posthoganalytics/test/test_request.py,sha256=Zc0VbkjpVmj8mKokQm9rzdgTr0b1U44vvMYSkB_IQLs,4467
42
42
  posthoganalytics/test/test_size_limited_dict.py,sha256=-5IQjIEr_-Dql24M0HusdR_XroOMrtgiT0v6ZQCRvzo,774
43
43
  posthoganalytics/test/test_types.py,sha256=bRPHdwVpP7hu7emsplU8UVyzSQptv6PaG5lAoOD_BtM,7595
44
44
  posthoganalytics/test/test_utils.py,sha256=sqUTbfweVcxxFRd3WDMFXqPMyU6DvzOBeAOc68Py9aw,9620
45
- posthoganalytics-6.1.0.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
46
- posthoganalytics-6.1.0.dist-info/METADATA,sha256=xp_DkYzPEjNwHIZO2Ac3-Z-NDqBy5jv2nqrdzpfNvFU,6024
47
- posthoganalytics-6.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
- posthoganalytics-6.1.0.dist-info/top_level.txt,sha256=8QsNIqIkBh1p2TXvKp0Em9ZLZKwe3uIqCETyW4s1GOE,17
49
- posthoganalytics-6.1.0.dist-info/RECORD,,
45
+ posthoganalytics-6.1.1.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
46
+ posthoganalytics-6.1.1.dist-info/METADATA,sha256=xrhfLBXcqwgMm1nsVsP5UDPo3nrGC6IZitVt0Dpn5XM,6024
47
+ posthoganalytics-6.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
+ posthoganalytics-6.1.1.dist-info/top_level.txt,sha256=8QsNIqIkBh1p2TXvKp0Em9ZLZKwe3uIqCETyW4s1GOE,17
49
+ posthoganalytics-6.1.1.dist-info/RECORD,,