11import ast
2+ import operator
23import re
34from typing import Callable
45
@@ -40,6 +41,23 @@ def decorated(*args, **kwargs) -> bool:
4041 return decorated
4142
4243
44+ def build_custom_operator (operator ) -> Callable :
45+ def custom_comparator (left : Callable , right : Callable ) -> Callable :
46+ def decorated (* args , ** kwargs ) -> bool :
47+ return bool (operator (left (* args , ** kwargs ), right (* args , ** kwargs )))
48+
49+ return decorated
50+
51+ return custom_comparator
52+
53+
54+ def build_constant (constant ) -> Callable :
55+ def decorated (* args , ** kwargs ):
56+ return constant
57+
58+ return decorated
59+
60+
4361def custom_or (left : Callable , right : Callable ) -> Callable :
4462 def decorated (* args , ** kwargs ) -> bool :
4563 return left (* args , ** kwargs ) or right (* args , ** kwargs ) # type: ignore[no-any-return]
@@ -49,7 +67,7 @@ def decorated(*args, **kwargs) -> bool:
4967 return decorated
5068
5169
52- def build_expression (node , variable_hook , operator_mapping ):
70+ def build_expression (node , variable_hook , operator_mapping ): # noqa: C901
5371 if isinstance (node , ast .BoolOp ):
5472 # Handle `and` / `or` operations
5573 operator_fn = operator_mapping [type (node .op )]
@@ -58,13 +76,29 @@ def build_expression(node, variable_hook, operator_mapping):
5876 right_expr = build_expression (right , variable_hook , operator_mapping )
5977 left_expr = operator_fn (left_expr , right_expr )
6078 return left_expr
79+ elif isinstance (node , ast .Compare ):
80+ operator_fn = operator_mapping [type (node .ops [0 ])]
81+ left_expr = build_expression (node .left , variable_hook , operator_mapping )
82+ for right in node .comparators :
83+ right_expr = build_expression (right , variable_hook , operator_mapping )
84+ left_expr = operator_fn (left_expr , right_expr )
85+ return left_expr
6186 elif isinstance (node , ast .UnaryOp ) and isinstance (node .op , ast .Not ):
6287 # Handle `not` operation
6388 operand_expr = build_expression (node .operand , variable_hook , operator_mapping )
6489 return operator_mapping [type (node .op )](operand_expr )
6590 elif isinstance (node , ast .Name ):
6691 # Handle variables by calling the variable_hook
6792 return variable_hook (node .id )
93+ elif isinstance (node , ast .Constant ):
94+ # Handle constants by returning the value
95+ return build_constant (node .value )
96+ elif hasattr (ast , "NameConstant" ) and isinstance (
97+ node , ast .NameConstant
98+ ): # pragma: no cover | python3.7
99+ return build_constant (node .value )
100+ elif hasattr (ast , "Num" ) and isinstance (node , ast .Num ): # pragma: no cover | python3.7
101+ return build_constant (node .n )
68102 else :
69103 raise ValueError (f"Unsupported expression structure: { node .__class__ .__name__ } " )
70104
@@ -80,4 +114,14 @@ def parse_boolean_expr(expr, variable_hook, operator_mapping):
80114 return build_expression (tree .body , variable_hook , operator_mapping )
81115
82116
83- operator_mapping = {ast .Or : custom_or , ast .And : custom_and , ast .Not : custom_not }
117+ operator_mapping = {
118+ ast .Or : custom_or ,
119+ ast .And : custom_and ,
120+ ast .Not : custom_not ,
121+ ast .GtE : build_custom_operator (operator .ge ),
122+ ast .Gt : build_custom_operator (operator .gt ),
123+ ast .LtE : build_custom_operator (operator .le ),
124+ ast .Lt : build_custom_operator (operator .lt ),
125+ ast .Eq : build_custom_operator (operator .eq ),
126+ ast .NotEq : build_custom_operator (operator .ne ),
127+ }
0 commit comments