24
24
# ended up having to do for the context.
25
25
class EvalResult :
26
26
__slots__ = ['detail' , 'events' , 'big_segments_status' , 'big_segments_membership' ,
27
- 'original_flag_key' , 'prereq_stack' , 'segment_stack' ]
27
+ 'original_flag_key' , 'prereq_stack' , 'segment_stack' , 'depth' , 'prerequisites' ]
28
28
29
29
def __init__ (self ):
30
30
self .detail = None
@@ -34,6 +34,12 @@ def __init__(self):
34
34
self .original_flag_key = None # type: Optional[str]
35
35
self .prereq_stack = None # type: Optional[List[str]]
36
36
self .segment_stack = None # type: Optional[List[str]]
37
+ self .depth = 0
38
+ self .prerequisites = [] # type: List[str]
39
+
40
+ def record_prerequisite (self , key : str ):
41
+ if self .depth == 0 :
42
+ self .prerequisites .append (key )
37
43
38
44
def add_event (self , event : EventInputEvaluation ):
39
45
if self .events is None :
@@ -48,7 +54,7 @@ class EvaluationException(Exception):
48
54
def __init__ (self , message : str , error_kind : str = 'MALFORMED_FLAG' ):
49
55
self ._message = message
50
56
self ._error_kind = error_kind
51
-
57
+
52
58
@property
53
59
def message (self ) -> str :
54
60
return self ._message
@@ -125,7 +131,7 @@ def _check_prerequisites(self, flag: FeatureFlag, context: Context, state: EvalR
125
131
prereq_res = None
126
132
if flag .prerequisites .count == 0 :
127
133
return None
128
-
134
+
129
135
try :
130
136
# We use the state object to guard against circular references in prerequisites. To avoid
131
137
# the overhead of creating the state.prereq_stack list in the most common case where
@@ -136,7 +142,7 @@ def _check_prerequisites(self, flag: FeatureFlag, context: Context, state: EvalR
136
142
if state .prereq_stack is None :
137
143
state .prereq_stack = []
138
144
state .prereq_stack .append (flag_key )
139
-
145
+
140
146
for prereq in flag .prerequisites :
141
147
prereq_key = prereq .key
142
148
if (prereq_key == state .original_flag_key or
@@ -145,11 +151,15 @@ def _check_prerequisites(self, flag: FeatureFlag, context: Context, state: EvalR
145
151
' this is probably a temporary condition due to an incomplete update' ) % prereq_key )
146
152
147
153
prereq_flag = self .__get_flag (prereq_key )
154
+ state .record_prerequisite (prereq_key )
155
+
148
156
if prereq_flag is None :
149
157
log .warning ("Missing prereq flag: " + prereq_key )
150
158
failed_prereq = prereq
151
159
else :
160
+ state .depth += 1
152
161
prereq_res = self ._evaluate (prereq_flag , context , state , event_factory )
162
+ state .depth -= 1
153
163
# Note that if the prerequisite flag is off, we don't consider it a match no matter what its
154
164
# off variation was. But we still need to evaluate it in order to generate an event.
155
165
if (not prereq_flag .on ) or prereq_res .variation_index != prereq .variation :
@@ -208,7 +218,7 @@ def _clause_matches_context(self, clause: Clause, context: Context, state: EvalR
208
218
if segment is not None and self ._segment_matches_context (segment , context , state ):
209
219
return _maybe_negate (clause , True )
210
220
return _maybe_negate (clause , False )
211
-
221
+
212
222
attr = clause .attribute
213
223
if attr is None :
214
224
return False
@@ -220,7 +230,7 @@ def _clause_matches_context(self, clause: Clause, context: Context, state: EvalR
220
230
context_value = _get_context_value_by_attr_ref (actual_context , attr )
221
231
if context_value is None :
222
232
return False
223
-
233
+
224
234
# is the attr an array?
225
235
if isinstance (context_value , (list , tuple )):
226
236
for v in context_value :
@@ -287,7 +297,7 @@ def _big_segment_match_context(self, segment: Segment, context: Context, state:
287
297
# that as a "not configured" condition.
288
298
state .big_segments_status = BigSegmentsStatus .NOT_CONFIGURED
289
299
return False
290
-
300
+
291
301
# A big segment can only apply to one context kind, so if we don't have a key for that kind,
292
302
# we don't need to bother querying the data.
293
303
match_context = context .get_individual_context (segment .unbounded_context_kind or Context .DEFAULT_KIND )
@@ -357,7 +367,7 @@ def _variation_index_for_context(flag: FeatureFlag, vr: VariationOrRollout, cont
357
367
variations = rollout .variations
358
368
if len (variations ) == 0 :
359
369
return (None , False )
360
-
370
+
361
371
bucket_by = None if rollout .is_experiment else rollout .bucket_by
362
372
bucket = _bucket_context (
363
373
rollout .seed ,
0 commit comments