3636from flag_engine .utils .types import SupportsStr , get_casting_function
3737
3838
39- class FeatureContextWithSegmentName (TypedDict , typing .Generic [FeatureMetadataT ]):
39+ class SegmentOverride (TypedDict , typing .Generic [FeatureMetadataT ]):
4040 feature_context : FeatureContext [FeatureMetadataT ]
4141 segment_name : str
4242
4343
44+ SegmentOverrides = dict [str , SegmentOverride [FeatureMetadataT ]]
45+
4446# Type alias for EvaluationContext with any metadata types
4547# used in internal evaluation logic
4648_EvaluationContextAnyMeta = EvaluationContext [typing .Any , typing .Any ]
@@ -55,15 +57,45 @@ def get_evaluation_result(
5557 :param context: the evaluation context
5658 :return: EvaluationResult containing the context, flags, and segments
5759 """
58- segments : list [SegmentResult [SegmentMetadataT ]] = []
59- flags : dict [str , FlagResult [FeatureMetadataT ]] = {}
60+ enrich_context (context )
61+ segments , segment_overrides = evaluate_segments (context )
62+ flags = evaluate_features (context , segment_overrides )
63+
64+ return {
65+ "flags" : flags ,
66+ "segments" : segments ,
67+ }
68+
69+
70+ def enrich_context (
71+ context : _EvaluationContextAnyMeta ,
72+ ) -> None :
73+ """
74+ Enrich the evaluation context in-place by ensuring that:
75+ - `$.identity.key` is set
76+
77+ :param context: the evaluation context to enrich
78+ """
79+ if identity_context := context .get ("identity" ):
80+ if not identity_context .get ("key" ):
81+ identity_context ["key" ] = (
82+ f"{ context ['environment' ]['key' ]} _{ identity_context ['identifier' ]} "
83+ )
6084
61- segment_feature_contexts : dict [
62- SupportsStr ,
63- FeatureContextWithSegmentName [FeatureMetadataT ],
64- ] = {}
6585
66- for segment_context in (context .get ("segments" ) or {}).values ():
86+ def evaluate_segments (
87+ context : EvaluationContext [SegmentMetadataT , FeatureMetadataT ],
88+ ) -> typing .Tuple [
89+ list [SegmentResult [SegmentMetadataT ]],
90+ SegmentOverrides [FeatureMetadataT ],
91+ ]:
92+ if not (segment_contexts := context .get ("segments" )):
93+ return [], {}
94+
95+ segment_results : list [SegmentResult [SegmentMetadataT ]] = []
96+ segment_overrides : SegmentOverrides [FeatureMetadataT ] = {}
97+
98+ for segment_context in segment_contexts .values ():
6799 if not is_context_in_segment (context , segment_context ):
68100 continue
69101
@@ -72,69 +104,75 @@ def get_evaluation_result(
72104 }
73105 if segment_metadata := segment_context .get ("metadata" ):
74106 segment_result ["metadata" ] = segment_metadata
75- segments .append (segment_result )
107+ segment_results .append (segment_result )
76108
77109 if overrides := segment_context .get ("overrides" ):
78110 for override_feature_context in overrides :
79111 feature_name = override_feature_context ["name" ]
80112 if (
81- feature_name not in segment_feature_contexts
113+ feature_name not in segment_overrides
82114 or override_feature_context .get (
83115 "priority" ,
84116 constants .DEFAULT_PRIORITY ,
85117 )
86- < (segment_feature_contexts [feature_name ]["feature_context" ]).get (
118+ < (segment_overrides [feature_name ]["feature_context" ]).get (
87119 "priority" ,
88120 constants .DEFAULT_PRIORITY ,
89121 )
90122 ):
91- segment_feature_contexts [feature_name ] = (
92- FeatureContextWithSegmentName (
93- feature_context = override_feature_context ,
94- segment_name = segment_context ["name" ],
95- )
123+ segment_overrides [feature_name ] = SegmentOverride (
124+ feature_context = override_feature_context ,
125+ segment_name = segment_context ["name" ],
96126 )
97127
98- identity_key = _get_identity_key (context )
128+ return segment_results , segment_overrides
129+
130+
131+ def evaluate_features (
132+ context : EvaluationContext [typing .Any , FeatureMetadataT ],
133+ segment_overrides : SegmentOverrides [FeatureMetadataT ],
134+ ) -> dict [str , FlagResult [FeatureMetadataT ]]:
135+ flags : dict [str , FlagResult [FeatureMetadataT ]] = {}
136+
99137 for feature_context in (context .get ("features" ) or {}).values ():
100138 feature_name = feature_context ["name" ]
101- if feature_context_with_segment_name := segment_feature_contexts .get (
139+ if segment_override := segment_overrides .get (
102140 feature_context ["name" ],
103141 ):
104- feature_context = feature_context_with_segment_name ["feature_context" ]
142+ feature_context = segment_override ["feature_context" ]
105143 flag_result : FlagResult [FeatureMetadataT ]
106144 flags [feature_name ] = flag_result = {
107145 "enabled" : feature_context ["enabled" ],
108146 "name" : feature_context ["name" ],
109- "reason" : f"TARGETING_MATCH; segment={ feature_context_with_segment_name ['segment_name' ]} " ,
147+ "reason" : f"TARGETING_MATCH; segment={ segment_override ['segment_name' ]} " ,
110148 "value" : feature_context .get ("value" ),
111149 }
112150 if feature_metadata := feature_context .get ("metadata" ):
113151 flag_result ["metadata" ] = feature_metadata
114152 continue
115- flags [feature_name ] = get_flag_result_from_feature_context (
116- feature_context = feature_context ,
117- key = identity_key ,
153+ flags [feature_name ] = get_flag_result_from_context (
154+ context = context ,
155+ feature_name = feature_name ,
118156 )
119157
120- return {
121- "flags" : flags ,
122- "segments" : segments ,
123- }
158+ return flags
124159
125160
126- def get_flag_result_from_feature_context (
127- feature_context : FeatureContext [ FeatureMetadataT ],
128- key : typing . Optional [ SupportsStr ] ,
161+ def get_flag_result_from_context (
162+ context : EvaluationContext [ typing . Any , FeatureMetadataT ],
163+ feature_name : str ,
129164) -> FlagResult [FeatureMetadataT ]:
130165 """
131- Get a feature value from the feature context
132- for a given key .
166+ Get a feature value from the evaluation context
167+ for a given feature name .
133168
134- :param feature_context : the feature context
135- :param key : the key to get the value for
136- :return: the value for the key in the feature context
169+ :param context : the evaluation context
170+ :param feature_name : the feature name to get the value for
171+ :return: the value for the feature name in the evaluation context
137172 """
173+ feature_context = context ["features" ][feature_name ]
174+ key = _get_identity_key (context )
175+
138176 flag_result : typing .Optional [FlagResult [FeatureMetadataT ]] = None
139177
140178 if key is not None and (variants := feature_context .get ("variants" )):
@@ -253,8 +291,8 @@ def context_matches_condition(
253291 if condition ["operator" ] == constants .PERCENTAGE_SPLIT :
254292 if context_value is not None :
255293 object_ids = [segment_key , context_value ]
256- elif identity_context := context . get ( "identity" ):
257- object_ids = [segment_key , identity_context [ "key" ] ]
294+ elif identity_key := _get_identity_key ( context ):
295+ object_ids = [segment_key , identity_key ]
258296 else :
259297 return False
260298
@@ -376,10 +414,7 @@ def _get_identity_key(
376414 context : _EvaluationContextAnyMeta ,
377415) -> typing .Optional [SupportsStr ]:
378416 if identity_context := context .get ("identity" ):
379- return (
380- identity_context .get ("key" )
381- or f"{ context ['environment' ]['key' ]} _{ identity_context ['identifier' ]} "
382- )
417+ return identity_context .get ("key" )
383418 return None
384419
385420
0 commit comments