1+ from __future__ import annotations
2+
13import json
24import operator
35import re
810
911import jsonpath_rfc9535
1012import semver
13+ from typing_extensions import TypedDict
1114
1215from flag_engine .context .mappers import map_any_value_to_context_value
1316from flag_engine .context .types import (
2326from flag_engine .segments .types import (
2427 ConditionOperator ,
2528 ContextValue ,
29+ FeatureMetadataT ,
2630 SegmentMetadataT ,
2731 is_context_value ,
2832)
3236from flag_engine .utils .types import SupportsStr , get_casting_function
3337
3438
35- class FeatureContextWithSegmentName (typing .TypedDict ):
36- feature_context : FeatureContext
39+ class FeatureContextWithSegmentName (TypedDict , typing .Generic [ FeatureMetadataT ] ):
40+ feature_context : FeatureContext [ FeatureMetadataT ]
3741 segment_name : str
3842
3943
4044def get_evaluation_result (
41- context : EvaluationContext [SegmentMetadataT ],
42- ) -> EvaluationResult [SegmentMetadataT ]:
45+ context : EvaluationContext [SegmentMetadataT , FeatureMetadataT ],
46+ ) -> EvaluationResult [SegmentMetadataT , FeatureMetadataT ]:
4347 """
4448 Get the evaluation result for a given context.
4549
4650 :param context: the evaluation context
4751 :return: EvaluationResult containing the context, flags, and segments
4852 """
4953 segments : list [SegmentResult [SegmentMetadataT ]] = []
50- flags : dict [str , FlagResult ] = {}
54+ flags : dict [str , FlagResult [ FeatureMetadataT ] ] = {}
5155
52- segment_feature_contexts : dict [SupportsStr , FeatureContextWithSegmentName ] = {}
56+ segment_feature_contexts : dict [
57+ SupportsStr ,
58+ FeatureContextWithSegmentName [FeatureMetadataT ],
59+ ] = {}
5360
5461 for segment_context in (context .get ("segments" ) or {}).values ():
5562 if not is_context_in_segment (context , segment_context ):
@@ -59,8 +66,8 @@ def get_evaluation_result(
5966 "key" : segment_context ["key" ],
6067 "name" : segment_context ["name" ],
6168 }
62- if metadata := segment_context .get ("metadata" ):
63- segment_result ["metadata" ] = metadata
69+ if segment_metadata := segment_context .get ("metadata" ):
70+ segment_result ["metadata" ] = segment_metadata
6471 segments .append (segment_result )
6572
6673 if overrides := segment_context .get ("overrides" ):
@@ -95,13 +102,16 @@ def get_evaluation_result(
95102 feature_context ["feature_key" ],
96103 ):
97104 feature_context = feature_context_with_segment_name ["feature_context" ]
98- flags [feature_name ] = {
105+ flag_result : FlagResult [FeatureMetadataT ]
106+ flags [feature_name ] = flag_result = {
99107 "enabled" : feature_context ["enabled" ],
100108 "feature_key" : feature_context ["feature_key" ],
101109 "name" : feature_context ["name" ],
102110 "reason" : f"TARGETING_MATCH; segment={ feature_context_with_segment_name ['segment_name' ]} " ,
103111 "value" : feature_context .get ("value" ),
104112 }
113+ if feature_metadata := feature_context .get ("metadata" ):
114+ flag_result ["metadata" ] = feature_metadata
105115 continue
106116 flags [feature_name ] = get_flag_result_from_feature_context (
107117 feature_context = feature_context ,
@@ -115,9 +125,9 @@ def get_evaluation_result(
115125
116126
117127def get_flag_result_from_feature_context (
118- feature_context : FeatureContext ,
128+ feature_context : FeatureContext [ FeatureMetadataT ] ,
119129 key : typing .Optional [SupportsStr ],
120- ) -> FlagResult :
130+ ) -> FlagResult [ FeatureMetadataT ] :
121131 """
122132 Get a feature value from the feature context
123133 for a given key.
@@ -126,6 +136,8 @@ def get_flag_result_from_feature_context(
126136 :param key: the key to get the value for
127137 :return: the value for the key in the feature context
128138 """
139+ flag_result : typing .Optional [FlagResult [FeatureMetadataT ]] = None
140+
129141 if key is not None and (variants := feature_context .get ("variants" )):
130142 percentage_value = get_hashed_percentage_for_object_ids (
131143 [feature_context ["key" ], key ]
@@ -139,28 +151,35 @@ def get_flag_result_from_feature_context(
139151 ):
140152 limit = (weight := variant ["weight" ]) + start_percentage
141153 if start_percentage <= percentage_value < limit :
142- return {
154+ flag_result = {
143155 "enabled" : feature_context ["enabled" ],
144156 "feature_key" : feature_context ["feature_key" ],
145157 "name" : feature_context ["name" ],
146158 "reason" : f"SPLIT; weight={ weight } " ,
147159 "value" : variant ["value" ],
148160 }
161+ break
149162
150163 start_percentage = limit
151164
152- return {
153- "enabled" : feature_context ["enabled" ],
154- "feature_key" : feature_context ["feature_key" ],
155- "name" : feature_context ["name" ],
156- "reason" : "DEFAULT" ,
157- "value" : feature_context ["value" ],
158- }
165+ if flag_result is None :
166+ flag_result = {
167+ "enabled" : feature_context ["enabled" ],
168+ "feature_key" : feature_context ["feature_key" ],
169+ "name" : feature_context ["name" ],
170+ "reason" : "DEFAULT" ,
171+ "value" : feature_context ["value" ],
172+ }
173+
174+ if metadata := feature_context .get ("metadata" ):
175+ flag_result ["metadata" ] = metadata
176+
177+ return flag_result
159178
160179
161180def is_context_in_segment (
162- context : EvaluationContext [SegmentMetadataT ],
163- segment_context : SegmentContext [SegmentMetadataT ],
181+ context : EvaluationContext [typing . Any , typing . Any ],
182+ segment_context : SegmentContext [typing . Any , typing . Any ],
164183) -> bool :
165184 return bool (rules := segment_context ["rules" ]) and all (
166185 context_matches_rule (
@@ -171,7 +190,7 @@ def is_context_in_segment(
171190
172191
173192def context_matches_rule (
174- context : EvaluationContext [SegmentMetadataT ],
193+ context : EvaluationContext [typing . Any , typing . Any ],
175194 rule : SegmentRule ,
176195 segment_key : SupportsStr ,
177196) -> bool :
@@ -201,7 +220,7 @@ def context_matches_rule(
201220
202221
203222def context_matches_condition (
204- context : EvaluationContext [SegmentMetadataT ],
223+ context : EvaluationContext [typing . Any , typing . Any ],
205224 condition : SegmentCondition ,
206225 segment_key : SupportsStr ,
207226) -> bool :
@@ -262,7 +281,7 @@ def context_matches_condition(
262281
263282
264283def get_context_value (
265- context : EvaluationContext [SegmentMetadataT ],
284+ context : EvaluationContext [typing . Any , typing . Any ],
266285 property : str ,
267286) -> ContextValue :
268287 value = None
0 commit comments