From 6eff5f14655601ea8b7664efb87dc46f7d58b9cc Mon Sep 17 00:00:00 2001 From: Kim Gustyr Date: Wed, 29 Oct 2025 17:42:57 +0000 Subject: [PATCH 1/3] fix: Multivariate segment overrides not supported --- .gitmodules | 2 +- flag_engine/segments/evaluator.py | 31 +++++++++---------- tests/engine_tests/engine-test-data | 2 +- .../unit/segments/test_segments_evaluator.py | 9 +++--- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/.gitmodules b/.gitmodules index c26ad3a..697fec5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "tests/engine_tests/engine-test-data"] path = tests/engine_tests/engine-test-data url = https://github.com/flagsmith/engine-test-data.git - tag = v3.2.0 + branch = feat/mv-regression diff --git a/flag_engine/segments/evaluator.py b/flag_engine/segments/evaluator.py index 8389b70..ba35070 100644 --- a/flag_engine/segments/evaluator.py +++ b/flag_engine/segments/evaluator.py @@ -146,38 +146,35 @@ def evaluate_features( if segment_override := segment_overrides.get( feature_context["name"], ): - feature_context = segment_override["feature_context"] - flag_result: FlagResult[FeatureMetadataT] - flags[feature_name] = flag_result = { - "enabled": feature_context["enabled"], - "name": feature_context["name"], - "reason": f"TARGETING_MATCH; segment={segment_override['segment_name']}", - "value": feature_context.get("value"), - } - if feature_metadata := feature_context.get("metadata"): - flag_result["metadata"] = feature_metadata + flags[feature_name] = get_flag_result_from_context( + context=context, + feature_context=segment_override["feature_context"], + reason=f"TARGETING_MATCH; segment={segment_override['segment_name']}", + ) continue flags[feature_name] = get_flag_result_from_context( context=context, - feature_name=feature_name, + feature_context=context["features"][feature_name], + reason="DEFAULT", ) return flags def get_flag_result_from_context( - context: EvaluationContext[typing.Any, FeatureMetadataT], - feature_name: str, + context: _EvaluationContextAnyMeta, + feature_context: FeatureContext[FeatureMetadataT], + reason: str, ) -> FlagResult[FeatureMetadataT]: """ Get a feature value from the evaluation context for a given feature name. - :param context: the evaluation context - :param feature_name: the feature name to get the value for + :param context: evaluation context + :param feature_context: feature context + :param reason: reason to use when no variant selected :return: the value for the feature name in the evaluation context """ - feature_context = context["features"][feature_name] key = _get_identity_key(context) flag_result: typing.Optional[FlagResult[FeatureMetadataT]] = None @@ -209,7 +206,7 @@ def get_flag_result_from_context( flag_result = { "enabled": feature_context["enabled"], "name": feature_context["name"], - "reason": "DEFAULT", + "reason": reason, "value": feature_context["value"], } diff --git a/tests/engine_tests/engine-test-data b/tests/engine_tests/engine-test-data index 97ab814..29b86ec 160000 --- a/tests/engine_tests/engine-test-data +++ b/tests/engine_tests/engine-test-data @@ -1 +1 @@ -Subproject commit 97ab814316ecb95896878a0eaa26c9c5d0300361 +Subproject commit 29b86ec40521c85c2fa20dfa7da202428cbb653e diff --git a/tests/unit/segments/test_segments_evaluator.py b/tests/unit/segments/test_segments_evaluator.py index 2f0a1c1..844d4fa 100644 --- a/tests/unit/segments/test_segments_evaluator.py +++ b/tests/unit/segments/test_segments_evaluator.py @@ -856,13 +856,13 @@ def test_get_flag_result_from_context__calls_returns_expected( {"value": "bar", "weight": 30, "priority": 2}, ], } - context["features"]["my_feature"] = feature_context context["identity"] = {"identifier": expected_key, "key": expected_key} # When result = get_flag_result_from_context( context=context, - feature_name="my_feature", + feature_context=feature_context, + reason="DEFAULT", ) # the value of the feature state is correct based on the percentage value returned @@ -899,15 +899,14 @@ def test_get_flag_result_from_feature_context__null_key__calls_returns_expected( ], } - context["features"]["my_feature"] = feature_context - # no identity context present context["identity"] = None # When result = get_flag_result_from_context( context=context, - feature_name="my_feature", + feature_context=feature_context, + reason="DEFAULT", ) # Then From 2006de4859ed017203f41b7d262f8602f83e1e6c Mon Sep 17 00:00:00 2001 From: Kim Gustyr Date: Thu, 30 Oct 2025 10:43:33 +0000 Subject: [PATCH 2/3] bump engine-test-data --- .gitmodules | 2 +- tests/engine_tests/engine-test-data | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 697fec5..8a03ce7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "tests/engine_tests/engine-test-data"] path = tests/engine_tests/engine-test-data url = https://github.com/flagsmith/engine-test-data.git - branch = feat/mv-regression + tag = v3.3.0 diff --git a/tests/engine_tests/engine-test-data b/tests/engine_tests/engine-test-data index 29b86ec..024f7bf 160000 --- a/tests/engine_tests/engine-test-data +++ b/tests/engine_tests/engine-test-data @@ -1 +1 @@ -Subproject commit 29b86ec40521c85c2fa20dfa7da202428cbb653e +Subproject commit 024f7bf6d2aa2090fae69874c87cf39e3d8b711e From fe14ab43cb0b1923bfb24738059d39823a28506a Mon Sep 17 00:00:00 2001 From: Kim Gustyr Date: Thu, 30 Oct 2025 10:54:54 +0000 Subject: [PATCH 3/3] code improvements --- flag_engine/segments/evaluator.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/flag_engine/segments/evaluator.py b/flag_engine/segments/evaluator.py index ba35070..abf3013 100644 --- a/flag_engine/segments/evaluator.py +++ b/flag_engine/segments/evaluator.py @@ -139,13 +139,14 @@ def evaluate_features( context: EvaluationContext[typing.Any, FeatureMetadataT], segment_overrides: SegmentOverrides[FeatureMetadataT], ) -> dict[str, FlagResult[FeatureMetadataT]]: + if not (features := context.get("features")): + return {} + flags: dict[str, FlagResult[FeatureMetadataT]] = {} - for feature_context in (context.get("features") or {}).values(): + for feature_context in features.values(): feature_name = feature_context["name"] - if segment_override := segment_overrides.get( - feature_context["name"], - ): + if segment_override := segment_overrides.get(feature_name): flags[feature_name] = get_flag_result_from_context( context=context, feature_context=segment_override["feature_context"],