Skip to content

Commit 33286f2

Browse files
authored
refactor(ir): impure values are never dereferencible (ibis-project#9023)
All impure values passed to the API methods are treated as separate impure invocations: ```py impure = ibis.random() t1 = t.mutate(y=impure) t2 = t1.mutate(z=impure.cast("string")) ``` now produce ``` r0 := UnboundTable: unbound_table_0 a int64 b string r1 := Project[r0] a: r0.a b: r0.b y: RandomScalar() Project[r1] a: r1.a b: r1.b y: r1.y z: Cast(RandomScalar(), to=string) ```
1 parent b3bbde1 commit 33286f2

5 files changed

Lines changed: 10 additions & 16 deletions

File tree

ibis/backends/pandas/executor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ def visit(cls, op: ops.Value, **operands):
110110
if func := cls.kernels.generic.get(typ):
111111
return cls.generic(func, **kwargs)
112112

113+
if len(operands) < 1:
114+
raise OperationNotDefinedError(
115+
f"No implementation found for operation {typ}"
116+
)
113117
_, *rest = operands.values()
114118
is_multi_arg = bool(rest)
115119
is_multi_column = any_of(rest, pd.Series)

ibis/expr/operations/generic.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,7 @@ class Constant(Scalar, Singleton):
185185

186186
@public
187187
class Impure(Value):
188-
_counter = itertools.count()
189-
uid: Optional[int] = None
190-
191-
def __init__(self, uid, **kwargs):
192-
if uid is None:
193-
uid = next(self._counter)
194-
super().__init__(uid=uid, **kwargs)
188+
pass
195189

196190

197191
@public

ibis/expr/operations/tests/test_generic.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,6 @@ def test_NULL():
146146

147147

148148
@pytest.mark.parametrize("op", [ops.RandomScalar, ops.RandomUUID])
149-
def test_unique_impure_values(op):
150-
assert op() != op()
151-
assert hash(op()) != hash(op())
152-
153-
node = op()
154-
other = node.copy()
155-
assert node == other
149+
def test_impure_values_are_equal(op):
150+
assert op() == op()
151+
assert hash(op()) == hash(op())

ibis/expr/tests/test_newrels.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1618,7 +1618,7 @@ def test_impure_operation_dereferencing(func):
16181618

16191619
expected = ops.Project(
16201620
parent=t1,
1621-
values={"x": t1.x, "y": t1.y, "z": t1.y.cast("string")},
1621+
values={"x": t1.x, "y": t1.y, "z": impure.cast("string")},
16221622
)
16231623
assert t2.op() == expected
16241624

ibis/expr/types/relations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def dereference_mapping(parents):
169169
while isinstance(v, ops.Field) and v not in mapping:
170170
mapping[v] = ops.Field(parent, k)
171171
v = v.rel.values.get(v.name)
172-
elif v not in mapping:
172+
elif v not in mapping and not v.find(ops.Impure):
173173
# do not dereference literal expressions
174174
mapping[v] = ops.Field(parent, k)
175175

0 commit comments

Comments
 (0)