Skip to content

Commit 63aeaa3

Browse files
committed
code review feedback
1 parent d271880 commit 63aeaa3

File tree

2 files changed

+62
-21
lines changed

2 files changed

+62
-21
lines changed

posthog/client.py

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import atexit
22
import logging
3+
from mailbox import NotEmptyError
34
import numbers
45
import sys
56
from datetime import datetime, timedelta
@@ -161,6 +162,15 @@ def get_feature_payloads(
161162
resp_data = self.get_decide(distinct_id, groups, person_properties, group_properties, disable_geoip)
162163
return resp_data["featureFlagPayloads"]
163164

165+
def get_feature_flags_and_payloads(
166+
self, distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None
167+
):
168+
resp_data = self.get_decide(distinct_id, groups, person_properties, group_properties, disable_geoip)
169+
return {
170+
"featureFlags": resp_data["featureFlags"],
171+
"featureFlagPayloads": resp_data["featureFlagPayloads"],
172+
}
173+
164174
def get_decide(self, distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None):
165175
require("distinct_id", distinct_id, ID_TYPES)
166176

@@ -723,23 +733,52 @@ def get_feature_flag_payload(
723733
groups=groups,
724734
person_properties=person_properties,
725735
group_properties=group_properties,
726-
send_feature_flag_events=send_feature_flag_events,
727-
only_evaluate_locally=only_evaluate_locally,
736+
send_feature_flag_events=False,
737+
# Disable automatic sending of feature flag events because we're manually handling event dispatch.
738+
# This prevents sending events with empty data when `get_feature_flag` cannot be evaluated locally.
739+
only_evaluate_locally=True, # Enable local evaluation of feature flags to avoid making multiple requests to `/decide`.
728740
disable_geoip=disable_geoip,
729741
)
730742

731743
response = None
744+
payload = None
732745

733746
if match_value is not None:
734-
response = self._compute_payload_locally(key, match_value)
747+
payload = self._compute_payload_locally(key, match_value)
748+
749+
flag_was_locally_evaluated = payload is not None
750+
if not flag_was_locally_evaluated and not only_evaluate_locally:
751+
try:
752+
responses_and_payloads = self.get_feature_flags_and_payloads(
753+
distinct_id, groups, person_properties, group_properties, disable_geoip
754+
)
755+
response = responses_and_payloads["featureFlags"].get(key, None)
756+
payload = responses_and_payloads["featureFlagPayloads"].get(str(key).lower(), None)
757+
except Exception as e:
758+
self.log.exception(f"[FEATURE FLAGS] Unable to get feature flags and payloads: {e}")
735759

736-
if response is None and not only_evaluate_locally:
737-
decide_payloads = self.get_feature_payloads(
738-
distinct_id, groups, person_properties, group_properties, disable_geoip
760+
feature_flag_reported_key = f"{key}_{str(response)}"
761+
762+
if (
763+
feature_flag_reported_key not in self.distinct_ids_feature_flags_reported[distinct_id]
764+
and send_feature_flag_events # noqa: W503
765+
):
766+
self.capture(
767+
distinct_id,
768+
"$feature_flag_called",
769+
{
770+
"$feature_flag": key,
771+
"$feature_flag_response": response,
772+
"$feature_flag_payload": payload,
773+
"locally_evaluated": flag_was_locally_evaluated,
774+
f"$feature/{key}": response,
775+
},
776+
groups=groups,
777+
disable_geoip=disable_geoip,
739778
)
740-
response = decide_payloads.get(str(key).lower(), None)
779+
self.distinct_ids_feature_flags_reported[distinct_id].add(feature_flag_reported_key)
741780

742-
return response
781+
return payload
743782

744783
def _compute_payload_locally(self, key, match_value):
745784
payload = None

posthog/test/test_feature_flags.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ def test_boolean_feature_flag_payloads_local(self, patch_decide):
16351635
@mock.patch.object(Client, "capture")
16361636
@mock.patch("posthog.client.decide")
16371637
def test_boolean_feature_flag_payload_decide(self, patch_decide, patch_capture):
1638-
patch_decide.return_value = {"featureFlagPayloads": {"person-flag": 300}}
1638+
patch_decide.return_value = {"featureFlags": {"person-flag": True}, "featureFlagPayloads": {"person-flag": 300}}
16391639
self.assertEqual(
16401640
self.client.get_feature_flag_payload(
16411641
"person-flag", "some-distinct-id", person_properties={"region": "USA"}
@@ -1649,7 +1649,7 @@ def test_boolean_feature_flag_payload_decide(self, patch_decide, patch_capture):
16491649
),
16501650
300,
16511651
)
1652-
self.assertEqual(patch_decide.call_count, 3)
1652+
self.assertEqual(patch_decide.call_count, 2)
16531653
self.assertEqual(patch_capture.call_count, 1)
16541654
patch_capture.reset_mock()
16551655

@@ -2341,7 +2341,7 @@ def test_capture_is_called(self, patch_decide, patch_capture):
23412341
@mock.patch("posthog.client.decide")
23422342
def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch_capture):
23432343
patch_decide.return_value = {
2344-
"featureFlags": {"decide-flag": "decide-value"},
2344+
"featureFlags": {"person-flag": True},
23452345
"featureFlagPayloads": {"person-flag": 300},
23462346
}
23472347
client = Client(api_key=FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
@@ -2350,7 +2350,7 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch
23502350
{
23512351
"id": 1,
23522352
"name": "Beta Feature",
2353-
"key": "complex-flag",
2353+
"key": "person-flag",
23542354
"is_simple_flag": False,
23552355
"active": True,
23562356
"filters": {
@@ -2366,7 +2366,7 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch
23662366

23672367
# Call get_feature_flag_payload with match_value=None to trigger get_feature_flag
23682368
client.get_feature_flag_payload(
2369-
key="complex-flag", distinct_id="some-distinct-id", person_properties={"region": "USA", "name": "Aloha"}
2369+
key="person-flag", distinct_id="some-distinct-id", person_properties={"region": "USA", "name": "Aloha"}
23702370
)
23712371

23722372
# Assert that capture was called once, with the correct parameters
@@ -2375,10 +2375,11 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch
23752375
"some-distinct-id",
23762376
"$feature_flag_called",
23772377
{
2378-
"$feature_flag": "complex-flag",
2378+
"$feature_flag": "person-flag",
23792379
"$feature_flag_response": True,
2380-
"locally_evaluated": True,
2381-
"$feature/complex-flag": True,
2380+
"$feature_flag_payload": 300,
2381+
"locally_evaluated": False,
2382+
"$feature/person-flag": True,
23822383
},
23832384
groups={},
23842385
disable_geoip=None,
@@ -2390,26 +2391,27 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch
23902391

23912392
# Call get_feature_flag_payload again for the same user; capture should not be called again because we've already reported an event for this distinct_id + flag
23922393
client.get_feature_flag_payload(
2393-
key="complex-flag", distinct_id="some-distinct-id", person_properties={"region": "USA", "name": "Aloha"}
2394+
key="person-flag", distinct_id="some-distinct-id", person_properties={"region": "USA", "name": "Aloha"}
23942395
)
23952396

23962397
self.assertEqual(patch_capture.call_count, 0)
23972398
patch_capture.reset_mock()
23982399

23992400
# Call get_feature_flag_payload for a different user; capture should be called
24002401
client.get_feature_flag_payload(
2401-
key="complex-flag", distinct_id="some-distinct-id2", person_properties={"region": "USA", "name": "Aloha"}
2402+
key="person-flag", distinct_id="some-distinct-id2", person_properties={"region": "USA", "name": "Aloha"}
24022403
)
24032404

24042405
self.assertEqual(patch_capture.call_count, 1)
24052406
patch_capture.assert_called_with(
24062407
"some-distinct-id2",
24072408
"$feature_flag_called",
24082409
{
2409-
"$feature_flag": "complex-flag",
2410+
"$feature_flag": "person-flag",
24102411
"$feature_flag_response": True,
2411-
"locally_evaluated": True,
2412-
"$feature/complex-flag": True,
2412+
"$feature_flag_payload": 300,
2413+
"locally_evaluated": False,
2414+
"$feature/person-flag": True,
24132415
},
24142416
groups={},
24152417
disable_geoip=None,

0 commit comments

Comments
 (0)