-
Notifications
You must be signed in to change notification settings - Fork 66
feat: add evaluate_flags() API for single-call flag evaluation #539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c6d1244
e8984df
17023b2
425dbb5
95eb1e9
eda573d
db6d3c0
7edcf03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| --- | ||
| pypi/posthog: minor | ||
| --- | ||
|
|
||
| Add `evaluate_flags()` and a new `flags` option on `capture()` so a single `/flags` call can power both flag branching and event enrichment per request: | ||
|
|
||
| ```python | ||
| flags = posthog.evaluate_flags(distinct_id, person_properties={"plan": "enterprise"}) | ||
| if flags.is_enabled("new-dashboard"): | ||
| render_new_dashboard() | ||
| posthog.capture("page_viewed", distinct_id=distinct_id, flags=flags) | ||
| ``` | ||
|
|
||
| The returned `FeatureFlagEvaluations` snapshot exposes `is_enabled()`, `get_flag()`, `get_flag_payload()` for branching and `only_accessed()` / `only([keys])` filter helpers. Pass `flag_keys=[...]` to `evaluate_flags()` to scope the underlying `/flags` request itself. | ||
|
|
||
| Deprecates `feature_enabled()`, `get_feature_flag()`, `get_feature_flag_payload()`, and `capture(send_feature_flags=...)`. They continue to work but now emit a `DeprecationWarning` pointing at `evaluate_flags()`. Removal is planned for the next major version. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,9 @@ | |
| DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS, | ||
| DEFAULT_CODE_VARIABLES_MASK_PATTERNS, | ||
| ) | ||
| from posthog.feature_flag_evaluations import ( | ||
| FeatureFlagEvaluations as FeatureFlagEvaluations, | ||
| ) | ||
| from posthog.feature_flags import ( | ||
| InconclusiveMatchError as InconclusiveMatchError, | ||
| ) | ||
|
|
@@ -770,6 +773,66 @@ def get_all_flags_and_payloads( | |
| ) | ||
|
|
||
|
|
||
| def evaluate_flags( | ||
| distinct_id=None, # type: Optional[str] | ||
| groups=None, # type: Optional[Dict[str, str]] | ||
| person_properties=None, # type: Optional[Dict[str, Any]] | ||
| group_properties=None, # type: Optional[Dict[str, Dict[str, Any]]] | ||
| only_evaluate_locally=False, # type: bool | ||
| disable_geoip=None, # type: Optional[bool] | ||
| flag_keys=None, # type: Optional[list] | ||
| device_id=None, # type: Optional[str] | ||
| ) -> FeatureFlagEvaluations: | ||
|
Comment on lines
+776
to
+785
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. surfaced when documenting: we should include a this might be relevant for other sdks as well?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good shout! So most of the other SDKs didn't actually support this yet – it was only this one and JS that supported that operation. I do think we should support |
||
| """Evaluate all feature flags for a user in a single call and return a | ||
| :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` / | ||
| ``.get_flag()`` and pass the same snapshot to ``capture()`` via the | ||
| ``flags`` option so events carry the exact flag values the code branched on. | ||
|
|
||
| Prefer this over repeated ``get_feature_flag()`` calls and over | ||
| ``capture(send_feature_flags=True)`` — it consolidates flag evaluation into | ||
| a single ``/flags`` request per incoming request. | ||
|
|
||
| Args: | ||
| distinct_id: The user's distinct ID. If ``None``, falls back to the context | ||
| distinct_id. If still unresolvable, returns an empty snapshot. | ||
| groups: Mapping of group type to group key. | ||
| person_properties: Person properties to use for evaluation. | ||
| group_properties: Group properties keyed by group type. | ||
| only_evaluate_locally: If ``True``, never fall back to remote evaluation. | ||
| disable_geoip: Whether to disable GeoIP lookup. | ||
| flag_keys: Optional list of flag keys. When provided, only these flags are | ||
| evaluated — the underlying ``/flags`` request asks the server for just | ||
| this subset, which makes the response smaller and the request cheaper. | ||
| Use this when you only need a handful of flags out of many. | ||
| device_id: Optional device ID override. If not provided, falls back to the | ||
| context device_id (which may be set via tracing headers). Used by | ||
| experience-continuity flags to match users across distinct_id changes. | ||
|
|
||
| Examples: | ||
| ```python | ||
| from posthog import evaluate_flags, capture | ||
| flags = evaluate_flags("user_123", person_properties={"plan": "enterprise"}) | ||
| if flags.is_enabled("new-dashboard"): | ||
| render_new_dashboard() | ||
| capture("page_viewed", distinct_id="user_123", flags=flags) | ||
| ``` | ||
|
|
||
| Category: | ||
| Feature flags | ||
| """ | ||
| return _proxy( | ||
| "evaluate_flags", | ||
| distinct_id=distinct_id, | ||
| groups=groups, | ||
| person_properties=person_properties, | ||
| group_properties=group_properties, | ||
| only_evaluate_locally=only_evaluate_locally, | ||
| disable_geoip=disable_geoip, | ||
| flag_keys=flag_keys, | ||
| device_id=device_id, | ||
| ) | ||
|
|
||
|
|
||
| def feature_flag_definitions(): | ||
| """ | ||
| Returns loaded feature flags. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's deprecate
get_feature_flag_resultas well. It lived a short life, but better to clean it up now.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!