1818
1919import json
2020import logging
21- from typing import Dict , Optional , Literal , Callable , Union
21+ from typing import Dict , Optional , Literal , Union
2222from enum import Enum
2323import re
2424import hashlib
@@ -228,15 +228,14 @@ def evaluate(self):
228228 evaluated_conditions = self .evaluate_conditions (self ._conditions , self ._context )
229229
230230 # Overlays config Value objects derived by evaluating the template.
231- # evaluated_conditions = None
232- if self ._parameters is not None :
231+ if self ._parameters :
233232 for key , parameter in self ._parameters .items ():
234233 conditional_values = parameter .get ('conditionalValues' , {})
235234 default_value = parameter .get ('defaultValue' , {})
236235 parameter_value_wrapper = None
237236 # Iterates in order over condition list. If there is a value associated
238237 # with a condition, this checks if the condition is true.
239- if evaluated_conditions is not None :
238+ if evaluated_conditions :
240239 for condition_name , condition_evaluation in evaluated_conditions .items ():
241240 if condition_name in conditional_values and condition_evaluation :
242241 parameter_value_wrapper = conditional_values [condition_name ]
@@ -404,6 +403,7 @@ def hash_seeded_randomization_id(self, seeded_randomization_id: str) -> int:
404403 hash_object .update (seeded_randomization_id .encode ('utf-8' ))
405404 hash64 = hash_object .hexdigest ()
406405 return abs (int (hash64 , 16 ))
406+
407407 def evaluate_custom_signal_condition (self , custom_signal_condition ,
408408 context ) -> bool :
409409 """Evaluates a custom signal condition.
@@ -417,124 +417,168 @@ def evaluate_custom_signal_condition(self, custom_signal_condition,
417417 """
418418 custom_signal_operator = custom_signal_condition .get ('custom_signal_operator' ) or {}
419419 custom_signal_key = custom_signal_condition .get ('custom_signal_key' ) or {}
420- tgt_custom_signal_values = custom_signal_condition .get ('target_custom_signal_values' ) or {}
420+ target_custom_signal_values = (
421+ custom_signal_condition .get ('target_custom_signal_values' ) or {})
421422
422- if not all ([custom_signal_operator , custom_signal_key , tgt_custom_signal_values ]):
423+ if not all ([custom_signal_operator , custom_signal_key , target_custom_signal_values ]):
423424 logger .warning ("Missing operator, key, or target values for custom signal condition." )
424425 return False
425426
426- if not tgt_custom_signal_values :
427+ if not target_custom_signal_values :
427428 return False
428- actual_custom_signal_value = getattr (context , custom_signal_key , None )
429- if actual_custom_signal_value is None :
429+ actual_custom_signal_value = context .get (custom_signal_key ) or {}
430+
431+ if not actual_custom_signal_value :
430432 logger .warning ("Custom signal value not found in context: %s" , custom_signal_key )
431433 return False
434+
432435 if custom_signal_operator == CustomSignalOperator .STRING_CONTAINS :
433- return compare_strings (lambda target , actual : target in actual )
436+ return self ._compare_strings (target_custom_signal_values ,
437+ actual_custom_signal_value ,
438+ lambda target , actual : target in actual )
434439 if custom_signal_operator == CustomSignalOperator .STRING_DOES_NOT_CONTAIN :
435- return not compare_strings (lambda target , actual : target in actual )
440+ return not self ._compare_strings (target_custom_signal_values ,
441+ actual_custom_signal_value ,
442+ lambda target , actual : target in actual )
436443 if custom_signal_operator == CustomSignalOperator .STRING_EXACTLY_MATCHES :
437- return compare_strings (lambda target , actual : target .strip () == actual .strip ())
444+ return self ._compare_strings (target_custom_signal_values ,
445+ actual_custom_signal_value ,
446+ lambda target , actual : target .strip () == actual .strip ())
438447 if custom_signal_operator == CustomSignalOperator .STRING_CONTAINS_REGEX :
439- return compare_strings (lambda target , actual : re .search (target , actual ) is not None )
448+ return self ._compare_strings (target_custom_signal_values ,
449+ actual_custom_signal_value ,
450+ re .search )
451+
452+ # For numeric operators only one target value is allowed.
440453 if custom_signal_operator == CustomSignalOperator .NUMERIC_LESS_THAN :
441- return compare_numbers (lambda r : r < 0 )
454+ return self ._compare_numbers (target_custom_signal_values [0 ],
455+ actual_custom_signal_value ,
456+ lambda r : r < 0 )
442457 if custom_signal_operator == CustomSignalOperator .NUMERIC_LESS_EQUAL :
443- return compare_numbers (lambda r : r <= 0 )
458+ return self ._compare_numbers (target_custom_signal_values [0 ],
459+ actual_custom_signal_value ,
460+ lambda r : r <= 0 )
444461 if custom_signal_operator == CustomSignalOperator .NUMERIC_EQUAL :
445- return compare_numbers (lambda r : r == 0 )
462+ return self ._compare_numbers (target_custom_signal_values [0 ],
463+ actual_custom_signal_value ,
464+ lambda r : r == 0 )
446465 if custom_signal_operator == CustomSignalOperator .NUMERIC_NOT_EQUAL :
447- return compare_numbers (lambda r : r != 0 )
466+ return self ._compare_numbers (target_custom_signal_values [0 ],
467+ actual_custom_signal_value ,
468+ lambda r : r != 0 )
448469 if custom_signal_operator == CustomSignalOperator .NUMERIC_GREATER_THAN :
449- return compare_numbers (lambda r : r > 0 )
470+ return self ._compare_numbers (target_custom_signal_values [0 ],
471+ actual_custom_signal_value ,
472+ lambda r : r > 0 )
450473 if custom_signal_operator == CustomSignalOperator .NUMERIC_GREATER_EQUAL :
451- return compare_numbers (lambda r : r >= 0 )
474+ return self ._compare_numbers (target_custom_signal_values [0 ],
475+ actual_custom_signal_value ,
476+ lambda r : r >= 0 )
477+
478+ # For semantic operators only one target value is allowed.
452479 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_LESS_THAN :
453- return compare_semantic_versions (lambda r : r < 0 )
480+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
481+ actual_custom_signal_value ,
482+ lambda r : r < 0 )
454483 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_LESS_EQUAL :
455- return compare_semantic_versions (lambda r : r <= 0 )
484+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
485+ actual_custom_signal_value ,
486+ lambda r : r <= 0 )
456487 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_EQUAL :
457- return compare_semantic_versions (lambda r : r == 0 )
488+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
489+ actual_custom_signal_value ,
490+ lambda r : r == 0 )
458491 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_NOT_EQUAL :
459- return compare_semantic_versions (lambda r : r != 0 )
492+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
493+ actual_custom_signal_value ,
494+ lambda r : r != 0 )
460495 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_GREATER_THAN :
461- return compare_semantic_versions (lambda r : r > 0 )
496+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
497+ actual_custom_signal_value ,
498+ lambda r : r > 0 )
462499 if custom_signal_operator == CustomSignalOperator .SEMANTIC_VERSION_GREATER_EQUAL :
463- return compare_semantic_versions (lambda r : r >= 0 )
464-
465- def compare_strings (predicate_fn : Callable [[str , str ], bool ]) -> bool :
466- """Compares the actual string value of a signal against a list of target values.
467-
468- Args:
469- predicate_fn: A function that takes two string arguments (target and actual)
470- and returns a boolean indicating whether
471- the target matches the actual value.
472-
473- Returns:
474- bool: True if the predicate function returns True for any target value in the list,
475- False otherwise.
476- """
477- for target in tgt_custom_signal_values :
478- if predicate_fn (target , str (actual_custom_signal_value )):
479- return True
480- return False
500+ return self ._compare_semantic_versions (target_custom_signal_values [0 ],
501+ actual_custom_signal_value ,
502+ lambda r : r >= 0 )
503+ logger .warning ("Unknown custom signal operator: %s" , custom_signal_operator )
504+ return False
481505
482- def compare_numbers (predicate_fn : Callable [[int ], bool ]) -> bool :
483- try :
484- target = float (tgt_custom_signal_values [0 ])
485- actual = float (actual_custom_signal_value )
486- result = - 1 if actual < target else 1 if actual > target else 0
487- return predicate_fn (result )
488- except ValueError :
489- logger .warning ("Invalid numeric value for comparison." )
490- return False
506+ def _compare_strings (self , target_values , actual_value , predicate_fn ) -> bool :
507+ """Compares the actual string value of a signal against a list of target values.
491508
492- def compare_semantic_versions (predicate_fn : Callable [[int ], bool ]) -> bool :
493- """Compares the actual semantic version value of a signal against a target value.
494- Calls the predicate function with -1, 0, 1 if actual is less than, equal to,
495- or greater than target.
496-
497- Args:
498- predicate_fn: A function that takes an integer (-1, 0, or 1) and returns a boolean.
499-
500- Returns:
501- bool: True if the predicate function returns True for the result of the comparison,
502- False otherwise.
503- """
504- return compare_versions (str (actual_custom_signal_value ),
505- str (tgt_custom_signal_values [0 ]), predicate_fn )
506- def compare_versions (version1 : str , version2 : str ,
507- predicate_fn : Callable [[int ], bool ]) -> bool :
508- """Compares two semantic version strings.
509-
510- Args:
511- version1: The first semantic version string.
512- version2: The second semantic version string.
513- predicate_fn: A function that takes an integer and returns a boolean.
514-
515- Returns:
516- bool: The result of the predicate function.
517- """
518- try :
519- v1_parts = [int (part ) for part in version1 .split ('.' )]
520- v2_parts = [int (part ) for part in version2 .split ('.' )]
521- max_length = max (len (v1_parts ), len (v2_parts ))
522- v1_parts .extend ([0 ] * (max_length - len (v1_parts )))
523- v2_parts .extend ([0 ] * (max_length - len (v2_parts )))
524-
525- for part1 , part2 in zip (v1_parts , v2_parts ):
526- if part1 < part2 :
527- return predicate_fn (- 1 )
528- if part1 > part2 :
529- return predicate_fn (1 )
530- return predicate_fn (0 )
531- except ValueError :
532- logger .warning ("Invalid semantic version format for comparison." )
533- return False
509+ Args:
510+ target_values: A list of target string values.
511+ actual_value: The actual value to compare, which can be a string or number.
512+ predicate_fn: A function that takes two string arguments (target and actual)
513+ and returns a boolean indicating whether
514+ the target matches the actual value.
534515
535- logger .warning ("Unknown custom signal operator: %s" , custom_signal_operator )
516+ Returns:
517+ bool: True if the predicate function returns True for any target value in the list,
518+ False otherwise.
519+ """
520+
521+ for target in target_values :
522+ if predicate_fn (target , str (actual_value )):
523+ return True
536524 return False
537525
526+ def _compare_numbers (self , target_value , actual_value , predicate_fn ) -> bool :
527+ try :
528+ target = float (target_value )
529+ actual = float (actual_value )
530+ result = - 1 if actual < target else 1 if actual > target else 0
531+ return predicate_fn (result )
532+ except ValueError :
533+ logger .warning ("Invalid numeric value for comparison." )
534+ return False
535+
536+ def _compare_semantic_versions (self , target_value , actual_value , predicate_fn ) -> bool :
537+ """Compares the actual semantic version value of a signal against a target value.
538+ Calls the predicate function with -1, 0, 1 if actual is less than, equal to,
539+ or greater than target.
540+
541+ Args:
542+ target_values: A list of target string values.
543+ actual_value: The actual value to compare, which can be a string or number.
544+ predicate_fn: A function that takes an integer (-1, 0, or 1) and returns a boolean.
545+
546+ Returns:
547+ bool: True if the predicate function returns True for the result of the comparison,
548+ False otherwise.
549+ """
550+ return self ._compare_versions (str (actual_value ),
551+ str (target_value ), predicate_fn )
552+
553+ def _compare_versions (self , version1 , version2 , predicate_fn ) -> bool :
554+ """Compares two semantic version strings.
555+
556+ Args:
557+ version1: The first semantic version string.
558+ version2: The second semantic version string.
559+ predicate_fn: A function that takes an integer and returns a boolean.
560+
561+ Returns:
562+ bool: The result of the predicate function.
563+ """
564+ try :
565+ v1_parts = [int (part ) for part in version1 .split ('.' )]
566+ v2_parts = [int (part ) for part in version2 .split ('.' )]
567+ max_length = max (len (v1_parts ), len (v2_parts ))
568+ v1_parts .extend ([0 ] * (max_length - len (v1_parts )))
569+ v2_parts .extend ([0 ] * (max_length - len (v2_parts )))
570+
571+ for part1 , part2 in zip (v1_parts , v2_parts ):
572+ if part1 < part2 :
573+ return predicate_fn (- 1 )
574+ if part1 > part2 :
575+ return predicate_fn (1 )
576+ return predicate_fn (0 )
577+ except ValueError :
578+ logger .warning ("Invalid semantic version format for comparison." )
579+ return False
580+
581+
538582async def get_server_template (app : App = None , default_config : Optional [Dict [str , str ]] = None ):
539583 """Initializes a new ServerTemplate instance and fetches the server template.
540584
0 commit comments