MCPcopy
hub / github.com/pydata/xarray / LogicalOperatorTransformer

Class LogicalOperatorTransformer

xarray/core/eval.py:52–119  ·  view source on GitHub ↗

Transform operators for consistency with query(). query() uses pd.eval() which transforms these operators automatically. We replicate that behavior here so syntax that works in query() also works in eval(). Transformations: 1. 'and'/'or'/'not' -> '&'/'|'/'~' 2. 'a < b < c'

Source from the content-addressed store, hash-verified

50
51
52class LogicalOperatorTransformer(ast.NodeTransformer):
53 """Transform operators for consistency with query().
54
55 query() uses pd.eval() which transforms these operators automatically.
56 We replicate that behavior here so syntax that works in query() also
57 works in eval().
58
59 Transformations:
60 1. 'and'/'or'/'not' -> '&'/'|'/'~'
61 2. 'a < b < c' -> '(a < b) & (b < c)'
62
63 These constructs fail on arrays in standard Python because they call
64 __bool__(), which is ambiguous for multi-element arrays.
65 """
66
67 def visit_BoolOp(self, node: ast.BoolOp) -> ast.AST:
68 # Transform: a and b -> a & b, a or b -> a | b
69 self.generic_visit(node)
70 op: ast.BitAnd | ast.BitOr
71 if isinstance(node.op, ast.And):
72 op = ast.BitAnd()
73 elif isinstance(node.op, ast.Or):
74 op = ast.BitOr()
75 else:
76 return node
77
78 # BoolOp can have multiple values: a and b and c
79 # Transform to chained BinOp: (a & b) & c
80 result = node.values[0]
81 for value in node.values[1:]:
82 result = ast.BinOp(left=result, op=op, right=value)
83 return ast.fix_missing_locations(result)
84
85 def visit_UnaryOp(self, node: ast.UnaryOp) -> ast.AST:
86 # Transform: not a -> ~a
87 self.generic_visit(node)
88 if isinstance(node.op, ast.Not):
89 return ast.fix_missing_locations(
90 ast.UnaryOp(op=ast.Invert(), operand=node.operand)
91 )
92 return node
93
94 def visit_Compare(self, node: ast.Compare) -> ast.AST:
95 # Transform chained comparisons: 1 < x < 5 -> (1 < x) & (x < 5)
96 # Python's chained comparisons use short-circuit evaluation at runtime,
97 # which calls __bool__ on intermediate results. This fails for arrays.
98 # We transform to bitwise AND which works element-wise.
99 self.generic_visit(node)
100
101 if len(node.ops) == 1:
102 # Simple comparison, no transformation needed
103 return node
104
105 # Build individual comparisons and chain with BitAnd
106 # For: a < b < c < d
107 # We need: (a < b) & (b < c) & (c < d)
108 comparisons = []
109 left = node.left

Callers 1

_eval_expressionMethod · 0.90

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…