posthog 7.0.0__py3-none-any.whl → 7.3.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.
@@ -6,16 +6,59 @@ import mock
6
6
  import pytest
7
7
  import requests
8
8
 
9
+ import posthog.request as request_module
9
10
  from posthog.request import (
11
+ APIError,
10
12
  DatetimeSerializer,
13
+ GetResponse,
14
+ KEEP_ALIVE_SOCKET_OPTIONS,
11
15
  QuotaLimitError,
16
+ _mask_tokens_in_url,
12
17
  batch_post,
13
18
  decide,
14
19
  determine_server_host,
20
+ disable_connection_reuse,
21
+ enable_keep_alive,
22
+ get,
23
+ set_socket_options,
15
24
  )
16
25
  from posthog.test.test_utils import TEST_API_KEY
17
26
 
18
27
 
28
+ @pytest.mark.parametrize(
29
+ "url, expected",
30
+ [
31
+ # Token with params after - masks keeping first 10 chars
32
+ (
33
+ "https://example.com/api/flags?token=phc_abc123xyz789&send_cohorts",
34
+ "https://example.com/api/flags?token=phc_abc123...&send_cohorts",
35
+ ),
36
+ # Token at end of URL
37
+ (
38
+ "https://example.com/api/flags?token=phc_abc123xyz789",
39
+ "https://example.com/api/flags?token=phc_abc123...",
40
+ ),
41
+ # No token - unchanged
42
+ (
43
+ "https://example.com/api/flags?other=value",
44
+ "https://example.com/api/flags?other=value",
45
+ ),
46
+ # Short token (<10 chars) - unchanged
47
+ (
48
+ "https://example.com/api/flags?token=short",
49
+ "https://example.com/api/flags?token=short",
50
+ ),
51
+ # Exactly 10 char token - gets ellipsis
52
+ (
53
+ "https://example.com/api/flags?token=1234567890",
54
+ "https://example.com/api/flags?token=1234567890...",
55
+ ),
56
+ ],
57
+ )
58
+ def test_mask_tokens_in_url(url, expected):
59
+ assert _mask_tokens_in_url(url) == expected
60
+
61
+
19
62
  class TestRequests(unittest.TestCase):
20
63
  def test_valid_request(self):
21
64
  res = batch_post(
@@ -107,6 +150,184 @@ class TestRequests(unittest.TestCase):
107
150
  self.assertEqual(response["featureFlags"], {"flag1": True})
108
151
 
109
152
 
153
+ class TestGet(unittest.TestCase):
154
+ """Unit tests for the get() function HTTP-level behavior."""
155
+
156
+ @mock.patch("posthog.request._session.get")
157
+ def test_get_returns_data_and_etag(self, mock_get):
158
+ """Test that get() returns GetResponse with data and etag from headers."""
159
+ mock_response = requests.Response()
160
+ mock_response.status_code = 200
161
+ mock_response.headers["ETag"] = '"abc123"'
162
+ mock_response._content = json.dumps({"flags": [{"key": "test-flag"}]}).encode(
163
+ "utf-8"
164
+ )
165
+ mock_get.return_value = mock_response
166
+
167
+ response = get("api_key", "/test-url", host="https://example.com")
168
+
169
+ self.assertIsInstance(response, GetResponse)
170
+ self.assertEqual(response.data, {"flags": [{"key": "test-flag"}]})
171
+ self.assertEqual(response.etag, '"abc123"')
172
+ self.assertFalse(response.not_modified)
173
+
174
+ @mock.patch("posthog.request._session.get")
175
+ def test_get_sends_if_none_match_header_when_etag_provided(self, mock_get):
176
+ """Test that If-None-Match header is sent when etag parameter is provided."""
177
+ mock_response = requests.Response()
178
+ mock_response.status_code = 200
179
+ mock_response.headers["ETag"] = '"new-etag"'
180
+ mock_response._content = json.dumps({"flags": []}).encode("utf-8")
181
+ mock_get.return_value = mock_response
182
+
183
+ get("api_key", "/test-url", host="https://example.com", etag='"previous-etag"')
184
+
185
+ call_kwargs = mock_get.call_args[1]
186
+ self.assertEqual(call_kwargs["headers"]["If-None-Match"], '"previous-etag"')
187
+
188
+ @mock.patch("posthog.request._session.get")
189
+ def test_get_does_not_send_if_none_match_when_no_etag(self, mock_get):
190
+ """Test that If-None-Match header is not sent when no etag provided."""
191
+ mock_response = requests.Response()
192
+ mock_response.status_code = 200
193
+ mock_response._content = json.dumps({"flags": []}).encode("utf-8")
194
+ mock_get.return_value = mock_response
195
+
196
+ get("api_key", "/test-url", host="https://example.com")
197
+
198
+ call_kwargs = mock_get.call_args[1]
199
+ self.assertNotIn("If-None-Match", call_kwargs["headers"])
200
+
201
+ @mock.patch("posthog.request._session.get")
202
+ def test_get_handles_304_not_modified(self, mock_get):
203
+ """Test that 304 Not Modified response returns not_modified=True with no data."""
204
+ mock_response = requests.Response()
205
+ mock_response.status_code = 304
206
+ mock_response.headers["ETag"] = '"unchanged-etag"'
207
+ mock_get.return_value = mock_response
208
+
209
+ response = get(
210
+ "api_key", "/test-url", host="https://example.com", etag='"unchanged-etag"'
211
+ )
212
+
213
+ self.assertIsInstance(response, GetResponse)
214
+ self.assertIsNone(response.data)
215
+ self.assertEqual(response.etag, '"unchanged-etag"')
216
+ self.assertTrue(response.not_modified)
217
+
218
+ @mock.patch("posthog.request._session.get")
219
+ def test_get_304_without_etag_header_uses_request_etag(self, mock_get):
220
+ """Test that 304 response without ETag header falls back to request etag."""
221
+ mock_response = requests.Response()
222
+ mock_response.status_code = 304
223
+ # Server doesn't return ETag header on 304
224
+ mock_get.return_value = mock_response
225
+
226
+ response = get(
227
+ "api_key", "/test-url", host="https://example.com", etag='"original-etag"'
228
+ )
229
+
230
+ self.assertTrue(response.not_modified)
231
+ self.assertEqual(response.etag, '"original-etag"')
232
+
233
+ @mock.patch("posthog.request._session.get")
234
+ def test_get_200_without_etag_header(self, mock_get):
235
+ """Test that 200 response without ETag header returns None for etag."""
236
+ mock_response = requests.Response()
237
+ mock_response.status_code = 200
238
+ mock_response._content = json.dumps({"flags": []}).encode("utf-8")
239
+ # No ETag header
240
+ mock_get.return_value = mock_response
241
+
242
+ response = get("api_key", "/test-url", host="https://example.com")
243
+
244
+ self.assertFalse(response.not_modified)
245
+ self.assertIsNone(response.etag)
246
+ self.assertEqual(response.data, {"flags": []})
247
+
248
+ @mock.patch("posthog.request._session.get")
249
+ def test_get_error_response_raises_api_error(self, mock_get):
250
+ """Test that error responses raise APIError."""
251
+ mock_response = requests.Response()
252
+ mock_response.status_code = 401
253
+ mock_response._content = json.dumps({"detail": "Unauthorized"}).encode("utf-8")
254
+ mock_get.return_value = mock_response
255
+
256
+ with self.assertRaises(APIError) as ctx:
257
+ get("bad_key", "/test-url", host="https://example.com")
258
+
259
+ self.assertEqual(ctx.exception.status, 401)
260
+ self.assertEqual(ctx.exception.message, "Unauthorized")
261
+
262
+ @mock.patch("posthog.request._session.get")
263
+ def test_get_sends_authorization_header(self, mock_get):
264
+ """Test that Authorization header is sent with Bearer token."""
265
+ mock_response = requests.Response()
266
+ mock_response.status_code = 200
267
+ mock_response._content = json.dumps({}).encode("utf-8")
268
+ mock_get.return_value = mock_response
269
+
270
+ get("my-api-key", "/test-url", host="https://example.com")
271
+
272
+ call_kwargs = mock_get.call_args[1]
273
+ self.assertEqual(call_kwargs["headers"]["Authorization"], "Bearer my-api-key")
274
+
275
+ @mock.patch("posthog.request._session.get")
276
+ def test_get_sends_user_agent_header(self, mock_get):
277
+ """Test that User-Agent header is sent."""
278
+ mock_response = requests.Response()
279
+ mock_response.status_code = 200
280
+ mock_response._content = json.dumps({}).encode("utf-8")
281
+ mock_get.return_value = mock_response
282
+
283
+ get("api_key", "/test-url", host="https://example.com")
284
+
285
+ call_kwargs = mock_get.call_args[1]
286
+ self.assertIn("User-Agent", call_kwargs["headers"])
287
+ self.assertTrue(
288
+ call_kwargs["headers"]["User-Agent"].startswith("posthog-python/")
289
+ )
290
+
291
+ @mock.patch("posthog.request._session.get")
292
+ def test_get_passes_timeout(self, mock_get):
293
+ """Test that timeout parameter is passed to the request."""
294
+ mock_response = requests.Response()
295
+ mock_response.status_code = 200
296
+ mock_response._content = json.dumps({}).encode("utf-8")
297
+ mock_get.return_value = mock_response
298
+
299
+ get("api_key", "/test-url", host="https://example.com", timeout=30)
300
+
301
+ call_kwargs = mock_get.call_args[1]
302
+ self.assertEqual(call_kwargs["timeout"], 30)
303
+
304
+ @mock.patch("posthog.request._session.get")
305
+ def test_get_constructs_full_url(self, mock_get):
306
+ """Test that host and url are combined correctly."""
307
+ mock_response = requests.Response()
308
+ mock_response.status_code = 200
309
+ mock_response._content = json.dumps({}).encode("utf-8")
310
+ mock_get.return_value = mock_response
311
+
312
+ get("api_key", "/api/flags", host="https://example.com")
313
+
314
+ call_args = mock_get.call_args[0]
315
+ self.assertEqual(call_args[0], "https://example.com/api/flags")
316
+
317
+ @mock.patch("posthog.request._session.get")
318
+ def test_get_removes_trailing_slash_from_host(self, mock_get):
319
+ """Test that trailing slash is removed from host."""
320
+ mock_response = requests.Response()
321
+ mock_response.status_code = 200
322
+ mock_response._content = json.dumps({}).encode("utf-8")
323
+ mock_get.return_value = mock_response
324
+
325
+ get("api_key", "/api/flags", host="https://example.com/")
326
+
327
+ call_args = mock_get.call_args[0]
328
+ self.assertEqual(call_args[0], "https://example.com/api/flags")
329
+
330
+
110
331
  @pytest.mark.parametrize(
111
332
  "host, expected",
112
333
  [
@@ -128,3 +349,47 @@ class TestRequests(unittest.TestCase):
128
349
  )
129
350
  def test_routing_to_custom_host(host, expected):
130
351
  assert determine_server_host(host) == expected
352
+
353
+
354
+ def test_enable_keep_alive_sets_socket_options():
355
+ try:
356
+ enable_keep_alive()
357
+ from posthog.request import _session
358
+
359
+ adapter = _session.get_adapter("https://example.com")
360
+ assert adapter.socket_options == KEEP_ALIVE_SOCKET_OPTIONS
361
+ finally:
362
+ set_socket_options(None)
363
+
364
+
365
+ def test_set_socket_options_clears_with_none():
366
+ try:
367
+ enable_keep_alive()
368
+ set_socket_options(None)
369
+ from posthog.request import _session
370
+
371
+ adapter = _session.get_adapter("https://example.com")
372
+ assert adapter.socket_options is None
373
+ finally:
374
+ set_socket_options(None)
375
+
376
+
377
+ def test_disable_connection_reuse_creates_fresh_sessions():
378
+ try:
379
+ disable_connection_reuse()
380
+ session1 = request_module._get_session()
381
+ session2 = request_module._get_session()
382
+ assert session1 is not session2
383
+ finally:
384
+ request_module._pooling_enabled = True
385
+
386
+
387
+ def test_set_socket_options_is_idempotent():
388
+ try:
389
+ enable_keep_alive()
390
+ session1 = request_module._session
391
+ enable_keep_alive()
392
+ session2 = request_module._session
393
+ assert session1 is session2
394
+ finally:
395
+ set_socket_options(None)
@@ -1,3 +1,4 @@
1
+ import sys
1
2
  import time
2
3
  import unittest
3
4
  from dataclasses import dataclass
@@ -122,7 +123,9 @@ class TestUtils(unittest.TestCase):
122
123
  "bar": 2,
123
124
  "baz": None,
124
125
  }
125
- assert utils.clean(ModelV1(foo=1, bar="2")) == {"foo": 1, "bar": "2"}
126
+ # Pydantic V1 is not compatible with Python 3.14+
127
+ if sys.version_info < (3, 14):
128
+ assert utils.clean(ModelV1(foo=1, bar="2")) == {"foo": 1, "bar": "2"}
126
129
  assert utils.clean(NestedModel(foo=ModelV2(foo="1", bar=2, baz="3"))) == {
127
130
  "foo": {"foo": "1", "bar": 2, "baz": "3"}
128
131
  }
posthog/types.py CHANGED
@@ -123,6 +123,7 @@ class FlagsResponse(TypedDict, total=False):
123
123
  errorsWhileComputingFlags: bool
124
124
  requestId: str
125
125
  quotaLimit: Optional[List[str]]
126
+ evaluatedAt: Optional[int]
126
127
 
127
128
 
128
129
  class FlagsAndPayloads(TypedDict, total=True):
@@ -306,3 +307,42 @@ def to_payloads(response: FlagsResponse) -> Optional[dict[str, str]]:
306
307
  and value.enabled
307
308
  and value.metadata.payload is not None
308
309
  }
310
+
311
+
312
+ class FeatureFlagError:
313
+ """Error type constants for the $feature_flag_error property.
314
+
315
+ These values are sent in analytics events to track flag evaluation failures.
316
+ They should not be changed without considering impact on existing dashboards
317
+ and queries that filter on these values.
318
+
319
+ Error values:
320
+ ERRORS_WHILE_COMPUTING: Server returned errorsWhileComputingFlags=true
321
+ FLAG_MISSING: Requested flag not in API response
322
+ QUOTA_LIMITED: Rate/quota limit exceeded
323
+ TIMEOUT: Request timed out
324
+ CONNECTION_ERROR: Network connectivity issue
325
+ UNKNOWN_ERROR: Unexpected exceptions
326
+
327
+ For API errors with status codes, use the api_error() method which returns
328
+ a string like "api_error_500".
329
+ """
330
+
331
+ ERRORS_WHILE_COMPUTING = "errors_while_computing_flags"
332
+ FLAG_MISSING = "flag_missing"
333
+ QUOTA_LIMITED = "quota_limited"
334
+ TIMEOUT = "timeout"
335
+ CONNECTION_ERROR = "connection_error"
336
+ UNKNOWN_ERROR = "unknown_error"
337
+
338
+ @staticmethod
339
+ def api_error(status: Union[int, str]) -> str:
340
+ """Generate API error string with status code.
341
+
342
+ Args:
343
+ status: HTTP status code from the API error
344
+
345
+ Returns:
346
+ Error string like "api_error_500"
347
+ """
348
+ return f"api_error_{status}"
posthog/version.py CHANGED
@@ -1,4 +1,4 @@
1
- VERSION = "7.0.0"
1
+ VERSION = "7.3.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: posthog
3
- Version: 7.0.0
3
+ Version: 7.3.1
4
4
  Summary: Integrate PostHog into any python application.
5
5
  Home-page: https://github.com/posthog/posthog-python
6
6
  Author: Posthog
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
21
  Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
22
23
  Requires-Python: >=3.10
23
24
  Description-Content-Type: text/markdown
24
25
  License-File: LICENSE
@@ -1,19 +1,20 @@
1
- posthog/__init__.py,sha256=3nTRvobcwGbU55ltU3tK1JjrzjO-l8NOG1JA3ceh9k8,27660
1
+ posthog/__init__.py,sha256=onbj2NNWstUUvWuwd8SdLQ2QIXMcB4JihsePbYj2Z64,28042
2
2
  posthog/args.py,sha256=JUt0vbtF33IzLt3ARgsxMEYYnZo3RNS_LcK4-CjWaco,3298
3
- posthog/client.py,sha256=FPCykXYOJ1jtiqxxmoe_dbvw5HKJH14wDHFJQyXSLjU,74076
3
+ posthog/client.py,sha256=poyc3X-gZwQHQmgreSMkSkmWsAOMKlwThmK8nm9n3mI,82092
4
4
  posthog/consumer.py,sha256=fdteMZ-deJGMpaQmHyznw_cwQG2Vvld1tmN9LUkZPrY,4608
5
5
  posthog/contexts.py,sha256=22z4KySFCTwPbz4OYsd_8EJpoc2H91NiLq9cscSvFfw,12600
6
6
  posthog/exception_capture.py,sha256=pmKtjQ6QY6zs4u_-ZA4H1gCyR3iI4sfqCQG_jwe_bKo,1774
7
- posthog/exception_utils.py,sha256=-pvsDuDlsTORFLLYAEhiii5VBKie3jbFgadMFSaix-g,31858
7
+ posthog/exception_utils.py,sha256=T3WexL76u7_-SjAfb7WWaD-ozwnmQuZ_453yGKRYrOk,33738
8
8
  posthog/feature_flags.py,sha256=4xAcYEpa97b5Lv9bIo5JHbCO6lhYBnH5EmJ2MrjbU3k,22517
9
+ posthog/flag_definition_cache.py,sha256=3LnB76BAU29FmypxagkqTpEW6MfjQQTw3E_xWJ679XQ,4697
9
10
  posthog/poller.py,sha256=jBz5rfH_kn_bBz7wCB46Fpvso4ttx4uzqIZWvXBCFmQ,595
10
11
  posthog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- posthog/request.py,sha256=CaONBN7a5RD8xiSShVMgHEd9XxKWM6ZQTLZypiqABhA,6168
12
- posthog/types.py,sha256=Dl3aFGX9XUR0wMmK12r2s5Hjan9jL4HpQ9GHpVcEq5U,10207
12
+ posthog/request.py,sha256=CfYneafeZH8bfQuAPHHjYIaWTLPl63YsZ66uBmn58vM,10349
13
+ posthog/types.py,sha256=OxGHSmmhVYwA7ecmJXUznDCZ1c4gAGtERzSLSYlyQFM,11540
13
14
  posthog/utils.py,sha256=-0w-OLcCaoldkbBebPzQyBzLJSo9G9yBOg8NDVz7La8,16088
14
- posthog/version.py,sha256=edN2hQk_R2GiV1TUr0Bsk7x67QYn6M0-GFUrIuzjNPg,87
15
+ posthog/version.py,sha256=ZP1gzAMj9_8ZAgoHQCpxcYzy3w0bR8IQuMtjzF3nWyg,87
15
16
  posthog/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- posthog/ai/sanitization.py,sha256=owipZ4eJYtd4JTI-CM_klatclXaeaIec3XJBOUfsOnQ,5770
17
+ posthog/ai/sanitization.py,sha256=Dpx_5gKZfDS38KjmK1C0lvvjm9N8Pp_oIxusac888-g,6057
17
18
  posthog/ai/types.py,sha256=arX98hR1PIPeJ3vFikxTlACIh1xPp6aEUw1gBLcKoB0,3273
18
19
  posthog/ai/utils.py,sha256=pMqL_Aydf08EvUuSVJ1SsIpNwaom6qYIoLNOvMBNSHU,21475
19
20
  posthog/ai/anthropic/__init__.py,sha256=8nTvETZzkfW-P3zBMmp06GOHs0N-xyOGu7Oa4di_lno,669
@@ -21,34 +22,36 @@ posthog/ai/anthropic/anthropic.py,sha256=UWyM6ryl5_VNQImaBi1RHN7tKXwkqaxy4yaXyPS
21
22
  posthog/ai/anthropic/anthropic_async.py,sha256=ppWHiVp4hTl62Zr3jIwXXidOsqhrwx6iHM3ukG7WiPM,8789
22
23
  posthog/ai/anthropic/anthropic_converter.py,sha256=prvaxt_R9kn9IbkxG_mLrw4kexT4i6T80U-6yhaZCNk,13053
23
24
  posthog/ai/anthropic/anthropic_providers.py,sha256=Q_v7U4wgieIkvii-Bqh4pLx5pEgbrHmgsCG8lUkKb_0,2103
24
- posthog/ai/gemini/__init__.py,sha256=JV_9-gBR87leHgZW4XAYZP7LSl4YaXeuhqDUpA8HygA,383
25
- posthog/ai/gemini/gemini.py,sha256=-c2MnBeask6SrAbFZ7XXZ_OMcuglTBRdnFe_ROVgXWQ,14972
26
- posthog/ai/gemini/gemini_converter.py,sha256=WFF1gzLGk1DId-1yrA9nDYMd9PXgbVsyhU3wgKjAJTE,18731
25
+ posthog/ai/gemini/__init__.py,sha256=W1c2YcMah5wi4lTk7w8l9Yabw4l7jashBaelYirLadQ,470
26
+ posthog/ai/gemini/gemini.py,sha256=RmmHriUoc2APrZtQiw63hJzBka0H-B3jYH29clxpciw,14958
27
+ posthog/ai/gemini/gemini_async.py,sha256=0UFk6ZHuG_F7dkw028BTyGybTpPvIKSNfw0yI_LciOM,15108
28
+ posthog/ai/gemini/gemini_converter.py,sha256=JptTOV98s3V0jPKKHVRktze1i0RaSbkkOT0GLxkt2Gk,21801
27
29
  posthog/ai/langchain/__init__.py,sha256=9CqAwLynTGj3ASAR80C3PmdTdrYGmu99tz0JL-HPFgI,70
28
30
  posthog/ai/langchain/callbacks.py,sha256=EhjXTok2M2w5k5x9XFycl-bUMA1gdZx6FxHAY-HZ6bo,31367
29
31
  posthog/ai/openai/__init__.py,sha256=u4OuUT7k1NgFj0TrxjuyegOg7a_UA8nAU6a-Hszr0OM,490
30
32
  posthog/ai/openai/openai.py,sha256=ts95vdvWH7h0TX4FpLLK_wU_7H0MP3eZBEg0S-lsCKw,20127
31
33
  posthog/ai/openai/openai_async.py,sha256=Ebd6_H3Zf3wGPycVJd_vOd3ZVoO3Mf3ZV339BExQd6Q,22436
32
- posthog/ai/openai/openai_converter.py,sha256=_T7Nx5gzGlklbu0iZjj8qaBB4W_IrnSFNUkUOav3TBE,25466
34
+ posthog/ai/openai/openai_converter.py,sha256=Miaqc9uzLRV_v-1zzn8Iqy8JsKb4l2Tjnw_1atp-8Xs,25828
33
35
  posthog/ai/openai/openai_providers.py,sha256=zQIFTXHS2-dBKQX7FZxTFo7rIj5iiN7VHm9_2RzuDs8,3941
34
36
  posthog/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
37
  posthog/integrations/django.py,sha256=9X37yCF-T-MXUsxqkqjBWG3kdgOCyQYYNJQG_ZlwbRg,12633
36
38
  posthog/test/__init__.py,sha256=VYgM6xPbJbvS-xhIcDiBRs0MFC9V_jT65uNeerCz_rM,299
37
39
  posthog/test/test_before_send.py,sha256=3546WKlk8rF6bhvqhwcxAsjJovDw0Hf8yTvcYGbrhyI,7912
38
- posthog/test/test_client.py,sha256=F-jUA0uKgHpVLOdnen2j6WSTp6whlJcpZdSecLoREFg,96273
40
+ posthog/test/test_client.py,sha256=cizqtsL7-13IDTZvSGg-FvEYY4Gcc1p_uzWl5CZwSl4,99805
39
41
  posthog/test/test_consumer.py,sha256=HRDXSH0IPpCfo5yHs23n-0VzFyGSjWBKLEa8XNtU3_Y,7080
40
42
  posthog/test/test_contexts.py,sha256=GDYpQNGhdzyA3--ia3WPao_4dqyLUpkWm1NMVm2L-So,7004
41
- posthog/test/test_exception_capture.py,sha256=tit980vqCvAq8W2UGJN-Mcz_BIKI4XZzmEU4y1Y4YaI,8939
43
+ posthog/test/test_exception_capture.py,sha256=nqG33mnxpMrSfPxXGoK6hz3yrQu3Yr-pV7r5_yYBZ68,13305
42
44
  posthog/test/test_feature_flag.py,sha256=yIMJkoRtdJr91Y6Rb0PPlpZWBIR394TgWhccnlf-vYE,6815
43
- posthog/test/test_feature_flag_result.py,sha256=jbdTgqlFbgvUlAoRWjguk3IvuzXgN2qbfn77gF_SqJU,15871
44
- posthog/test/test_feature_flags.py,sha256=JCSFtHhh60WoClmmuBMctSAOchFNqnZPlpfdi37zTMw,217298
45
+ posthog/test/test_feature_flag_result.py,sha256=KCQHismwddFWl-PHtRpZzcL5M45q_oUeHsST-zxzjkI,33321
46
+ posthog/test/test_feature_flags.py,sha256=5tfAYuzNjxgnxsQxlO7sDjmivUccTWf_26l_FEAJ3E8,222554
47
+ posthog/test/test_flag_definition_cache.py,sha256=_ssIKtrgNw3WsHv-GQNd_Nk-luDxAWgOVnAOMb8gWP8,21883
45
48
  posthog/test/test_module.py,sha256=CERR0dTPGsAmd7YBxK0yKeB2Zr2b_Lv7hNQoeJauc9I,813
46
- posthog/test/test_request.py,sha256=l19WVyZQc4Iqmh_bpnAFOj4nGRpDK1iO-o5aJDQfFdo,4449
49
+ posthog/test/test_request.py,sha256=PGJjA8tZ-9PrBZJI1EQGX03TocRFyCbjcRQatwoDvH4,14607
47
50
  posthog/test/test_size_limited_dict.py,sha256=Wom7BkzpHmusHilZy0SV3PNzhw7ucuQgqrx86jf8euo,765
48
51
  posthog/test/test_types.py,sha256=csLuBiz6RMV36cpg9LVIor4Khq6MfjjGxYXodx5VttY,7586
49
- posthog/test/test_utils.py,sha256=NUs2bgqrVuMdnKRq52syizgglt5_7wxxZl3dDMun-Tg,9602
50
- posthog-7.0.0.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
51
- posthog-7.0.0.dist-info/METADATA,sha256=kKD5z5ydkTz6-JjikDZaPRY407cZ68f9lJIrr0U9n-c,5959
52
- posthog-7.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
- posthog-7.0.0.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
54
- posthog-7.0.0.dist-info/RECORD,,
52
+ posthog/test/test_utils.py,sha256=YqAnXaMHxzEV_D3AHhs-RXnZYzdEN7kdIlpOT6Ti6t0,9714
53
+ posthog-7.3.1.dist-info/licenses/LICENSE,sha256=wGf9JBotDkSygFj43m49oiKlFnpMnn97keiZKF-40vE,2450
54
+ posthog-7.3.1.dist-info/METADATA,sha256=9bg4WHQ_dz-3wd0JanCkXIrJ2NzH96YvCbKkmVY0_64,6010
55
+ posthog-7.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
56
+ posthog-7.3.1.dist-info/top_level.txt,sha256=7FBLsRjIUHVKQsXIhozuI3k-mun1tapp8iZO9EmUPEw,8
57
+ posthog-7.3.1.dist-info/RECORD,,