Skip to content

Commit

Permalink
refactor(ir): impure values are never dereferencible (#9023)
Browse files Browse the repository at this point in the history
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)
```
  • Loading branch information
kszucs authored Apr 22, 2024
1 parent b3bbde1 commit 33286f2
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 16 deletions.
4 changes: 4 additions & 0 deletions ibis/backends/pandas/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ def visit(cls, op: ops.Value, **operands):
if func := cls.kernels.generic.get(typ):
return cls.generic(func, **kwargs)

if len(operands) < 1:
raise OperationNotDefinedError(
f"No implementation found for operation {typ}"
)
_, *rest = operands.values()
is_multi_arg = bool(rest)
is_multi_column = any_of(rest, pd.Series)
Expand Down
8 changes: 1 addition & 7 deletions ibis/expr/operations/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,7 @@ class Constant(Scalar, Singleton):

@public
class Impure(Value):
_counter = itertools.count()
uid: Optional[int] = None

def __init__(self, uid, **kwargs):
if uid is None:
uid = next(self._counter)
super().__init__(uid=uid, **kwargs)
pass


@public
Expand Down
10 changes: 3 additions & 7 deletions ibis/expr/operations/tests/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,6 @@ def test_NULL():


@pytest.mark.parametrize("op", [ops.RandomScalar, ops.RandomUUID])
def test_unique_impure_values(op):
assert op() != op()
assert hash(op()) != hash(op())

node = op()
other = node.copy()
assert node == other
def test_impure_values_are_equal(op):
assert op() == op()
assert hash(op()) == hash(op())
2 changes: 1 addition & 1 deletion ibis/expr/tests/test_newrels.py
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,7 @@ def test_impure_operation_dereferencing(func):

expected = ops.Project(
parent=t1,
values={"x": t1.x, "y": t1.y, "z": t1.y.cast("string")},
values={"x": t1.x, "y": t1.y, "z": impure.cast("string")},
)
assert t2.op() == expected

Expand Down
2 changes: 1 addition & 1 deletion ibis/expr/types/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def dereference_mapping(parents):
while isinstance(v, ops.Field) and v not in mapping:
mapping[v] = ops.Field(parent, k)
v = v.rel.values.get(v.name)
elif v not in mapping:
elif v not in mapping and not v.find(ops.Impure):
# do not dereference literal expressions
mapping[v] = ops.Field(parent, k)

Expand Down

0 comments on commit 33286f2

Please sign in to comment.