@@ -228,25 +228,57 @@ def context_matches_condition(
228228 condition : SegmentCondition ,
229229 segment_key : SupportsStr ,
230230) -> bool :
231- context_value = (
232- get_context_value (context , condition_property )
233- if (condition_property := condition .get ("property" ))
234- else None
235- )
236-
237- if condition ["operator" ] == constants .PERCENTAGE_SPLIT :
238- if context_value is not None :
239- object_ids = [segment_key , context_value ]
231+ context_value : ContextValue
232+ condition_property = condition ["property" ]
233+ condition_operator = condition ["operator" ]
234+
235+ if condition_operator == constants .PERCENTAGE_SPLIT and (not condition_property ):
236+ # Currently, the only supported condition with a blank property
237+ # is percentage split.
238+ # In this case, we use the identity key as context value.
239+ # This is mainly to support legacy segments created before
240+ # we introduced JSONPath support.
241+ context_value = _get_identity_key (context )
242+ else :
243+ context_value = get_context_value (context , condition_property )
244+
245+ if condition_operator == constants .IN :
246+ if isinstance (segment_value := condition ["value" ], list ):
247+ in_values = segment_value
240248 else :
241- object_ids = [segment_key , get_context_value (context , "$.identity.key" )]
249+ try :
250+ in_values = json .loads (segment_value )
251+ # Only accept JSON lists.
252+ # Ideally, we should use something like pydantic.TypeAdapter[list[str]],
253+ # but we aim to ditch the pydantic dependency in the future.
254+ if not isinstance (in_values , list ):
255+ raise ValueError
256+ except ValueError :
257+ in_values = segment_value .split ("," )
258+ in_values = [str (value ) for value in in_values ]
259+ # Guard against comparing boolean values to numeric strings.
260+ if isinstance (context_value , int ) and not (
261+ context_value is True or context_value is False
262+ ):
263+ context_value = str (context_value )
264+ return context_value in in_values
242265
243- float_value = float (condition ["value" ])
266+ if condition_operator == constants .PERCENTAGE_SPLIT :
267+ if context_value is None :
268+ return False
269+
270+ object_ids = [segment_key , context_value ]
271+
272+ try :
273+ float_value = float (condition ["value" ])
274+ except ValueError :
275+ return False
244276 return get_hashed_percentage_for_object_ids (object_ids ) <= float_value
245277
246- if condition [ "operator" ] == constants .IS_NOT_SET :
278+ if condition_operator == constants .IS_NOT_SET :
247279 return context_value is None
248280
249- if condition [ "operator" ] == constants .IS_SET :
281+ if condition_operator == constants .IS_SET :
250282 return context_value is not None
251283
252284 return (
@@ -368,6 +400,14 @@ def inner(
368400 return inner
369401
370402
403+ def _get_identity_key (
404+ context : EvaluationContext ,
405+ ) -> typing .Optional [str ]:
406+ if identity_context := context .get ("identity" ):
407+ return identity_context .get ("key" )
408+ return None
409+
410+
371411MATCHERS_BY_OPERATOR : typing .Dict [
372412 ConditionOperator , typing .Callable [[typing .Optional [str ], ContextValue ], bool ]
373413] = {
0 commit comments