Skip to content

Commit 54327df

Browse files
committed
Lookup local flag by key
Fixes #121
1 parent 6923891 commit 54327df

File tree

2 files changed

+43
-21
lines changed

2 files changed

+43
-21
lines changed

posthog/client.py

+41-19
Original file line numberDiff line numberDiff line change
@@ -206,14 +206,21 @@ def __init__(
206206

207207
@property
208208
def feature_flags(self):
209+
"""
210+
Get the local evaluation feature flags.
211+
"""
209212
return self._feature_flags
210213

211214
@feature_flags.setter
212215
def feature_flags(self, flags):
216+
"""
217+
Set the local evaluation feature flags.
218+
"""
213219
self._feature_flags = flags or []
214220
self.feature_flags_by_key = {
215221
flag["key"]: flag for flag in self._feature_flags if flag.get("key") is not None
216222
}
223+
assert self.feature_flags_by_key is not None, "feature_flags_by_key should be initialized when feature_flags is set"
217224

218225
def identify(self, distinct_id=None, properties=None, context=None, timestamp=None, uuid=None, disable_geoip=None):
219226
if context is not None:
@@ -240,18 +247,27 @@ def identify(self, distinct_id=None, properties=None, context=None, timestamp=No
240247
def get_feature_variants(
241248
self, distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None
242249
) -> dict[str, str | bool]:
250+
"""
251+
Get feature flag variants for a distinct_id by calling decide.
252+
"""
243253
resp_data = self.get_decide(distinct_id, groups, person_properties, group_properties, disable_geoip)
244254
return to_values(resp_data) or {}
245255

246256
def get_feature_payloads(
247257
self, distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None
248258
) -> dict[str, str]:
259+
"""
260+
Get feature flag payloads for a distinct_id by calling decide.
261+
"""
249262
resp_data = self.get_decide(distinct_id, groups, person_properties, group_properties, disable_geoip)
250263
return to_payloads(resp_data) or {}
251264

252265
def get_feature_flags_and_payloads(
253266
self, distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None
254267
) -> FlagsAndPayloads:
268+
"""
269+
Get feature flags and payloads for a distinct_id by calling decide.
270+
"""
255271
resp = self.get_decide(distinct_id, groups, person_properties, group_properties, disable_geoip)
256272
return to_flags_and_payloads(resp)
257273

@@ -783,7 +799,14 @@ def get_feature_flag(
783799
only_evaluate_locally=False,
784800
send_feature_flag_events=True,
785801
disable_geoip=None,
786-
):
802+
) -> FlagValue | None:
803+
"""
804+
Get a feature flag value for a key by evaluating locally or remotely
805+
depending on whether local evaluation is enabled and the flag can be
806+
locally evaluated.
807+
808+
This also captures the $feature_flag_called event unless send_feature_flag_events is False.
809+
"""
787810
require("key", key, string_types)
788811
require("distinct_id", distinct_id, ID_TYPES)
789812
require("groups", groups, dict)
@@ -800,24 +823,23 @@ def get_feature_flag(
800823
response = None
801824

802825
if self.feature_flags:
803-
for flag in self.feature_flags:
804-
if flag["key"] == key:
805-
try:
806-
response = self._compute_flag_locally(
807-
flag,
808-
distinct_id,
809-
groups=groups,
810-
person_properties=person_properties,
811-
group_properties=group_properties,
812-
)
813-
self.log.debug(f"Successfully computed flag locally: {key} -> {response}")
814-
except InconclusiveMatchError as e:
815-
self.log.debug(f"Failed to compute flag {key} locally: {e}")
816-
continue
817-
except Exception as e:
818-
self.log.exception(f"[FEATURE FLAGS] Error while computing variant locally: {e}")
819-
continue
820-
break
826+
assert self.feature_flags_by_key is not None, "feature_flags_by_key should be initialized when feature_flags is set"
827+
# Local evaluation
828+
flag = self.feature_flags_by_key.get(key)
829+
if flag:
830+
try:
831+
response = self._compute_flag_locally(
832+
flag,
833+
distinct_id,
834+
groups=groups,
835+
person_properties=person_properties,
836+
group_properties=group_properties,
837+
)
838+
self.log.debug(f"Successfully computed flag locally: {key} -> {response}")
839+
except InconclusiveMatchError as e:
840+
self.log.debug(f"Failed to compute flag {key} locally: {e}")
841+
except Exception as e:
842+
self.log.exception(f"[FEATURE FLAGS] Error while computing variant locally: {e}")
821843

822844
flag_was_locally_evaluated = response is not None
823845
if not flag_was_locally_evaluated and not only_evaluate_locally:

posthog/types.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def normalize_decide_response(resp: Any) -> DecideResponse:
8484
resp["flags"] = flags
8585
return cast(DecideResponse, resp)
8686

87-
def to_flags_and_payloads(resp: DecideResponse) -> tuple[dict[str, FlagValue], dict[str, Any], bool]:
87+
def to_flags_and_payloads(resp: DecideResponse) -> FlagsAndPayloads:
8888
"""
8989
Convert a DecideResponse into a FlagsAndPayloads object which is a
9090
dict of feature flags and their payloads. This is needed by certain
@@ -102,7 +102,7 @@ def to_flags_and_payloads(resp: DecideResponse) -> tuple[dict[str, FlagValue], d
102102
"featureFlagPayloads": to_payloads(resp)
103103
}
104104

105-
def to_values(response: DecideResponse) -> dict[str, bool | str] | None:
105+
def to_values(response: DecideResponse) -> dict[str, FlagValue] | None:
106106
if "flags" not in response:
107107
return None
108108

0 commit comments

Comments
 (0)