18
18
19
19
import json
20
20
import logging
21
- from typing import Dict , Optional , Literal , Callable , Union
21
+ from typing import Dict , Optional , Literal , Union
22
22
from enum import Enum
23
23
import re
24
24
import hashlib
@@ -228,15 +228,14 @@ def evaluate(self):
228
228
evaluated_conditions = self .evaluate_conditions (self ._conditions , self ._context )
229
229
230
230
# Overlays config Value objects derived by evaluating the template.
231
- # evaluated_conditions = None
232
- if self ._parameters is not None :
231
+ if self ._parameters :
233
232
for key , parameter in self ._parameters .items ():
234
233
conditional_values = parameter .get ('conditionalValues' , {})
235
234
default_value = parameter .get ('defaultValue' , {})
236
235
parameter_value_wrapper = None
237
236
# Iterates in order over condition list. If there is a value associated
238
237
# with a condition, this checks if the condition is true.
239
- if evaluated_conditions is not None :
238
+ if evaluated_conditions :
240
239
for condition_name , condition_evaluation in evaluated_conditions .items ():
241
240
if condition_name in conditional_values and condition_evaluation :
242
241
parameter_value_wrapper = conditional_values [condition_name ]
@@ -404,6 +403,7 @@ def hash_seeded_randomization_id(self, seeded_randomization_id: str) -> int:
404
403
hash_object .update (seeded_randomization_id .encode ('utf-8' ))
405
404
hash64 = hash_object .hexdigest ()
406
405
return abs (int (hash64 , 16 ))
406
+
407
407
def evaluate_custom_signal_condition (self , custom_signal_condition ,
408
408
context ) -> bool :
409
409
"""Evaluates a custom signal condition.
@@ -417,124 +417,168 @@ def evaluate_custom_signal_condition(self, custom_signal_condition,
417
417
"""
418
418
custom_signal_operator = custom_signal_condition .get ('custom_signal_operator' ) or {}
419
419
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 {})
421
422
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 ]):
423
424
logger .warning ("Missing operator, key, or target values for custom signal condition." )
424
425
return False
425
426
426
- if not tgt_custom_signal_values :
427
+ if not target_custom_signal_values :
427
428
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 :
430
432
logger .warning ("Custom signal value not found in context: %s" , custom_signal_key )
431
433
return False
434
+
432
435
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 )
434
439
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 )
436
443
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 ())
438
447
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.
440
453
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 )
442
457
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 )
444
461
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 )
446
465
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 )
448
469
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 )
450
473
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.
452
479
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 )
454
483
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 )
456
487
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 )
458
491
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 )
460
495
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 )
462
499
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
481
505
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.
491
508
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.
534
515
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
536
524
return False
537
525
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
+
538
582
async def get_server_template (app : App = None , default_config : Optional [Dict [str , str ]] = None ):
539
583
"""Initializes a new ServerTemplate instance and fetches the server template.
540
584
0 commit comments