Skip to content

Commit 309db1f

Browse files
committed
feat(typing): Generic BinaryExpr, default TypeVars
- Needed to add the `ruff` config in the end (#2578) - Prevented forward refs in `TypeVar` from showing as unused imports
1 parent 6d524ba commit 309db1f

File tree

4 files changed

+41
-18
lines changed

4 files changed

+41
-18
lines changed

narwhals/_plan/expr.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,18 @@
1313
import typing as t
1414

1515
from narwhals._plan.common import ExprIR
16+
from narwhals._plan.typing import FunctionT, LeftT, OperatorT, RightT, RollingT
1617

1718
if t.TYPE_CHECKING:
1819
from typing_extensions import Self
1920

20-
from narwhals._plan.common import Function, Seq
21-
from narwhals._plan.functions import (
22-
MapBatches, # noqa: F401
23-
RollingWindow,
24-
)
21+
from narwhals._plan.common import Seq
22+
from narwhals._plan.functions import MapBatches # noqa: F401
2523
from narwhals._plan.literal import LiteralValue
26-
from narwhals._plan.operators import Operator
2724
from narwhals._plan.options import FunctionOptions, SortMultipleOptions, SortOptions
2825
from narwhals._plan.window import Window
2926
from narwhals.dtypes import DType
3027

31-
_FunctionT = t.TypeVar("_FunctionT", bound="Function")
32-
_RollingT = t.TypeVar("_RollingT", bound="RollingWindow")
33-
3428

3529
class Alias(ExprIR):
3630
__slots__ = ("expr", "name")
@@ -95,7 +89,7 @@ def __repr__(self) -> str:
9589
return f"lit({self.value!r})"
9690

9791

98-
class BinaryExpr(ExprIR):
92+
class BinaryExpr(ExprIR, t.Generic[LeftT, OperatorT, RightT]):
9993
"""Application of two exprs via an `Operator`.
10094
10195
This ✅
@@ -107,9 +101,9 @@ class BinaryExpr(ExprIR):
107101

108102
__slots__ = ("left", "op", "right")
109103

110-
left: ExprIR
111-
op: Operator
112-
right: ExprIR
104+
left: LeftT
105+
op: OperatorT
106+
right: RightT
113107

114108
@property
115109
def is_scalar(self) -> bool:
@@ -203,7 +197,7 @@ def iter_right(self) -> t.Iterator[ExprIR]:
203197
yield from self.expr.iter_right()
204198

205199

206-
class FunctionExpr(ExprIR, t.Generic[_FunctionT]):
200+
class FunctionExpr(ExprIR, t.Generic[FunctionT]):
207201
"""**Representing `Expr::Function`**.
208202
209203
https://github.com/pola-rs/polars/blob/dafd0a2d0e32b52bcfa4273bffdd6071a0d5977a/crates/polars-plan/src/dsl/expr.rs#L114-L120
@@ -214,7 +208,7 @@ class FunctionExpr(ExprIR, t.Generic[_FunctionT]):
214208
__slots__ = ("function", "input", "options")
215209

216210
input: Seq[ExprIR]
217-
function: _FunctionT
211+
function: FunctionT
218212
"""Enum type is named `FunctionExpr` in `polars`.
219213
220214
Mirroring *exactly* doesn't make much sense in OOP.
@@ -257,7 +251,7 @@ def iter_right(self) -> t.Iterator[ExprIR]:
257251
yield from e.iter_right()
258252

259253

260-
class RollingExpr(FunctionExpr[_RollingT]): ...
254+
class RollingExpr(FunctionExpr[RollingT]): ...
261255

262256

263257
class AnonymousExpr(FunctionExpr["MapBatches"]):

narwhals/_plan/operators.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
from typing import TYPE_CHECKING
44

55
if TYPE_CHECKING:
6+
from typing_extensions import Self
7+
68
from narwhals._plan.expr import BinaryExpr
9+
from narwhals._plan.typing import LeftT, RightT
710

8-
from narwhals._plan.common import ExprIR, Immutable
11+
from narwhals._plan.common import Immutable
912

1013

1114
class Operator(Immutable):
@@ -28,10 +31,13 @@ def __repr__(self) -> str:
2831
Modulus: "%",
2932
And: "&",
3033
Or: "|",
34+
ExclusiveOr: "^",
3135
}
3236
return m[tp]
3337

34-
def to_binary_expr(self, left: ExprIR, right: ExprIR, /) -> BinaryExpr:
38+
def to_binary_expr(
39+
self, left: LeftT, right: RightT, /
40+
) -> BinaryExpr[LeftT, Self, RightT]:
3541
from narwhals._plan.expr import BinaryExpr
3642

3743
return BinaryExpr(left=left, op=self, right=right)
@@ -77,3 +83,6 @@ class And(Operator): ...
7783

7884

7985
class Or(Operator): ...
86+
87+
88+
class ExclusiveOr(Operator): ...

narwhals/_plan/typing.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from __future__ import annotations
2+
3+
import typing as t
4+
5+
from narwhals._typing_compat import TypeVar
6+
7+
if t.TYPE_CHECKING:
8+
from narwhals._plan import operators as ops
9+
from narwhals._plan.common import ExprIR, Function
10+
from narwhals._plan.functions import RollingWindow
11+
12+
__all__ = ["FunctionT", "LeftT", "OperatorT", "RightT", "RollingT"]
13+
14+
15+
FunctionT = TypeVar("FunctionT", bound="Function")
16+
RollingT = TypeVar("RollingT", bound="RollingWindow")
17+
LeftT = TypeVar("LeftT", bound="ExprIR", default="ExprIR")
18+
OperatorT = TypeVar("OperatorT", bound="ops.Operator", default="ops.Operator")
19+
RightT = TypeVar("RightT", bound="ExprIR", default="ExprIR")

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ extend-exclude = ["**/this.py"]
130130
[tool.ruff.lint]
131131
preview = true
132132
explicit-preview-rules = true
133+
typing-modules = ["narwhals._typing_compat"]
133134

134135
extend-safe-fixes = [
135136
"C419", # unnecessary-comprehension-in-call

0 commit comments

Comments
 (0)