Skip to content

Commit 484776f

Browse files
NickCrewscpcloud
authored andcommitted
refactor: move _binop() util function to be near ir.Value, not ir.Expr
This function is only uses with Values, not general Expr, so it should be co-located there. This adds a runtime assert to verify we are using this correctly internally. It also improves the docstring to not be misleading about ops.Binary, and to give some more examples of the edge cases.
1 parent d6cb832 commit 484776f

File tree

6 files changed

+57
-44
lines changed

6 files changed

+57
-44
lines changed

ibis/expr/types/core.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import ibis
1111
import ibis.expr.operations as ops
12-
from ibis.common.annotations import ValidationError
1312
from ibis.common.exceptions import IbisError, TranslationError
1413
from ibis.common.grounds import Immutable
1514
from ibis.common.patterns import Coercible, CoercionError
@@ -944,38 +943,3 @@ def as_scalar(self) -> ir.Scalar:
944943
raise NotImplementedError(
945944
f"{type(self)} expressions cannot be converted into scalars"
946945
)
947-
948-
949-
def _binop(op_class: type[ops.Binary], left: ir.Value, right: ir.Value) -> ir.Value:
950-
"""Try to construct a binary operation.
951-
952-
Parameters
953-
----------
954-
op_class
955-
The `ops.Binary` subclass for the operation
956-
left
957-
Left operand
958-
right
959-
Right operand
960-
961-
Returns
962-
-------
963-
ir.Value
964-
A value expression
965-
966-
Examples
967-
--------
968-
>>> import ibis
969-
>>> import ibis.expr.operations as ops
970-
>>> expr = _binop(ops.TimeAdd, ibis.time("01:00"), ibis.interval(hours=1))
971-
>>> expr
972-
TimeAdd(datetime.time(1, 0), 1h): datetime.time(1, 0) + 1 h
973-
>>> _binop(ops.TimeAdd, 1, ibis.interval(hours=1))
974-
TimeAdd(datetime.time(0, 0, 1), 1h): datetime.time(0, 0, 1) + 1 h
975-
"""
976-
try:
977-
node = op_class(left, right)
978-
except (ValidationError, NotImplementedError):
979-
return NotImplemented
980-
else:
981-
return node.to_expr()

ibis/expr/types/generic.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
import ibis.expr.builders as bl
1111
import ibis.expr.datatypes as dt
1212
import ibis.expr.operations as ops
13+
from ibis.common.annotations import ValidationError
1314
from ibis.common.deferred import Deferred, _, deferrable
1415
from ibis.common.grounds import Singleton
1516
from ibis.expr.rewrites import rewrite_window_input
16-
from ibis.expr.types.core import Expr, _binop
17+
from ibis.expr.types.core import Expr
1718
from ibis.expr.types.rich import FixedTextJupyterMixin, to_rich
1819
from ibis.util import deprecated, experimental, promote_list
1920

@@ -3119,6 +3120,57 @@ def _is_null_literal(value: Any) -> bool:
31193120
)
31203121

31213122

3123+
def _binop(op_class: type[ops.Value], left: Value | Any, right: Value | Any) -> Value:
3124+
"""Try to construct a binary operation between two Values.
3125+
3126+
Parameters
3127+
----------
3128+
op_class
3129+
An ops.Value that accepts two Value positional operands.
3130+
Not necessarily a ops.Binary subclass,
3131+
eg this works with ops.Repeat for string repetition.
3132+
left
3133+
Left operand. Can be a Value, or something coercible to a Value.
3134+
right
3135+
Right operand. Can be a Value, or something coercible to a Value.
3136+
3137+
Returns
3138+
-------
3139+
ir.Value
3140+
A value expression
3141+
3142+
Examples
3143+
--------
3144+
>>> import ibis
3145+
>>> import ibis.expr.operations as ops
3146+
>>> _binop(ops.TimeAdd, ibis.time("01:00"), ibis.interval(hours=1))
3147+
TimeAdd(datetime.time(1, 0), 1h): datetime.time(1, 0) + 1 h
3148+
>>> _binop(ops.TimeAdd, 1, ibis.interval(hours=1))
3149+
TimeAdd(datetime.time(0, 0, 1), 1h): datetime.time(0, 0, 1) + 1 h
3150+
>>> _binop(ops.Equals, 5, 2)
3151+
Equals(5, 2): 5 == 2
3152+
3153+
This raises if the operation is known to be invalid between the two operands:
3154+
3155+
>>> _binop(ops.Equals, ibis.literal("foo"), 2) # quartodoc: +EXPECTED_FAILURE
3156+
Traceback (most recent call last):
3157+
...
3158+
IbisTypeError: Arguments Literal(foo):string and Literal(2):int8 are not comparable
3159+
3160+
But returns NotImplemented if we aren't sure:
3161+
3162+
>>> _binop(ops.Equals, 5, ibis.table({"a": "int"}))
3163+
NotImplemented
3164+
"""
3165+
assert issubclass(op_class, ops.Value)
3166+
try:
3167+
node = op_class(left, right)
3168+
except (ValidationError, NotImplementedError):
3169+
return NotImplemented
3170+
else:
3171+
return node.to_expr()
3172+
3173+
31223174
public(
31233175
ValueExpr=Value,
31243176
ScalarExpr=Scalar,

ibis/expr/types/logical.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import ibis
88
import ibis.expr.operations as ops
99
from ibis import util
10-
from ibis.expr.types.core import _binop
10+
from ibis.expr.types.generic import _binop
1111
from ibis.expr.types.numeric import NumericColumn, NumericScalar, NumericValue
1212

1313
if TYPE_CHECKING:

ibis/expr/types/numeric.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
import ibis
99
import ibis.expr.operations as ops
1010
from ibis.common.exceptions import IbisTypeError
11-
from ibis.expr.types.core import _binop
12-
from ibis.expr.types.generic import Column, Scalar, Value
11+
from ibis.expr.types.generic import Column, Scalar, Value, _binop
1312

1413
if TYPE_CHECKING:
1514
import ibis.expr.types as ir

ibis/expr/types/strings.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
import ibis.expr.operations as ops
1010
from ibis import util
11-
from ibis.expr.types.core import _binop
12-
from ibis.expr.types.generic import Column, Scalar, Value
11+
from ibis.expr.types.generic import Column, Scalar, Value, _binop
1312

1413
if TYPE_CHECKING:
1514
from collections.abc import Iterable, Sequence

ibis/expr/types/temporal.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
from ibis import util
1212
from ibis.common.annotations import annotated
1313
from ibis.common.temporal import IntervalUnit
14-
from ibis.expr.types.core import _binop
15-
from ibis.expr.types.generic import Column, Scalar, Value
14+
from ibis.expr.types.generic import Column, Scalar, Value, _binop
1615
from ibis.util import deprecated
1716

1817
if TYPE_CHECKING:

0 commit comments

Comments
 (0)