posthoganalytics 6.0.3__py3-none-any.whl → 6.1.0__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.
- posthoganalytics/__init__.py +4 -0
- posthoganalytics/client.py +7 -1
- posthoganalytics/integrations/django.py +11 -2
- posthoganalytics/test/test_client.py +90 -0
- posthoganalytics/version.py +1 -1
- {posthoganalytics-6.0.3.dist-info → posthoganalytics-6.1.0.dist-info}/METADATA +1 -1
- {posthoganalytics-6.0.3.dist-info → posthoganalytics-6.1.0.dist-info}/RECORD +10 -10
- {posthoganalytics-6.0.3.dist-info → posthoganalytics-6.1.0.dist-info}/WHEEL +0 -0
- {posthoganalytics-6.0.3.dist-info → posthoganalytics-6.1.0.dist-info}/licenses/LICENSE +0 -0
- {posthoganalytics-6.0.3.dist-info → posthoganalytics-6.1.0.dist-info}/top_level.txt +0 -0
posthoganalytics/__init__.py
CHANGED
|
@@ -60,6 +60,9 @@ log_captured_exceptions = False # type: bool
|
|
|
60
60
|
project_root = None # type: Optional[str]
|
|
61
61
|
# Used for our AI observability feature to not capture any prompt or output just usage + metadata
|
|
62
62
|
privacy_mode = False # type: bool
|
|
63
|
+
# Whether to enable feature flag polling for local evaluation by default. Defaults to True.
|
|
64
|
+
# We recommend setting this to False if you are only using the personalApiKey for evaluating remote config payloads via `get_remote_config_payload` and not using local evaluation.
|
|
65
|
+
enable_local_evaluation = True # type: bool
|
|
63
66
|
|
|
64
67
|
default_client = None # type: Optional[Client]
|
|
65
68
|
|
|
@@ -465,6 +468,7 @@ def setup():
|
|
|
465
468
|
# or deprecate this proxy option fully (it's already in the process of deprecation, no new clients should be using this method since like 5-6 months)
|
|
466
469
|
enable_exception_autocapture=enable_exception_autocapture,
|
|
467
470
|
log_captured_exceptions=log_captured_exceptions,
|
|
471
|
+
enable_local_evaluation=enable_local_evaluation,
|
|
468
472
|
)
|
|
469
473
|
|
|
470
474
|
# always set incase user changes it
|
posthoganalytics/client.py
CHANGED
|
@@ -152,6 +152,7 @@ class Client(object):
|
|
|
152
152
|
privacy_mode=False,
|
|
153
153
|
before_send=None,
|
|
154
154
|
flag_fallback_cache_url=None,
|
|
155
|
+
enable_local_evaluation=True,
|
|
155
156
|
):
|
|
156
157
|
self.queue = queue.Queue(max_queue_size)
|
|
157
158
|
|
|
@@ -187,6 +188,7 @@ class Client(object):
|
|
|
187
188
|
self.log_captured_exceptions = log_captured_exceptions
|
|
188
189
|
self.exception_capture = None
|
|
189
190
|
self.privacy_mode = privacy_mode
|
|
191
|
+
self.enable_local_evaluation = enable_local_evaluation
|
|
190
192
|
|
|
191
193
|
if project_root is None:
|
|
192
194
|
try:
|
|
@@ -807,7 +809,11 @@ class Client(object):
|
|
|
807
809
|
return
|
|
808
810
|
|
|
809
811
|
self._load_feature_flags()
|
|
810
|
-
|
|
812
|
+
|
|
813
|
+
# Only start the poller if local evaluation is enabled
|
|
814
|
+
if self.enable_local_evaluation and not (
|
|
815
|
+
self.poller and self.poller.is_alive()
|
|
816
|
+
):
|
|
811
817
|
self.poller = Poller(
|
|
812
818
|
interval=timedelta(seconds=self.poll_interval),
|
|
813
819
|
execute=self._load_feature_flags,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, cast
|
|
2
2
|
from posthoganalytics import contexts
|
|
3
|
+
from posthoganalytics.client import Client
|
|
3
4
|
|
|
4
5
|
if TYPE_CHECKING:
|
|
5
6
|
from django.http import HttpRequest, HttpResponse # noqa: F401
|
|
@@ -16,7 +17,8 @@ class PosthogContextMiddleware:
|
|
|
16
17
|
- Request Method as $request_method
|
|
17
18
|
|
|
18
19
|
The context will also auto-capture exceptions and send them to PostHog, unless you disable it by setting
|
|
19
|
-
`POSTHOG_MW_CAPTURE_EXCEPTIONS` to `False` in your Django settings.
|
|
20
|
+
`POSTHOG_MW_CAPTURE_EXCEPTIONS` to `False` in your Django settings. The exceptions are captured using the
|
|
21
|
+
global client, unless the setting `POSTHOG_MW_CLIENT` is set to a custom client instance
|
|
20
22
|
|
|
21
23
|
The middleware behaviour is customisable through 3 additional functions:
|
|
22
24
|
- `POSTHOG_MW_EXTRA_TAGS`, which is a Callable[[HttpRequest], Dict[str, Any]] expected to return a dictionary of additional tags to be added to the context.
|
|
@@ -74,6 +76,13 @@ class PosthogContextMiddleware:
|
|
|
74
76
|
else:
|
|
75
77
|
self.capture_exceptions = True
|
|
76
78
|
|
|
79
|
+
if hasattr(settings, "POSTHOG_MW_CLIENT") and isinstance(
|
|
80
|
+
settings.POSTHOG_MW_CLIENT, Client
|
|
81
|
+
):
|
|
82
|
+
self.client = cast("Optional[Client]", settings.POSTHOG_MW_CLIENT)
|
|
83
|
+
else:
|
|
84
|
+
self.client = None
|
|
85
|
+
|
|
77
86
|
def extract_tags(self, request):
|
|
78
87
|
# type: (HttpRequest) -> Dict[str, Any]
|
|
79
88
|
tags = {}
|
|
@@ -153,7 +162,7 @@ class PosthogContextMiddleware:
|
|
|
153
162
|
if self.request_filter and not self.request_filter(request):
|
|
154
163
|
return self.get_response(request)
|
|
155
164
|
|
|
156
|
-
with contexts.new_context(self.capture_exceptions):
|
|
165
|
+
with contexts.new_context(self.capture_exceptions, client=self.client):
|
|
157
166
|
for k, v in self.extract_tags(request).items():
|
|
158
167
|
contexts.tag(k, v)
|
|
159
168
|
|
|
@@ -1903,3 +1903,93 @@ class TestClient(unittest.TestCase):
|
|
|
1903
1903
|
self.assertEqual(
|
|
1904
1904
|
msg["properties"]["$session_id"], "explicit-session-override"
|
|
1905
1905
|
)
|
|
1906
|
+
|
|
1907
|
+
@mock.patch("posthog.client.Poller")
|
|
1908
|
+
@mock.patch("posthog.client.get")
|
|
1909
|
+
def test_enable_local_evaluation_false_disables_poller(
|
|
1910
|
+
self, patch_get, patch_poller
|
|
1911
|
+
):
|
|
1912
|
+
"""Test that when enable_local_evaluation=False, the poller is not started"""
|
|
1913
|
+
patch_get.return_value = {
|
|
1914
|
+
"flags": [
|
|
1915
|
+
{"id": 1, "name": "Beta Feature", "key": "beta-feature", "active": True}
|
|
1916
|
+
],
|
|
1917
|
+
"group_type_mapping": {},
|
|
1918
|
+
"cohorts": {},
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
client = Client(
|
|
1922
|
+
FAKE_TEST_API_KEY,
|
|
1923
|
+
personal_api_key="test-personal-key",
|
|
1924
|
+
enable_local_evaluation=False,
|
|
1925
|
+
)
|
|
1926
|
+
|
|
1927
|
+
# Load feature flags should not start the poller
|
|
1928
|
+
client.load_feature_flags()
|
|
1929
|
+
|
|
1930
|
+
# Assert that the poller was not created/started
|
|
1931
|
+
patch_poller.assert_not_called()
|
|
1932
|
+
# But the feature flags should still be loaded
|
|
1933
|
+
patch_get.assert_called_once()
|
|
1934
|
+
self.assertEqual(len(client.feature_flags), 1)
|
|
1935
|
+
self.assertEqual(client.feature_flags[0]["key"], "beta-feature")
|
|
1936
|
+
|
|
1937
|
+
@mock.patch("posthog.client.Poller")
|
|
1938
|
+
@mock.patch("posthog.client.get")
|
|
1939
|
+
def test_enable_local_evaluation_true_starts_poller(self, patch_get, patch_poller):
|
|
1940
|
+
"""Test that when enable_local_evaluation=True (default), the poller is started"""
|
|
1941
|
+
patch_get.return_value = {
|
|
1942
|
+
"flags": [
|
|
1943
|
+
{"id": 1, "name": "Beta Feature", "key": "beta-feature", "active": True}
|
|
1944
|
+
],
|
|
1945
|
+
"group_type_mapping": {},
|
|
1946
|
+
"cohorts": {},
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
client = Client(
|
|
1950
|
+
FAKE_TEST_API_KEY,
|
|
1951
|
+
personal_api_key="test-personal-key",
|
|
1952
|
+
enable_local_evaluation=True,
|
|
1953
|
+
)
|
|
1954
|
+
|
|
1955
|
+
# Load feature flags should start the poller
|
|
1956
|
+
client.load_feature_flags()
|
|
1957
|
+
|
|
1958
|
+
# Assert that the poller was created and started
|
|
1959
|
+
patch_poller.assert_called_once()
|
|
1960
|
+
patch_get.assert_called_once()
|
|
1961
|
+
self.assertEqual(len(client.feature_flags), 1)
|
|
1962
|
+
self.assertEqual(client.feature_flags[0]["key"], "beta-feature")
|
|
1963
|
+
|
|
1964
|
+
@mock.patch("posthog.client.remote_config")
|
|
1965
|
+
def test_get_remote_config_payload_works_without_poller(self, patch_remote_config):
|
|
1966
|
+
"""Test that get_remote_config_payload works without local evaluation enabled"""
|
|
1967
|
+
patch_remote_config.return_value = {"test": "payload"}
|
|
1968
|
+
|
|
1969
|
+
client = Client(
|
|
1970
|
+
FAKE_TEST_API_KEY,
|
|
1971
|
+
personal_api_key="test-personal-key",
|
|
1972
|
+
enable_local_evaluation=False,
|
|
1973
|
+
)
|
|
1974
|
+
|
|
1975
|
+
# Should work without poller
|
|
1976
|
+
result = client.get_remote_config_payload("test-flag")
|
|
1977
|
+
|
|
1978
|
+
self.assertEqual(result, {"test": "payload"})
|
|
1979
|
+
patch_remote_config.assert_called_once_with(
|
|
1980
|
+
"test-personal-key",
|
|
1981
|
+
client.host,
|
|
1982
|
+
"test-flag",
|
|
1983
|
+
timeout=client.feature_flags_request_timeout_seconds,
|
|
1984
|
+
)
|
|
1985
|
+
|
|
1986
|
+
def test_get_remote_config_payload_requires_personal_api_key(self):
|
|
1987
|
+
"""Test that get_remote_config_payload requires personal API key"""
|
|
1988
|
+
client = Client(
|
|
1989
|
+
FAKE_TEST_API_KEY,
|
|
1990
|
+
enable_local_evaluation=False,
|
|
1991
|
+
)
|
|
1992
|
+
|
|
1993
|
+
result = client.get_remote_config_payload("test-flag")
|
|
1994
|
+
|
|
1995
|
+
self.assertIsNone(result)
|
posthoganalytics/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
posthoganalytics/__init__.py,sha256=
|
|
1
|
+
posthoganalytics/__init__.py,sha256=U8EmLKYL0N-k-kPb-R0c36MXtaGkA7PmFnhek3Xga5k,17070
|
|
2
2
|
posthoganalytics/args.py,sha256=hRKPQ3cPGyDn4S7Ay9t2NlgoZg1cJ0GeN_Mb6OKtmfo,3145
|
|
3
|
-
posthoganalytics/client.py,sha256=
|
|
3
|
+
posthoganalytics/client.py,sha256=w2br3pz_1sV3IF39SZavukXowbDG8phrx9sHlltdgmc,50971
|
|
4
4
|
posthoganalytics/consumer.py,sha256=CiNbJBdyW9jER3ZYCKbX-JFmEDXlE1lbDy1MSl43-a0,4617
|
|
5
5
|
posthoganalytics/contexts.py,sha256=B3Y62sX7w-MCqNqgguUceQnKn5RCBFIqen3VeR3qems,9020
|
|
6
6
|
posthoganalytics/exception_capture.py,sha256=1VHBfffrXXrkK0PT8iVgKPpj_R1pGAzG5f3Qw0WF79w,1783
|
|
@@ -11,7 +11,7 @@ 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=
|
|
14
|
+
posthoganalytics/version.py,sha256=t8G8Y2aHP3Z3Ej7UE-_LT8EK7Gi6rurkCbuJWBqvhXE,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,10 +27,10 @@ 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=
|
|
30
|
+
posthoganalytics/integrations/django.py,sha256=BdBo6nvY6cjKMYPpKTzvwiICCLIhCOyslgqwFCObPzo,6441
|
|
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
|
-
posthoganalytics/test/test_client.py,sha256=
|
|
33
|
+
posthoganalytics/test/test_client.py,sha256=fghypNGUqOY9OmycsdNK5SsyCoimxYVisFfFq2nkTCU,77840
|
|
34
34
|
posthoganalytics/test/test_consumer.py,sha256=HGMfU9PzQ5ZAe_R3kHnZNsMvD7jUjHL-gie0isrvMMk,7107
|
|
35
35
|
posthoganalytics/test/test_contexts.py,sha256=c--hNUIEf6SHQ7H9vdPhU1oLCN0SnD4wDbFr-eLPHDo,7013
|
|
36
36
|
posthoganalytics/test/test_exception_capture.py,sha256=al37Kg6wjzL_IBCFUUXRvkP6nVrqS6IZRCOKSo29Nh8,1063
|
|
@@ -42,8 +42,8 @@ posthoganalytics/test/test_request.py,sha256=Zc0VbkjpVmj8mKokQm9rzdgTr0b1U44vvMY
|
|
|
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.0.
|
|
46
|
-
posthoganalytics-6.0.
|
|
47
|
-
posthoganalytics-6.0.
|
|
48
|
-
posthoganalytics-6.0.
|
|
49
|
-
posthoganalytics-6.0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|