From 40e1737caad42831927fa94c7f179e9cb44838d0 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 1 Aug 2025 14:25:22 +0100 Subject: [PATCH 01/30] temp fix to type problem --- narwhals/_sql/typing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index c6634af6f3..c39190f31f 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -12,3 +12,7 @@ SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") + + +# it needs to inherit from SQLExpr, but getting errors if passing SQLExprT +class NativeSQLExpr(Any): ... From a8af8219eeff36c360ff06283d145fbd588ac463 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 1 Aug 2025 15:29:18 +0100 Subject: [PATCH 02/30] notes on what should happen --- narwhals/_sql/typing.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index c39190f31f..07938c93ff 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -14,5 +14,34 @@ SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -# it needs to inherit from SQLExpr, but getting errors if passing SQLExprT -class NativeSQLExpr(Any): ... +# it needs to inherit from SQLExpr, but getting errors if passing SQLExprT. I have not sorted out what relation +# (covarinant, contravariant or intravariant it needs to be yet) it needs to have to its parent class +class NativeSQLExpr(SQLExprT): + # not sure how to initialise the class, I want it to have all the methods etc of SQLExprT and add its own operator + # methods in addition to those + def __init__(self, other: [Int | Float]) -> None: + super().__init__(other) + + # I've seen lots of examples of the operator dunder methods being used, but I'm struggling with + # the typing. for sql, there are nice binary examples, but that's not the right method + # e.g. + def __gt__(self, other: Self) -> Self: + return self._with_binary(lambda expr, other: expr.__gt__(other), other) + + def __and__(self, other: Self) -> Self: + return self._with_binary(lambda expr, other: expr.__and__(other), other) + + # so it should probably look more like something from the polars examples, + # where we just apply the native method: + def __gt__(self, other: Any) -> Self: + return self._with_native(self.native.__gt__(extract_native(other))) + + def __le__(self, other: Any) -> Self: + return self._with_native(self.native.__le__(extract_native(other))) + + + + + + + From f8e853c0af69c7721b873095c7f054ebadfffde5 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 1 Aug 2025 17:12:42 +0100 Subject: [PATCH 03/30] corrected base class --- narwhals/_sql/typing.py | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 07938c93ff..c81dd4390e 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -2,6 +2,8 @@ from typing import TYPE_CHECKING, Any, TypeVar +from narwhals._compliant.expr import NativeExpr + if TYPE_CHECKING: from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr @@ -14,34 +16,4 @@ SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -# it needs to inherit from SQLExpr, but getting errors if passing SQLExprT. I have not sorted out what relation -# (covarinant, contravariant or intravariant it needs to be yet) it needs to have to its parent class -class NativeSQLExpr(SQLExprT): - # not sure how to initialise the class, I want it to have all the methods etc of SQLExprT and add its own operator - # methods in addition to those - def __init__(self, other: [Int | Float]) -> None: - super().__init__(other) - - # I've seen lots of examples of the operator dunder methods being used, but I'm struggling with - # the typing. for sql, there are nice binary examples, but that's not the right method - # e.g. - def __gt__(self, other: Self) -> Self: - return self._with_binary(lambda expr, other: expr.__gt__(other), other) - - def __and__(self, other: Self) -> Self: - return self._with_binary(lambda expr, other: expr.__and__(other), other) - - # so it should probably look more like something from the polars examples, - # where we just apply the native method: - def __gt__(self, other: Any) -> Self: - return self._with_native(self.native.__gt__(extract_native(other))) - - def __le__(self, other: Any) -> Self: - return self._with_native(self.native.__le__(extract_native(other))) - - - - - - - +class NativeSQLExpr(NativeExpr): ... From fda9a7d5e0dac645699f7893b25f9f5edce79c90 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 1 Aug 2025 17:21:11 +0100 Subject: [PATCH 04/30] wip experimenting with methods --- narwhals/_sql/typing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index c81dd4390e..fe632c44c5 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -16,4 +16,7 @@ SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -class NativeSQLExpr(NativeExpr): ... +class NativeSQLExpr(NativeExpr): + def __gt__(self, other: Any) -> Any: ... + + def __le__(self, other: Any) -> Any: ... From 86ed14acfa65119f01f807477ecbc090e3ee02a6 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 1 Aug 2025 17:45:38 +0100 Subject: [PATCH 05/30] wip improved types --- narwhals/_sql/typing.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index fe632c44c5..d003510b0d 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -7,6 +7,7 @@ if TYPE_CHECKING: from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr + from narwhals.dtypes import Boolean SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] @@ -17,6 +18,23 @@ class NativeSQLExpr(NativeExpr): - def __gt__(self, other: Any) -> Any: ... + # TODO @mp: fix input type for all these! + def __gt__(self, value: float) -> Boolean: ... - def __le__(self, other: Any) -> Any: ... + def __lt__(self, value: float) -> Boolean: ... + + def __ge__(self, value: float) -> Boolean: ... + + def __le__(self, value: float) -> Boolean: ... + + def __eq__(self, value: float) -> Boolean: ... + + def __ne__(self, value: float) -> Boolean: ... + # do we want any more of the arithmetic methods? I wasn't sure between lefthan & righthand methods.. + def __sub__(self, value: float) -> Any: ... + + def __add__(self, value: float) -> Any: ... + + def __truediv__(self, value: float) -> Any: ... + + def __mul__(self, value: float) -> Any: ... From ec089493e2df6cf5502e7227a4fe915a5d760a29 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Sun, 3 Aug 2025 17:08:58 +0100 Subject: [PATCH 06/30] wip operator --- narwhals/_sql/expr.py | 10 +++++----- narwhals/_sql/typing.py | 27 ++++++++++++++------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 53bfe5f56a..fecccc3075 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -15,7 +15,7 @@ combine_alias_output_names, combine_evaluate_output_names, ) -from narwhals._sql.typing import SQLLazyFrameT +from narwhals._sql.typing import SQLLazyFrameT, NativeSQLExprT from narwhals._utils import Implementation, Version, not_implemented if TYPE_CHECKING: @@ -249,7 +249,7 @@ def _rolling_window_func( end = 0 def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] ) -> Sequence[NativeExprT]: if func_name in {"sum", "mean"}: func_: str = func_name @@ -496,11 +496,11 @@ def round(self, decimals: int) -> Self: return self._with_elementwise( lambda expr: self._function("round", expr, self._lit(decimals)) ) - + # WIP: trying new NativeSQLExprT def sqrt(self) -> Self: - def _sqrt(expr: NativeExprT) -> NativeExprT: + def _sqrt(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), # type: ignore[operator] + expr < self._lit(0), self._lit(float("nan")), self._function("sqrt", expr), ) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index d003510b0d..981f0eb0d5 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, Self from narwhals._compliant.expr import NativeExpr @@ -9,32 +9,33 @@ from narwhals._sql.expr import SQLExpr from narwhals.dtypes import Boolean + # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") - +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") class NativeSQLExpr(NativeExpr): - # TODO @mp: fix input type for all these! - def __gt__(self, value: float) -> Boolean: ... + # both Self because we're comparing an expression with an expression? + def __gt__(self, value: Self) -> Self: ... - def __lt__(self, value: float) -> Boolean: ... + def __lt__(self, value: Self) -> Self: ... - def __ge__(self, value: float) -> Boolean: ... + def __ge__(self, value: Self) -> Self: ... - def __le__(self, value: float) -> Boolean: ... + def __le__(self, value: Self) -> Self: ... - def __eq__(self, value: float) -> Boolean: ... + def __eq__(self, value: Self) -> Self: ... - def __ne__(self, value: float) -> Boolean: ... + def __ne__(self, value: Self) -> Self: ... # do we want any more of the arithmetic methods? I wasn't sure between lefthan & righthand methods.. - def __sub__(self, value: float) -> Any: ... + def __sub__(self, value: Self) -> Self: ... - def __add__(self, value: float) -> Any: ... + def __add__(self, value: Self) -> Self: ... - def __truediv__(self, value: float) -> Any: ... + def __truediv__(self, value: Self) -> Self: ... - def __mul__(self, value: float) -> Any: ... + def __mul__(self, value: Self) -> Self: ... From a4de0bbc13cc10a0aa2f3752681a77663667fed3 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Sun, 3 Aug 2025 18:04:16 +0100 Subject: [PATCH 07/30] wip operator: added thoughts --- narwhals/_sql/typing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 981f0eb0d5..67b77c9841 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -16,7 +16,8 @@ SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") +# TODO: @mp, should this be contravariant as to do with function arguments? think through! +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") class NativeSQLExpr(NativeExpr): # both Self because we're comparing an expression with an expression? From d372db6e2341e71656c134e36c1033170e36dacd Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 09:07:46 +0100 Subject: [PATCH 08/30] wip operator: modified Self import --- narwhals/_sql/expr.py | 2 +- narwhals/_sql/typing.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index fecccc3075..e88be184c3 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -500,7 +500,7 @@ def round(self, decimals: int) -> Self: def sqrt(self) -> Self: def _sqrt(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), + expr < self._lit(0), # type: ignore[operator] self._lit(float("nan")), self._function("sqrt", expr), ) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 67b77c9841..250044f607 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, TypeVar, Self +from typing import TYPE_CHECKING, Any, TypeVar from narwhals._compliant.expr import NativeExpr @@ -8,6 +8,7 @@ from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr from narwhals.dtypes import Boolean + from typing_extensions import Self # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT SQLExprAny = SQLExpr[Any, Any] From 3a97dcb59493a95811261ee555c4016dd0b1cfa9 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 10:45:12 +0100 Subject: [PATCH 09/30] wip operator: added thoughts & only using in sqrt --- narwhals/_sql/expr.py | 2 +- narwhals/_sql/typing.py | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index e88be184c3..9539cac17e 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -249,7 +249,7 @@ def _rolling_window_func( end = 0 def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] ) -> Sequence[NativeExprT]: if func_name in {"sum", "mean"}: func_: str = func_name diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 250044f607..57278e89d0 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -4,22 +4,6 @@ from narwhals._compliant.expr import NativeExpr -if TYPE_CHECKING: - from narwhals._sql.dataframe import SQLLazyFrame - from narwhals._sql.expr import SQLExpr - from narwhals.dtypes import Boolean - from typing_extensions import Self - - # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT - SQLExprAny = SQLExpr[Any, Any] - SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] - -SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") -SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) -SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -# TODO: @mp, should this be contravariant as to do with function arguments? think through! -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") - class NativeSQLExpr(NativeExpr): # both Self because we're comparing an expression with an expression? def __gt__(self, value: Self) -> Self: ... @@ -41,3 +25,22 @@ def __add__(self, value: Self) -> Self: ... def __truediv__(self, value: Self) -> Self: ... def __mul__(self, value: Self) -> Self: ... + +if TYPE_CHECKING: + from narwhals._sql.dataframe import SQLLazyFrame + from narwhals._sql.expr import SQLExpr + from narwhals.dtypes import Boolean + from typing_extensions import Self + + # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT; + # seem to reflect number of different 'catgories' each of the parent class has + # tbc! since NativeExpr only has Protocol, I don't think we need this for NativeSQLExpr + SQLExprAny = SQLExpr[Any, Any] + SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] + +SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") +SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) +SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") +# TODO: @mp, should this be contravariant as to do with function arguments? think through! +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") + From 9a544e8de5480523f8527a13c450d6c08239b2bb Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 11:20:02 +0100 Subject: [PATCH 10/30] wip operator: more comments, going back to reading --- narwhals/_sql/expr.py | 10 ++++++---- narwhals/_sql/typing.py | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 9539cac17e..a713db003b 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -28,15 +28,17 @@ from narwhals._sql.namespace import SQLNamespace from narwhals.typing import NumericLiteral, PythonLiteral, RankMethod, TemporalLiteral - -class SQLExpr(LazyExpr[SQLLazyFrameT, NativeExprT], Protocol[SQLLazyFrameT, NativeExprT]): - _call: EvalSeries[SQLLazyFrameT, NativeExprT] +# am I right in thinking we need to pass NativeSQLExprT here, not NativeExprT? since NativeSQLExprT +# inherits from it, I can use that througout in the code and it will also have its parents functionality. +# though at the moment there are still problems with the class.. +class SQLExpr(LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, NativeSQLExprT]): + _call: EvalSeries[SQLLazyFrameT, NativeSQLExprT] _evaluate_output_names: EvalNames[SQLLazyFrameT] _alias_output_names: AliasNames | None _version: Version _implementation: Implementation _metadata: ExprMetadata | None - _window_function: WindowFunction[SQLLazyFrameT, NativeExprT] | None + _window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] | None def __init__( self, diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 57278e89d0..ab0994a124 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -35,8 +35,11 @@ def __mul__(self, value: Self) -> Self: ... # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT; # seem to reflect number of different 'catgories' each of the parent class has # tbc! since NativeExpr only has Protocol, I don't think we need this for NativeSQLExpr + # NativeSQLExpr isn't accepting Any arguments :) I need to go back to the reading on + # cov-, contra- & invariance SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] + NativeSQLExprAny = NativeSQLExpr SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) From 266cab923e77b60a75c5880cf46261792d85d045 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 13:02:15 +0100 Subject: [PATCH 11/30] wip operator: fixed NativeSQLExprT bound --- narwhals/_sql/expr.py | 2 +- narwhals/_sql/typing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index a713db003b..dfef5e6d64 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -502,7 +502,7 @@ def round(self, decimals: int) -> Self: def sqrt(self) -> Self: def _sqrt(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), # type: ignore[operator] + expr < self._lit(0), self._lit(float("nan")), self._function("sqrt", expr), ) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index ab0994a124..77d0ae642a 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -45,5 +45,5 @@ def __mul__(self, value: Self) -> Self: ... SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") # TODO: @mp, should this be contravariant as to do with function arguments? think through! -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExprAny") From 4aae0690d8457937d9f7267895c6434f4faa8b93 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 13:22:51 +0100 Subject: [PATCH 12/30] wip operator: adding NativeSQLExprT to get rid of operator issue --- narwhals/_sql/expr.py | 49 +++++++++++++++++++++-------------------- narwhals/_sql/typing.py | 2 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index dfef5e6d64..edc8880c23 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -102,7 +102,7 @@ def window_f( return window_f def _with_window_function( - self, window_function: WindowFunction[SQLLazyFrameT, NativeExprT] + self, window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] ) -> Self: return self.__class__( self._call, @@ -165,10 +165,10 @@ def _with_alias_output_names(self, func: AliasNames | None, /) -> Self: ) @property - def window_function(self) -> WindowFunction[SQLLazyFrameT, NativeExprT]: + def window_function(self) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: def default_window_func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: assert not inputs.order_by # noqa: S101 return [ self._window_expression(expr, inputs.partition_by, inputs.order_by) @@ -277,7 +277,7 @@ def func( } return [ self._when( - self._window_expression( # type: ignore[operator] + self._window_expression( self._function("count", expr), **window_kwargs ) >= self._lit(min_samples), @@ -308,8 +308,8 @@ def call(df: SQLLazyFrameT) -> Sequence[NativeExprT]: return [func(cols)] def window_function( - df: SQLLazyFrameT, window_inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, window_inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: cols = ( col for _expr in exprs for col in _expr.window_function(df, window_inputs) ) @@ -513,14 +513,14 @@ def exp(self) -> Self: return self._with_elementwise(lambda expr: self._function("exp", expr)) def log(self, base: float) -> Self: - def _log(expr: NativeExprT) -> NativeExprT: + def _log(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), # type: ignore[operator] + expr < self._lit(0), self._lit(float("nan")), self._when( - cast("NativeExprT", expr == self._lit(0)), + cast("NativeSQLExprT", expr == self._lit(0)), self._lit(float("-inf")), - self._function("log", expr) / self._function("log", self._lit(base)), # type: ignore[operator] + self._function("log", expr) / self._function("log", self._lit(base)), ), ) @@ -576,10 +576,10 @@ def rolling_std( # Other window functions def diff(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ - expr # type: ignore[operator] + expr - self._window_expression( self._function("lag", expr), inputs.partition_by, inputs.order_by ) @@ -651,13 +651,13 @@ def rank(self, method: RankMethod, *, descending: bool) -> Self: func = self._function("row_number") def _rank( - expr: NativeExprT, - partition_by: Sequence[str | NativeExprT] = (), - order_by: Sequence[str | NativeExprT] = (), + expr: NativeSQLExprT, + partition_by: Sequence[str | NativeSQLExprT] = (), + order_by: Sequence[str | NativeSQLExprT] = (), *, descending: Sequence[bool], nulls_last: Sequence[bool], - ) -> NativeExprT: + ) -> NativeSQLExprT: count_expr = self._count_star() window_kwargs: dict[str, Any] = { "partition_by": partition_by, @@ -668,25 +668,26 @@ def _rank( count_window_kwargs: dict[str, Any] = {"partition_by": (*partition_by, expr)} if method == "max": rank_expr = ( - self._window_expression(func, **window_kwargs) # type: ignore[operator] + self._window_expression(func, **window_kwargs) + self._window_expression(count_expr, **count_window_kwargs) - self._lit(1) ) elif method == "average": rank_expr = self._window_expression(func, **window_kwargs) + ( - self._window_expression(count_expr, **count_window_kwargs) # type: ignore[operator] + self._window_expression(count_expr, **count_window_kwargs) - self._lit(1) ) / self._lit(2.0) else: rank_expr = self._window_expression(func, **window_kwargs) - return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] + # TODO: @mp, thought I added this to NativeSQLExprT but not working? + return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] - def _unpartitioned_rank(expr: NativeExprT) -> NativeExprT: + def _unpartitioned_rank(expr: NativeSQLExprT) -> NativeSQLExprT: return _rank(expr, descending=[descending], nulls_last=[True]) def _partitioned_rank( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: # node: when `descending` / `nulls_last` are supported in `.over`, they should be respected here # https://github.com/narwhals-dev/narwhals/issues/2790 return [ diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 77d0ae642a..7d1376d255 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -26,6 +26,8 @@ def __truediv__(self, value: Self) -> Self: ... def __mul__(self, value: Self) -> Self: ... + def __invert__(self, value: Self) -> Self: ... + if TYPE_CHECKING: from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr From 7ad112a4d76cdf76751d834d6baf69950199a094 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 14:45:16 +0100 Subject: [PATCH 13/30] wip operator: added all changes from pair session, 1 problem remaining --- narwhals/_sql/expr.py | 132 +++++++++++++++++++--------------------- narwhals/_sql/typing.py | 63 ++++++++++--------- 2 files changed, 95 insertions(+), 100 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index edc8880c23..39584627cc 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -7,7 +7,6 @@ AliasNames, EvalNames, EvalSeries, - NativeExprT, WindowFunction, ) from narwhals._compliant.window import WindowInputs @@ -28,9 +27,6 @@ from narwhals._sql.namespace import SQLNamespace from narwhals.typing import NumericLiteral, PythonLiteral, RankMethod, TemporalLiteral -# am I right in thinking we need to pass NativeSQLExprT here, not NativeExprT? since NativeSQLExprT -# inherits from it, I can use that througout in the code and it will also have its parents functionality. -# though at the moment there are still problems with the class.. class SQLExpr(LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, NativeSQLExprT]): _call: EvalSeries[SQLLazyFrameT, NativeSQLExprT] _evaluate_output_names: EvalNames[SQLLazyFrameT] @@ -42,8 +38,8 @@ class SQLExpr(LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, N def __init__( self, - call: EvalSeries[SQLLazyFrameT, NativeExprT], - window_function: WindowFunction[SQLLazyFrameT, NativeExprT] | None = None, + call: EvalSeries[SQLLazyFrameT, NativeSQLExprT], + window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] | None = None, *, evaluate_output_names: EvalNames[SQLLazyFrameT], alias_output_names: AliasNames | None, @@ -51,17 +47,17 @@ def __init__( implementation: Implementation = Implementation.DUCKDB, ) -> None: ... - def __call__(self, df: SQLLazyFrameT) -> Sequence[NativeExprT]: + def __call__(self, df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: return self._call(df) def __narwhals_namespace__( self, - ) -> SQLNamespace[SQLLazyFrameT, Self, Any, NativeExprT]: ... + ) -> SQLNamespace[SQLLazyFrameT, Self, Any, NativeSQLExprT]: ... def _callable_to_eval_series( - self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any - ) -> EvalSeries[SQLLazyFrameT, NativeExprT]: - def func(df: SQLLazyFrameT) -> list[NativeExprT]: + self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any + ) -> EvalSeries[SQLLazyFrameT, NativeSQLExprT]: + def func(df: SQLLazyFrameT) -> list[NativeSQLExprT]: native_series_list = self(df) other_native_series = { key: df._evaluate_expr(value) @@ -77,11 +73,11 @@ def func(df: SQLLazyFrameT) -> list[NativeExprT]: return func def _push_down_window_function( - self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any - ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: + self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any + ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: def window_f( - df: SQLLazyFrameT, window_inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, window_inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: # If a function `f` is elementwise, and `g` is another function, then # - `f(g) over (window)` # - `f(g over (window)) @@ -114,7 +110,7 @@ def _with_window_function( ) def _with_callable( - self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any + self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any ) -> Self: return self.__class__( self._callable_to_eval_series(call, **expressifiable_args), @@ -125,7 +121,7 @@ def _with_callable( ) def _with_elementwise( - self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any + self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any ) -> Self: return self.__class__( self._callable_to_eval_series(call, **expressifiable_args), @@ -136,7 +132,7 @@ def _with_elementwise( implementation=self._implementation, ) - def _with_binary(self, op: Callable[..., NativeExprT], other: Self | Any) -> Self: + def _with_binary(self, op: Callable[..., NativeSQLExprT], other: Self | Any) -> Self: return self.__class__( self._callable_to_eval_series(op, other=other), self._push_down_window_function(op, other=other), @@ -177,46 +173,46 @@ def default_window_func( return self._window_function or default_window_func - def _function(self, name: str, *args: NativeExprT | PythonLiteral) -> NativeExprT: + def _function(self, name: str, *args: NativeSQLExprT | PythonLiteral) -> NativeSQLExprT: return self.__narwhals_namespace__()._function(name, *args) - def _lit(self, value: Any) -> NativeExprT: + def _lit(self, value: Any) -> NativeSQLExprT: return self.__narwhals_namespace__()._lit(value) - def _coalesce(self, *expr: NativeExprT) -> NativeExprT: + def _coalesce(self, *expr: NativeSQLExprT) -> NativeSQLExprT: return self.__narwhals_namespace__()._coalesce(*expr) - def _count_star(self) -> NativeExprT: ... + def _count_star(self) -> NativeSQLExprT: ... def _when( self, - condition: NativeExprT, - value: NativeExprT, - otherwise: NativeExprT | None = None, - ) -> NativeExprT: + condition: NativeSQLExprT, + value: NativeSQLExprT, + otherwise: NativeSQLExprT | None = None, + ) -> NativeSQLExprT: return self.__narwhals_namespace__()._when(condition, value, otherwise) def _window_expression( self, - expr: NativeExprT, - partition_by: Sequence[str | NativeExprT] = (), - order_by: Sequence[str | NativeExprT] = (), + expr: NativeSQLExprT, + partition_by: Sequence[str | NativeSQLExprT] = (), + order_by: Sequence[str | NativeSQLExprT] = (), rows_start: int | None = None, rows_end: int | None = None, *, descending: Sequence[bool] | None = None, nulls_last: Sequence[bool] | None = None, - ) -> NativeExprT: ... + ) -> NativeSQLExprT: ... def _cum_window_func( self, func_name: Literal["sum", "max", "min", "count", "product"], *, reverse: bool, - ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: + ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ self._window_expression( self._function(func_name, expr), @@ -239,7 +235,7 @@ def _rolling_window_func( ddof: int | None = None, *, center: bool, - ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: + ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: supported_funcs = ["sum", "mean", "std", "var"] if center: half = (window_size - 1) // 2 @@ -251,8 +247,8 @@ def _rolling_window_func( end = 0 def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: if func_name in {"sum", "mean"}: func_: str = func_name elif func_name == "var" and ddof == 0: @@ -297,13 +293,13 @@ def _backend_version(self) -> tuple[int, ...]: return self._implementation._backend_version() @classmethod - def _alias_native(cls, expr: NativeExprT, name: str, /) -> NativeExprT: ... + def _alias_native(cls, expr: NativeSQLExprT, name: str, /) -> NativeSQLExprT: ... @classmethod def _from_elementwise_horizontal_op( - cls, func: Callable[[Iterable[NativeExprT]], NativeExprT], *exprs: Self + cls, func: Callable[[Iterable[NativeSQLExprT]], NativeSQLExprT], *exprs: Self ) -> Self: - def call(df: SQLLazyFrameT) -> Sequence[NativeExprT]: + def call(df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: cols = (col for _expr in exprs for col in _expr(df)) return [func(cols)] @@ -390,12 +386,12 @@ def __or__(self, other: Self) -> Self: # Aggregations def all(self) -> Self: - def f(expr: NativeExprT) -> NativeExprT: + def f(expr: NativeSQLExprT) -> NativeSQLExprT: return self._coalesce(self._function("bool_and", expr), self._lit(True)) # noqa: FBT003 def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ self._coalesce( self._window_expression( @@ -409,12 +405,12 @@ def window_f( return self._with_callable(f)._with_window_function(window_f) def any(self) -> Self: - def f(expr: NativeExprT) -> NativeExprT: + def f(expr: NativeSQLExprT) -> NativeSQLExprT: return self._coalesce(self._function("bool_or", expr), self._lit(False)) # noqa: FBT003 def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ self._coalesce( self._window_expression( @@ -443,12 +439,12 @@ def count(self) -> Self: return self._with_callable(lambda expr: self._function("count", expr)) def sum(self) -> Self: - def f(expr: NativeExprT) -> NativeExprT: + def f(expr: NativeSQLExprT) -> NativeSQLExprT: return self._coalesce(self._function("sum", expr), self._lit(0)) def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ self._coalesce( self._window_expression( @@ -470,15 +466,15 @@ def clip( lower_bound: Self | NumericLiteral | TemporalLiteral | None, upper_bound: Self | NumericLiteral | TemporalLiteral | None, ) -> Self: - def _clip_lower(expr: NativeExprT, lower_bound: Any) -> NativeExprT: + def _clip_lower(expr: NativeSQLExprT, lower_bound: Any) -> NativeSQLExprT: return self._function("greatest", expr, lower_bound) - def _clip_upper(expr: NativeExprT, upper_bound: Any) -> NativeExprT: + def _clip_upper(expr: NativeSQLExprT, upper_bound: Any) -> NativeSQLExprT: return self._function("least", expr, upper_bound) def _clip_both( - expr: NativeExprT, lower_bound: Any, upper_bound: Any - ) -> NativeExprT: + expr: NativeSQLExprT, lower_bound: Any, upper_bound: Any + ) -> NativeSQLExprT: return self._function( "greatest", self._function("least", expr, upper_bound), lower_bound ) @@ -590,8 +586,8 @@ def func( def shift(self, n: int) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ self._window_expression( self._function("lag", expr, n), inputs.partition_by, inputs.order_by @@ -603,12 +599,12 @@ def func( def is_first_distinct(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: # pyright checkers think the return type is `list[bool]` because of `==` return [ cast( - "NativeExprT", + "NativeSQLExprT", self._window_expression( self._function("row_number"), (*inputs.partition_by, expr), @@ -623,11 +619,11 @@ def func( def is_last_distinct(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: return [ cast( - "NativeExprT", + "NativeSQLExprT", self._window_expression( self._function("row_number"), (*inputs.partition_by, expr), @@ -707,20 +703,20 @@ def _partitioned_rank( def is_unique(self) -> Self: def _is_unique( - expr: NativeExprT, *partition_by: str | NativeExprT - ) -> NativeExprT: + expr: NativeSQLExprT, *partition_by: str | NativeSQLExprT + ) -> NativeSQLExprT: return cast( - "NativeExprT", + "NativeSQLExprT", self._window_expression(self._count_star(), (expr, *partition_by)) == self._lit(1), ) - def _unpartitioned_is_unique(expr: NativeExprT) -> NativeExprT: + def _unpartitioned_is_unique(expr: NativeSQLExprT) -> NativeSQLExprT: return _is_unique(expr) def _partitioned_is_unique( - df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] - ) -> Sequence[NativeExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] + ) -> Sequence[NativeSQLExprT]: assert not inputs.order_by # noqa: S101 return [_is_unique(expr, *inputs.partition_by) for expr in self(df)] @@ -730,9 +726,9 @@ def _partitioned_is_unique( # Other def over( - self, partition_by: Sequence[str | NativeExprT], order_by: Sequence[str] + self, partition_by: Sequence[str | NativeSQLExprT], order_by: Sequence[str] ) -> Self: - def func(df: SQLLazyFrameT) -> Sequence[NativeExprT]: + def func(df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: return self.window_function(df, WindowInputs(partition_by, order_by)) return self.__class__( diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 7d1376d255..54fdc18f1d 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -1,51 +1,50 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, Protocol from narwhals._compliant.expr import NativeExpr -class NativeSQLExpr(NativeExpr): - # both Self because we're comparing an expression with an expression? - def __gt__(self, value: Self) -> Self: ... - - def __lt__(self, value: Self) -> Self: ... - - def __ge__(self, value: Self) -> Self: ... - - def __le__(self, value: Self) -> Self: ... - - def __eq__(self, value: Self) -> Self: ... - - def __ne__(self, value: Self) -> Self: ... - # do we want any more of the arithmetic methods? I wasn't sure between lefthan & righthand methods.. - def __sub__(self, value: Self) -> Self: ... - - def __add__(self, value: Self) -> Self: ... - - def __truediv__(self, value: Self) -> Self: ... - - def __mul__(self, value: Self) -> Self: ... - - def __invert__(self, value: Self) -> Self: ... - if TYPE_CHECKING: from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr from narwhals.dtypes import Boolean from typing_extensions import Self - # TODO: @mp, understand why these are here & if we need one for NativeSQLExprT; - # seem to reflect number of different 'catgories' each of the parent class has - # tbc! since NativeExpr only has Protocol, I don't think we need this for NativeSQLExpr - # NativeSQLExpr isn't accepting Any arguments :) I need to go back to the reading on - # cov-, contra- & invariance + # TODO: check we SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] - NativeSQLExprAny = NativeSQLExpr SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") # TODO: @mp, should this be contravariant as to do with function arguments? think through! -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExprAny") +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") + +class NativeSQLExpr(NativeExpr, Protocol): + # both Self because we're comparing an expression with an expression? + def __gt__(self, value: Any, /) -> Self: ... + + def __lt__(self, value: Any, /) -> Self: ... + + def __ge__(self, value: Any, /) -> Self: ... + + # def __le__(self, value: Self) -> Self: ... + + # def __eq__(self, value: Self) -> Self: ... + + # def __ne__(self, value: Self) -> Self: ... + # # do we want any more of the arithmetic methods? I wasn't sure between lefthan & righthand methods.. + # def __sub__(self, value: Self) -> Self: ... + + def __add__(self, value: Any, /) -> Self: ... + + def __sub__(self, value: Any, /) -> Self: ... + + def __truediv__(self, value: Any, /) -> Self: ... + + # def __mul__(self, value: Self) -> Self: ... + + # def __invert__(self, value: Self) -> Self: ... + + From 937e2a14c347c042c08bfe01920571547b3b566c Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 15:07:37 +0100 Subject: [PATCH 14/30] checkpoint --- narwhals/_sql/typing.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 54fdc18f1d..36f967f558 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -17,7 +17,6 @@ SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -# TODO: @mp, should this be contravariant as to do with function arguments? think through! NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") class NativeSQLExpr(NativeExpr, Protocol): @@ -44,7 +43,7 @@ def __truediv__(self, value: Any, /) -> Self: ... # def __mul__(self, value: Self) -> Self: ... - # def __invert__(self, value: Self) -> Self: ... + #def __invert__(self) -> Self: ... From 854e2f308bd4429a30faaa06abc1b4bf2d766f23 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 16:56:59 +0100 Subject: [PATCH 15/30] joy: only 1 type error to go --- narwhals/_sql/dataframe.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/narwhals/_sql/dataframe.py b/narwhals/_sql/dataframe.py index 80e8d5143b..e66c4b8324 100644 --- a/narwhals/_sql/dataframe.py +++ b/narwhals/_sql/dataframe.py @@ -11,6 +11,7 @@ from narwhals._compliant.window import WindowInputs from narwhals._sql.expr import SQLExpr + from narwhals._sql.typing import NativeSQLExprT Incomplete: TypeAlias = Any @@ -21,10 +22,10 @@ class SQLLazyFrame( ): def _evaluate_window_expr( self, - expr: SQLExpr[Self, NativeExprT], + expr: SQLExpr[Self, NativeSQLExprT], /, - window_inputs: WindowInputs[NativeExprT], - ) -> NativeExprT: + window_inputs: WindowInputs[NativeSQLExprT], + ) -> NativeSQLExprT: result = expr.window_function(self, window_inputs) assert len(result) == 1 # debug assertion # noqa: S101 return result[0] From a2deceb20f0dc5c605b9e1e4d0f80464ae4da34c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:11:33 +0000 Subject: [PATCH 16/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- narwhals/_sql/dataframe.py | 2 +- narwhals/_sql/expr.py | 46 +++++++++++++++++++++----------------- narwhals/_sql/typing.py | 18 +++++++-------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/narwhals/_sql/dataframe.py b/narwhals/_sql/dataframe.py index 40de29e434..7e18df50a8 100644 --- a/narwhals/_sql/dataframe.py +++ b/narwhals/_sql/dataframe.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Protocol from narwhals._compliant.dataframe import CompliantLazyFrame -from narwhals._compliant.typing import CompliantExprT_contra, NativeExprT, NativeFrameT +from narwhals._compliant.typing import CompliantExprT_contra, NativeFrameT from narwhals._translate import ToNarwhalsT_co from narwhals._utils import check_columns_exist diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 39584627cc..e7553e5ca7 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -3,18 +3,13 @@ from typing import TYPE_CHECKING, Any, Callable, Literal, Protocol, cast from narwhals._compliant.expr import LazyExpr -from narwhals._compliant.typing import ( - AliasNames, - EvalNames, - EvalSeries, - WindowFunction, -) +from narwhals._compliant.typing import AliasNames, WindowFunction from narwhals._compliant.window import WindowInputs from narwhals._expression_parsing import ( combine_alias_output_names, combine_evaluate_output_names, ) -from narwhals._sql.typing import SQLLazyFrameT, NativeSQLExprT +from narwhals._sql.typing import NativeSQLExprT, SQLLazyFrameT from narwhals._utils import Implementation, Version, not_implemented if TYPE_CHECKING: @@ -22,12 +17,20 @@ from typing_extensions import Self, TypeIs - from narwhals._compliant.typing import AliasNames, WindowFunction + from narwhals._compliant.typing import ( + AliasNames, + EvalNames, + EvalSeries, + WindowFunction, + ) from narwhals._expression_parsing import ExprMetadata from narwhals._sql.namespace import SQLNamespace from narwhals.typing import NumericLiteral, PythonLiteral, RankMethod, TemporalLiteral -class SQLExpr(LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, NativeSQLExprT]): + +class SQLExpr( + LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, NativeSQLExprT] +): _call: EvalSeries[SQLLazyFrameT, NativeSQLExprT] _evaluate_output_names: EvalNames[SQLLazyFrameT] _alias_output_names: AliasNames | None @@ -173,7 +176,9 @@ def default_window_func( return self._window_function or default_window_func - def _function(self, name: str, *args: NativeSQLExprT | PythonLiteral) -> NativeSQLExprT: + def _function( + self, name: str, *args: NativeSQLExprT | PythonLiteral + ) -> NativeSQLExprT: return self.__narwhals_namespace__()._function(name, *args) def _lit(self, value: Any) -> NativeSQLExprT: @@ -273,7 +278,7 @@ def func( } return [ self._when( - self._window_expression( + self._window_expression( self._function("count", expr), **window_kwargs ) >= self._lit(min_samples), @@ -494,13 +499,12 @@ def round(self, decimals: int) -> Self: return self._with_elementwise( lambda expr: self._function("round", expr, self._lit(decimals)) ) - # WIP: trying new NativeSQLExprT + + # WIP: trying new NativeSQLExprT def sqrt(self) -> Self: def _sqrt(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), - self._lit(float("nan")), - self._function("sqrt", expr), + expr < self._lit(0), self._lit(float("nan")), self._function("sqrt", expr) ) return self._with_elementwise(_sqrt) @@ -511,12 +515,12 @@ def exp(self) -> Self: def log(self, base: float) -> Self: def _log(expr: NativeSQLExprT) -> NativeSQLExprT: return self._when( - expr < self._lit(0), + expr < self._lit(0), self._lit(float("nan")), self._when( cast("NativeSQLExprT", expr == self._lit(0)), self._lit(float("-inf")), - self._function("log", expr) / self._function("log", self._lit(base)), + self._function("log", expr) / self._function("log", self._lit(base)), ), ) @@ -664,19 +668,19 @@ def _rank( count_window_kwargs: dict[str, Any] = {"partition_by": (*partition_by, expr)} if method == "max": rank_expr = ( - self._window_expression(func, **window_kwargs) + self._window_expression(func, **window_kwargs) + self._window_expression(count_expr, **count_window_kwargs) - self._lit(1) ) elif method == "average": rank_expr = self._window_expression(func, **window_kwargs) + ( - self._window_expression(count_expr, **count_window_kwargs) + self._window_expression(count_expr, **count_window_kwargs) - self._lit(1) ) / self._lit(2.0) else: rank_expr = self._window_expression(func, **window_kwargs) - # TODO: @mp, thought I added this to NativeSQLExprT but not working? - return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] + # TODO: @mp, thought I added this to NativeSQLExprT but not working? + return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] def _unpartitioned_rank(expr: NativeSQLExprT) -> NativeSQLExprT: return _rank(expr, descending=[descending], nulls_last=[True]) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 36f967f558..222a4b4bd6 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -1,26 +1,27 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, TypeVar, Protocol +from typing import TYPE_CHECKING, Any, Protocol, TypeVar from narwhals._compliant.expr import NativeExpr if TYPE_CHECKING: + from typing_extensions import Self + from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr - from narwhals.dtypes import Boolean - from typing_extensions import Self - # TODO: check we + # TODO: check we SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") +NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") + class NativeSQLExpr(NativeExpr, Protocol): - # both Self because we're comparing an expression with an expression? + # both Self because we're comparing an expression with an expression? def __gt__(self, value: Any, /) -> Self: ... def __lt__(self, value: Any, /) -> Self: ... @@ -43,7 +44,4 @@ def __truediv__(self, value: Any, /) -> Self: ... # def __mul__(self, value: Self) -> Self: ... - #def __invert__(self) -> Self: ... - - - + # def __invert__(self) -> Self: ... From a9cabad5cf93025387fc581407746906173e2cd0 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 17:52:32 +0100 Subject: [PATCH 17/30] silencing ibisexpr type error --- narwhals/_ibis/expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/narwhals/_ibis/expr.py b/narwhals/_ibis/expr.py index cf970a3d86..85af89c9cd 100644 --- a/narwhals/_ibis/expr.py +++ b/narwhals/_ibis/expr.py @@ -38,7 +38,7 @@ IbisWindowInputs = WindowInputs[ir.Value] -class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): +class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # type: ignore[operator] _implementation = Implementation.IBIS def __init__( From a2c4f312e7af304eb4ff0c9698ebc5034ae26204 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Mon, 4 Aug 2025 17:54:40 +0100 Subject: [PATCH 18/30] correcting error silencing --- narwhals/_ibis/expr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/narwhals/_ibis/expr.py b/narwhals/_ibis/expr.py index 85af89c9cd..682831d415 100644 --- a/narwhals/_ibis/expr.py +++ b/narwhals/_ibis/expr.py @@ -38,7 +38,7 @@ IbisWindowInputs = WindowInputs[ir.Value] -class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # type: ignore[operator] +class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # pyright: ignore[reportInvalidTypeArguments] _implementation = Implementation.IBIS def __init__( From dea1fe227c621e68136058184763d24224eb47c2 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 09:09:50 +0100 Subject: [PATCH 19/30] adding extra dunder methods --- narwhals/_sql/typing.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 222a4b4bd6..c243792db1 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -10,7 +10,6 @@ from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr - # TODO: check we SQLExprAny = SQLExpr[Any, Any] SQLLazyFrameAny = SQLLazyFrame[Any, Any, Any] @@ -28,13 +27,11 @@ def __lt__(self, value: Any, /) -> Self: ... def __ge__(self, value: Any, /) -> Self: ... - # def __le__(self, value: Self) -> Self: ... + def __le__(self, value: Any, /) -> Self: ... - # def __eq__(self, value: Self) -> Self: ... + def __eq__(self, value: Any, /) -> Self: ... - # def __ne__(self, value: Self) -> Self: ... - # # do we want any more of the arithmetic methods? I wasn't sure between lefthan & righthand methods.. - # def __sub__(self, value: Self) -> Self: ... + def __ne__(self, value: Any, /) -> Self: ... def __add__(self, value: Any, /) -> Self: ... @@ -42,6 +39,6 @@ def __sub__(self, value: Any, /) -> Self: ... def __truediv__(self, value: Any, /) -> Self: ... - # def __mul__(self, value: Self) -> Self: ... + def __mul__(self, value: Any, /) -> Self: ... - # def __invert__(self) -> Self: ... + def __invert__(self) -> Self: ... From 27e748f45da59852b60194e2dad2162285667746 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 09:14:01 +0100 Subject: [PATCH 20/30] removed leftover comment --- narwhals/_sql/typing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index c243792db1..03048027df 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -20,7 +20,6 @@ class NativeSQLExpr(NativeExpr, Protocol): - # both Self because we're comparing an expression with an expression? def __gt__(self, value: Any, /) -> Self: ... def __lt__(self, value: Any, /) -> Self: ... From 495fb11dc32d3806d2a3cb70522f08149ca8198c Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 10:17:34 +0100 Subject: [PATCH 21/30] wip: trying to move operators into NativeExpr --- narwhals/_compliant/expr.py | 24 +++++ narwhals/_sql/dataframe.py | 9 +- narwhals/_sql/expr.py | 179 ++++++++++++++++++------------------ narwhals/_sql/typing.py | 30 +++--- 4 files changed, 133 insertions(+), 109 deletions(-) diff --git a/narwhals/_compliant/expr.py b/narwhals/_compliant/expr.py index 3e18663fe4..56932b6f87 100644 --- a/narwhals/_compliant/expr.py +++ b/narwhals/_compliant/expr.py @@ -64,6 +64,30 @@ class NativeExpr(Protocol): def between(self, *args: Any, **kwds: Any) -> Any: ... def isin(self, *args: Any, **kwds: Any) -> Any: ... + # trying to move operator functions from _sql/typing: + + def __gt__(self, value: Any, /) -> Self: ... + + def __lt__(self, value: Any, /) -> Self: ... + + def __ge__(self, value: Any, /) -> Self: ... + + def __le__(self, value: Any, /) -> Self: ... + + def __eq__(self, value: Any, /) -> Self: ... + + def __ne__(self, value: Any, /) -> Self: ... + + def __add__(self, value: Any, /) -> Self: ... + + def __sub__(self, value: Any, /) -> Self: ... + + def __truediv__(self, value: Any, /) -> Self: ... + + def __mul__(self, value: Any, /) -> Self: ... + + def __invert__(self) -> Self: ... + class CompliantExpr(Protocol[CompliantFrameT, CompliantSeriesOrNativeExprT_co]): _implementation: Implementation diff --git a/narwhals/_sql/dataframe.py b/narwhals/_sql/dataframe.py index 7e18df50a8..540ed7d0d8 100644 --- a/narwhals/_sql/dataframe.py +++ b/narwhals/_sql/dataframe.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Protocol from narwhals._compliant.dataframe import CompliantLazyFrame -from narwhals._compliant.typing import CompliantExprT_contra, NativeFrameT +from narwhals._compliant.typing import CompliantExprT_contra, NativeExprT, NativeFrameT from narwhals._translate import ToNarwhalsT_co from narwhals._utils import check_columns_exist @@ -14,7 +14,6 @@ from narwhals._compliant.window import WindowInputs from narwhals._sql.expr import SQLExpr - from narwhals._sql.typing import NativeSQLExprT from narwhals.exceptions import ColumnNotFoundError Incomplete: TypeAlias = Any @@ -26,10 +25,10 @@ class SQLLazyFrame( ): def _evaluate_window_expr( self, - expr: SQLExpr[Self, NativeSQLExprT], + expr: SQLExpr[Self, NativeExprT], /, - window_inputs: WindowInputs[NativeSQLExprT], - ) -> NativeSQLExprT: + window_inputs: WindowInputs[NativeExprT], + ) -> NativeExprT: result = expr.window_function(self, window_inputs) assert len(result) == 1 # debug assertion # noqa: S101 return result[0] diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index e7553e5ca7..11565bca26 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -9,7 +9,7 @@ combine_alias_output_names, combine_evaluate_output_names, ) -from narwhals._sql.typing import NativeSQLExprT, SQLLazyFrameT +from narwhals._sql.typing import SQLLazyFrameT from narwhals._utils import Implementation, Version, not_implemented if TYPE_CHECKING: @@ -21,6 +21,7 @@ AliasNames, EvalNames, EvalSeries, + NativeExprT, WindowFunction, ) from narwhals._expression_parsing import ExprMetadata @@ -29,20 +30,20 @@ class SQLExpr( - LazyExpr[SQLLazyFrameT, NativeSQLExprT], Protocol[SQLLazyFrameT, NativeSQLExprT] + LazyExpr[SQLLazyFrameT, NativeExprT], Protocol[SQLLazyFrameT, NativeExprT] ): - _call: EvalSeries[SQLLazyFrameT, NativeSQLExprT] + _call: EvalSeries[SQLLazyFrameT, NativeExprT] _evaluate_output_names: EvalNames[SQLLazyFrameT] _alias_output_names: AliasNames | None _version: Version _implementation: Implementation _metadata: ExprMetadata | None - _window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] | None + _window_function: WindowFunction[SQLLazyFrameT, NativeExprT] | None def __init__( self, - call: EvalSeries[SQLLazyFrameT, NativeSQLExprT], - window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] | None = None, + call: EvalSeries[SQLLazyFrameT, NativeExprT], + window_function: WindowFunction[SQLLazyFrameT, NativeExprT] | None = None, *, evaluate_output_names: EvalNames[SQLLazyFrameT], alias_output_names: AliasNames | None, @@ -50,17 +51,17 @@ def __init__( implementation: Implementation = Implementation.DUCKDB, ) -> None: ... - def __call__(self, df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: + def __call__(self, df: SQLLazyFrameT) -> Sequence[NativeExprT]: return self._call(df) def __narwhals_namespace__( self, - ) -> SQLNamespace[SQLLazyFrameT, Self, Any, NativeSQLExprT]: ... + ) -> SQLNamespace[SQLLazyFrameT, Self, Any, NativeExprT]: ... def _callable_to_eval_series( - self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any - ) -> EvalSeries[SQLLazyFrameT, NativeSQLExprT]: - def func(df: SQLLazyFrameT) -> list[NativeSQLExprT]: + self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any + ) -> EvalSeries[SQLLazyFrameT, NativeExprT]: + def func(df: SQLLazyFrameT) -> list[NativeExprT]: native_series_list = self(df) other_native_series = { key: df._evaluate_expr(value) @@ -76,11 +77,11 @@ def func(df: SQLLazyFrameT) -> list[NativeSQLExprT]: return func def _push_down_window_function( - self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any - ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: + self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any + ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: def window_f( - df: SQLLazyFrameT, window_inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, window_inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: # If a function `f` is elementwise, and `g` is another function, then # - `f(g) over (window)` # - `f(g over (window)) @@ -101,7 +102,7 @@ def window_f( return window_f def _with_window_function( - self, window_function: WindowFunction[SQLLazyFrameT, NativeSQLExprT] + self, window_function: WindowFunction[SQLLazyFrameT, NativeExprT] ) -> Self: return self.__class__( self._call, @@ -113,7 +114,7 @@ def _with_window_function( ) def _with_callable( - self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any + self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any ) -> Self: return self.__class__( self._callable_to_eval_series(call, **expressifiable_args), @@ -124,7 +125,7 @@ def _with_callable( ) def _with_elementwise( - self, call: Callable[..., NativeSQLExprT], /, **expressifiable_args: Self | Any + self, call: Callable[..., NativeExprT], /, **expressifiable_args: Self | Any ) -> Self: return self.__class__( self._callable_to_eval_series(call, **expressifiable_args), @@ -135,7 +136,7 @@ def _with_elementwise( implementation=self._implementation, ) - def _with_binary(self, op: Callable[..., NativeSQLExprT], other: Self | Any) -> Self: + def _with_binary(self, op: Callable[..., NativeExprT], other: Self | Any) -> Self: return self.__class__( self._callable_to_eval_series(op, other=other), self._push_down_window_function(op, other=other), @@ -164,10 +165,10 @@ def _with_alias_output_names(self, func: AliasNames | None, /) -> Self: ) @property - def window_function(self) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: + def window_function(self) -> WindowFunction[SQLLazyFrameT, NativeExprT]: def default_window_func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: assert not inputs.order_by # noqa: S101 return [ self._window_expression(expr, inputs.partition_by, inputs.order_by) @@ -177,47 +178,47 @@ def default_window_func( return self._window_function or default_window_func def _function( - self, name: str, *args: NativeSQLExprT | PythonLiteral - ) -> NativeSQLExprT: + self, name: str, *args: NativeExprT | PythonLiteral + ) -> NativeExprT: return self.__narwhals_namespace__()._function(name, *args) - def _lit(self, value: Any) -> NativeSQLExprT: + def _lit(self, value: Any) -> NativeExprT: return self.__narwhals_namespace__()._lit(value) - def _coalesce(self, *expr: NativeSQLExprT) -> NativeSQLExprT: + def _coalesce(self, *expr: NativeExprT) -> NativeExprT: return self.__narwhals_namespace__()._coalesce(*expr) - def _count_star(self) -> NativeSQLExprT: ... + def _count_star(self) -> NativeExprT: ... def _when( self, - condition: NativeSQLExprT, - value: NativeSQLExprT, - otherwise: NativeSQLExprT | None = None, - ) -> NativeSQLExprT: + condition: NativeExprT, + value: NativeExprT, + otherwise: NativeExprT | None = None, + ) -> NativeExprT: return self.__narwhals_namespace__()._when(condition, value, otherwise) def _window_expression( self, - expr: NativeSQLExprT, - partition_by: Sequence[str | NativeSQLExprT] = (), - order_by: Sequence[str | NativeSQLExprT] = (), + expr: NativeExprT, + partition_by: Sequence[str | NativeExprT] = (), + order_by: Sequence[str | NativeExprT] = (), rows_start: int | None = None, rows_end: int | None = None, *, descending: Sequence[bool] | None = None, nulls_last: Sequence[bool] | None = None, - ) -> NativeSQLExprT: ... + ) -> NativeExprT: ... def _cum_window_func( self, func_name: Literal["sum", "max", "min", "count", "product"], *, reverse: bool, - ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: + ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ self._window_expression( self._function(func_name, expr), @@ -240,7 +241,7 @@ def _rolling_window_func( ddof: int | None = None, *, center: bool, - ) -> WindowFunction[SQLLazyFrameT, NativeSQLExprT]: + ) -> WindowFunction[SQLLazyFrameT, NativeExprT]: supported_funcs = ["sum", "mean", "std", "var"] if center: half = (window_size - 1) // 2 @@ -252,8 +253,8 @@ def _rolling_window_func( end = 0 def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: if func_name in {"sum", "mean"}: func_: str = func_name elif func_name == "var" and ddof == 0: @@ -298,19 +299,19 @@ def _backend_version(self) -> tuple[int, ...]: return self._implementation._backend_version() @classmethod - def _alias_native(cls, expr: NativeSQLExprT, name: str, /) -> NativeSQLExprT: ... + def _alias_native(cls, expr: NativeExprT, name: str, /) -> NativeExprT: ... @classmethod def _from_elementwise_horizontal_op( - cls, func: Callable[[Iterable[NativeSQLExprT]], NativeSQLExprT], *exprs: Self + cls, func: Callable[[Iterable[NativeExprT]], NativeExprT], *exprs: Self ) -> Self: - def call(df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: + def call(df: SQLLazyFrameT) -> Sequence[NativeExprT]: cols = (col for _expr in exprs for col in _expr(df)) return [func(cols)] def window_function( - df: SQLLazyFrameT, window_inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, window_inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: cols = ( col for _expr in exprs for col in _expr.window_function(df, window_inputs) ) @@ -391,12 +392,12 @@ def __or__(self, other: Self) -> Self: # Aggregations def all(self) -> Self: - def f(expr: NativeSQLExprT) -> NativeSQLExprT: + def f(expr: NativeExprT) -> NativeExprT: return self._coalesce(self._function("bool_and", expr), self._lit(True)) # noqa: FBT003 def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ self._coalesce( self._window_expression( @@ -410,12 +411,12 @@ def window_f( return self._with_callable(f)._with_window_function(window_f) def any(self) -> Self: - def f(expr: NativeSQLExprT) -> NativeSQLExprT: + def f(expr: NativeExprT) -> NativeExprT: return self._coalesce(self._function("bool_or", expr), self._lit(False)) # noqa: FBT003 def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ self._coalesce( self._window_expression( @@ -444,12 +445,12 @@ def count(self) -> Self: return self._with_callable(lambda expr: self._function("count", expr)) def sum(self) -> Self: - def f(expr: NativeSQLExprT) -> NativeSQLExprT: + def f(expr: NativeExprT) -> NativeExprT: return self._coalesce(self._function("sum", expr), self._lit(0)) def window_f( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ self._coalesce( self._window_expression( @@ -471,15 +472,15 @@ def clip( lower_bound: Self | NumericLiteral | TemporalLiteral | None, upper_bound: Self | NumericLiteral | TemporalLiteral | None, ) -> Self: - def _clip_lower(expr: NativeSQLExprT, lower_bound: Any) -> NativeSQLExprT: + def _clip_lower(expr: NativeExprT, lower_bound: Any) -> NativeExprT: return self._function("greatest", expr, lower_bound) - def _clip_upper(expr: NativeSQLExprT, upper_bound: Any) -> NativeSQLExprT: + def _clip_upper(expr: NativeExprT, upper_bound: Any) -> NativeExprT: return self._function("least", expr, upper_bound) def _clip_both( - expr: NativeSQLExprT, lower_bound: Any, upper_bound: Any - ) -> NativeSQLExprT: + expr: NativeExprT, lower_bound: Any, upper_bound: Any + ) -> NativeExprT: return self._function( "greatest", self._function("least", expr, upper_bound), lower_bound ) @@ -500,9 +501,9 @@ def round(self, decimals: int) -> Self: lambda expr: self._function("round", expr, self._lit(decimals)) ) - # WIP: trying new NativeSQLExprT + # WIP: trying new NativeExprT def sqrt(self) -> Self: - def _sqrt(expr: NativeSQLExprT) -> NativeSQLExprT: + def _sqrt(expr: NativeExprT) -> NativeExprT: return self._when( expr < self._lit(0), self._lit(float("nan")), self._function("sqrt", expr) ) @@ -513,12 +514,12 @@ def exp(self) -> Self: return self._with_elementwise(lambda expr: self._function("exp", expr)) def log(self, base: float) -> Self: - def _log(expr: NativeSQLExprT) -> NativeSQLExprT: + def _log(expr: NativeExprT) -> NativeExprT: return self._when( expr < self._lit(0), self._lit(float("nan")), self._when( - cast("NativeSQLExprT", expr == self._lit(0)), + cast("NativeExprT", expr == self._lit(0)), self._lit(float("-inf")), self._function("log", expr) / self._function("log", self._lit(base)), ), @@ -576,8 +577,8 @@ def rolling_std( # Other window functions def diff(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ expr - self._window_expression( @@ -590,8 +591,8 @@ def func( def shift(self, n: int) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ self._window_expression( self._function("lag", expr, n), inputs.partition_by, inputs.order_by @@ -603,12 +604,12 @@ def func( def is_first_distinct(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: # pyright checkers think the return type is `list[bool]` because of `==` return [ cast( - "NativeSQLExprT", + "NativeExprT", self._window_expression( self._function("row_number"), (*inputs.partition_by, expr), @@ -623,11 +624,11 @@ def func( def is_last_distinct(self) -> Self: def func( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: return [ cast( - "NativeSQLExprT", + "NativeExprT", self._window_expression( self._function("row_number"), (*inputs.partition_by, expr), @@ -651,13 +652,13 @@ def rank(self, method: RankMethod, *, descending: bool) -> Self: func = self._function("row_number") def _rank( - expr: NativeSQLExprT, - partition_by: Sequence[str | NativeSQLExprT] = (), - order_by: Sequence[str | NativeSQLExprT] = (), + expr: NativeExprT, + partition_by: Sequence[str | NativeExprT] = (), + order_by: Sequence[str | NativeExprT] = (), *, descending: Sequence[bool], nulls_last: Sequence[bool], - ) -> NativeSQLExprT: + ) -> NativeExprT: count_expr = self._count_star() window_kwargs: dict[str, Any] = { "partition_by": partition_by, @@ -679,15 +680,15 @@ def _rank( ) / self._lit(2.0) else: rank_expr = self._window_expression(func, **window_kwargs) - # TODO: @mp, thought I added this to NativeSQLExprT but not working? + # TODO: @mp, thought I added this to NativeExprT but not working? return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] - def _unpartitioned_rank(expr: NativeSQLExprT) -> NativeSQLExprT: + def _unpartitioned_rank(expr: NativeExprT) -> NativeExprT: return _rank(expr, descending=[descending], nulls_last=[True]) def _partitioned_rank( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: # node: when `descending` / `nulls_last` are supported in `.over`, they should be respected here # https://github.com/narwhals-dev/narwhals/issues/2790 return [ @@ -707,20 +708,20 @@ def _partitioned_rank( def is_unique(self) -> Self: def _is_unique( - expr: NativeSQLExprT, *partition_by: str | NativeSQLExprT - ) -> NativeSQLExprT: + expr: NativeExprT, *partition_by: str | NativeExprT + ) -> NativeExprT: return cast( - "NativeSQLExprT", + "NativeExprT", self._window_expression(self._count_star(), (expr, *partition_by)) == self._lit(1), ) - def _unpartitioned_is_unique(expr: NativeSQLExprT) -> NativeSQLExprT: + def _unpartitioned_is_unique(expr: NativeExprT) -> NativeExprT: return _is_unique(expr) def _partitioned_is_unique( - df: SQLLazyFrameT, inputs: WindowInputs[NativeSQLExprT] - ) -> Sequence[NativeSQLExprT]: + df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] + ) -> Sequence[NativeExprT]: assert not inputs.order_by # noqa: S101 return [_is_unique(expr, *inputs.partition_by) for expr in self(df)] @@ -730,9 +731,9 @@ def _partitioned_is_unique( # Other def over( - self, partition_by: Sequence[str | NativeSQLExprT], order_by: Sequence[str] + self, partition_by: Sequence[str | NativeExprT], order_by: Sequence[str] ) -> Self: - def func(df: SQLLazyFrameT) -> Sequence[NativeSQLExprT]: + def func(df: SQLLazyFrameT) -> Sequence[NativeExprT]: return self.window_function(df, WindowInputs(partition_by, order_by)) return self.__class__( diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 03048027df..8fb28d2488 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -1,8 +1,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Protocol, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar -from narwhals._compliant.expr import NativeExpr +#from narwhals._compliant.expr import NativeExpr if TYPE_CHECKING: from typing_extensions import Self @@ -16,28 +16,28 @@ SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") +# NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") -class NativeSQLExpr(NativeExpr, Protocol): - def __gt__(self, value: Any, /) -> Self: ... +# class NativeSQLExpr(NativeExpr, Protocol): +# def __gt__(self, value: Any, /) -> Self: ... - def __lt__(self, value: Any, /) -> Self: ... +# def __lt__(self, value: Any, /) -> Self: ... - def __ge__(self, value: Any, /) -> Self: ... +# def __ge__(self, value: Any, /) -> Self: ... - def __le__(self, value: Any, /) -> Self: ... +# def __le__(self, value: Any, /) -> Self: ... - def __eq__(self, value: Any, /) -> Self: ... +# def __eq__(self, value: Any, /) -> Self: ... - def __ne__(self, value: Any, /) -> Self: ... +# def __ne__(self, value: Any, /) -> Self: ... - def __add__(self, value: Any, /) -> Self: ... +# def __add__(self, value: Any, /) -> Self: ... - def __sub__(self, value: Any, /) -> Self: ... +# def __sub__(self, value: Any, /) -> Self: ... - def __truediv__(self, value: Any, /) -> Self: ... +# def __truediv__(self, value: Any, /) -> Self: ... - def __mul__(self, value: Any, /) -> Self: ... +# def __mul__(self, value: Any, /) -> Self: ... - def __invert__(self) -> Self: ... +# def __invert__(self) -> Self: ... From 9383492ef391b7cbe1510c9411969f9b63e2e6f6 Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 10:38:51 +0100 Subject: [PATCH 22/30] move to NativeExpr works --- narwhals/_sql/expr.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 11565bca26..a94a570e39 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -3,7 +3,14 @@ from typing import TYPE_CHECKING, Any, Callable, Literal, Protocol, cast from narwhals._compliant.expr import LazyExpr -from narwhals._compliant.typing import AliasNames, WindowFunction +from narwhals._compliant.typing import ( + AliasNames, + EvalNames, + WindowFunction, + EvalSeries, + NativeExprT, + WindowFunction + ) from narwhals._compliant.window import WindowInputs from narwhals._expression_parsing import ( combine_alias_output_names, @@ -17,13 +24,7 @@ from typing_extensions import Self, TypeIs - from narwhals._compliant.typing import ( - AliasNames, - EvalNames, - EvalSeries, - NativeExprT, - WindowFunction, - ) + from narwhals._compliant.typing import AliasNames, WindowFunction from narwhals._expression_parsing import ExprMetadata from narwhals._sql.namespace import SQLNamespace from narwhals.typing import NumericLiteral, PythonLiteral, RankMethod, TemporalLiteral From df8274472f96f78d266b18cdf902ab1d00fb285c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:40:21 +0000 Subject: [PATCH 23/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- narwhals/_sql/expr.py | 17 ++++++----------- narwhals/_sql/typing.py | 3 +-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index a94a570e39..2001754f66 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -6,11 +6,10 @@ from narwhals._compliant.typing import ( AliasNames, EvalNames, - WindowFunction, - EvalSeries, - NativeExprT, - WindowFunction - ) + EvalSeries, + NativeExprT, + WindowFunction, +) from narwhals._compliant.window import WindowInputs from narwhals._expression_parsing import ( combine_alias_output_names, @@ -30,9 +29,7 @@ from narwhals.typing import NumericLiteral, PythonLiteral, RankMethod, TemporalLiteral -class SQLExpr( - LazyExpr[SQLLazyFrameT, NativeExprT], Protocol[SQLLazyFrameT, NativeExprT] -): +class SQLExpr(LazyExpr[SQLLazyFrameT, NativeExprT], Protocol[SQLLazyFrameT, NativeExprT]): _call: EvalSeries[SQLLazyFrameT, NativeExprT] _evaluate_output_names: EvalNames[SQLLazyFrameT] _alias_output_names: AliasNames | None @@ -178,9 +175,7 @@ def default_window_func( return self._window_function or default_window_func - def _function( - self, name: str, *args: NativeExprT | PythonLiteral - ) -> NativeExprT: + def _function(self, name: str, *args: NativeExprT | PythonLiteral) -> NativeExprT: return self.__narwhals_namespace__()._function(name, *args) def _lit(self, value: Any) -> NativeExprT: diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index 8fb28d2488..c19a4bb144 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -2,10 +2,9 @@ from typing import TYPE_CHECKING, Any, TypeVar -#from narwhals._compliant.expr import NativeExpr +# from narwhals._compliant.expr import NativeExpr if TYPE_CHECKING: - from typing_extensions import Self from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr From dd39727917577f948fc38112f6d0ec8d8ff37848 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:43:15 +0000 Subject: [PATCH 24/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- narwhals/_sql/typing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index c19a4bb144..ae681ef7ef 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -5,7 +5,6 @@ # from narwhals._compliant.expr import NativeExpr if TYPE_CHECKING: - from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr From 09731daedd8462ade88fb66b2bf605c1a5a347fd Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 10:55:46 +0100 Subject: [PATCH 25/30] removing commented out --- narwhals/_sql/expr.py | 1 - narwhals/_sql/typing.py | 27 --------------------------- 2 files changed, 28 deletions(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 2001754f66..08a60be2e0 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -676,7 +676,6 @@ def _rank( ) / self._lit(2.0) else: rank_expr = self._window_expression(func, **window_kwargs) - # TODO: @mp, thought I added this to NativeExprT but not working? return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] def _unpartitioned_rank(expr: NativeExprT) -> NativeExprT: diff --git a/narwhals/_sql/typing.py b/narwhals/_sql/typing.py index ae681ef7ef..c6634af6f3 100644 --- a/narwhals/_sql/typing.py +++ b/narwhals/_sql/typing.py @@ -2,8 +2,6 @@ from typing import TYPE_CHECKING, Any, TypeVar -# from narwhals._compliant.expr import NativeExpr - if TYPE_CHECKING: from narwhals._sql.dataframe import SQLLazyFrame from narwhals._sql.expr import SQLExpr @@ -14,28 +12,3 @@ SQLExprT = TypeVar("SQLExprT", bound="SQLExprAny") SQLExprT_contra = TypeVar("SQLExprT_contra", bound="SQLExprAny", contravariant=True) SQLLazyFrameT = TypeVar("SQLLazyFrameT", bound="SQLLazyFrameAny") -# NativeSQLExprT = TypeVar("NativeSQLExprT", bound="NativeSQLExpr") - - -# class NativeSQLExpr(NativeExpr, Protocol): -# def __gt__(self, value: Any, /) -> Self: ... - -# def __lt__(self, value: Any, /) -> Self: ... - -# def __ge__(self, value: Any, /) -> Self: ... - -# def __le__(self, value: Any, /) -> Self: ... - -# def __eq__(self, value: Any, /) -> Self: ... - -# def __ne__(self, value: Any, /) -> Self: ... - -# def __add__(self, value: Any, /) -> Self: ... - -# def __sub__(self, value: Any, /) -> Self: ... - -# def __truediv__(self, value: Any, /) -> Self: ... - -# def __mul__(self, value: Any, /) -> Self: ... - -# def __invert__(self) -> Self: ... From b4b48d96c20bd2fb69797dcb25f5a3338b24756e Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 11:01:18 +0100 Subject: [PATCH 26/30] trying to silence ibis type errors --- narwhals/_ibis/expr.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/narwhals/_ibis/expr.py b/narwhals/_ibis/expr.py index 682831d415..f148eacb93 100644 --- a/narwhals/_ibis/expr.py +++ b/narwhals/_ibis/expr.py @@ -34,8 +34,8 @@ from narwhals.typing import IntoDType, RankMethod, RollingInterpolationMethod ExprT = TypeVar("ExprT", bound=ir.Value) - IbisWindowFunction = WindowFunction[IbisLazyFrame, ir.Value] - IbisWindowInputs = WindowInputs[ir.Value] + IbisWindowFunction = WindowFunction[IbisLazyFrame, ir.Value] # pyright: ignore[reportInvalidTypeArguments] + IbisWindowInputs = WindowInputs[ir.Value] # pyright: ignore[reportInvalidTypeArguments] class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # pyright: ignore[reportInvalidTypeArguments] @@ -43,7 +43,7 @@ class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # pyright: ignore[reportI def __init__( self, - call: EvalSeries[IbisLazyFrame, ir.Value], + call: EvalSeries[IbisLazyFrame, ir.Value], # pyright: ignore[reportInvalidTypeArguments] window_function: IbisWindowFunction | None = None, *, evaluate_output_names: EvalNames[IbisLazyFrame], From a131d518cc87a6515bd41ee9397f4543bf52bcbe Mon Sep 17 00:00:00 2001 From: ym-pett Date: Tue, 5 Aug 2025 11:05:21 +0100 Subject: [PATCH 27/30] annoying stray comment --- narwhals/_compliant/expr.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/narwhals/_compliant/expr.py b/narwhals/_compliant/expr.py index 56932b6f87..18bda37cb1 100644 --- a/narwhals/_compliant/expr.py +++ b/narwhals/_compliant/expr.py @@ -64,8 +64,6 @@ class NativeExpr(Protocol): def between(self, *args: Any, **kwds: Any) -> Any: ... def isin(self, *args: Any, **kwds: Any) -> Any: ... - # trying to move operator functions from _sql/typing: - def __gt__(self, value: Any, /) -> Self: ... def __lt__(self, value: Any, /) -> Self: ... From dcf1b800dd514808555f81de3861b5d5a4cc4205 Mon Sep 17 00:00:00 2001 From: dangotbanned <125183946+dangotbanned@users.noreply.github.com> Date: Thu, 7 Aug 2025 16:26:11 +0000 Subject: [PATCH 28/30] fix(suggestion): Remove problematic protocol members Replaces them with calls to `operator` functions, which use `Any` --- narwhals/_compliant/expr.py | 23 +++-------- narwhals/_dask/expr.py | 6 +-- narwhals/_dask/namespace.py | 4 +- narwhals/_dask/selectors.py | 2 +- narwhals/_ibis/expr.py | 8 ++-- narwhals/_sql/expr.py | 81 +++++++++++++++++++------------------ 6 files changed, 57 insertions(+), 67 deletions(-) diff --git a/narwhals/_compliant/expr.py b/narwhals/_compliant/expr.py index 18bda37cb1..980d005d08 100644 --- a/narwhals/_compliant/expr.py +++ b/narwhals/_compliant/expr.py @@ -64,27 +64,16 @@ class NativeExpr(Protocol): def between(self, *args: Any, **kwds: Any) -> Any: ... def isin(self, *args: Any, **kwds: Any) -> Any: ... + # NOTE: None of these are annotated for `dx.Series`, but are added imperatively + # Probably better to define a sub-protocol for `NativeSQLExpr` + # - match `dx.Series` to `NativeExpr` + # - match the others to `NativeSQLExpr` def __gt__(self, value: Any, /) -> Self: ... - def __lt__(self, value: Any, /) -> Self: ... - def __ge__(self, value: Any, /) -> Self: ... - def __le__(self, value: Any, /) -> Self: ... - - def __eq__(self, value: Any, /) -> Self: ... - - def __ne__(self, value: Any, /) -> Self: ... - - def __add__(self, value: Any, /) -> Self: ... - - def __sub__(self, value: Any, /) -> Self: ... - - def __truediv__(self, value: Any, /) -> Self: ... - - def __mul__(self, value: Any, /) -> Self: ... - - def __invert__(self) -> Self: ... + def __eq__(self, value: Any, /) -> Self: ... # type: ignore[override] + def __ne__(self, value: Any, /) -> Self: ... # type: ignore[override] class CompliantExpr(Protocol[CompliantFrameT, CompliantSeriesOrNativeExprT_co]): diff --git a/narwhals/_dask/expr.py b/narwhals/_dask/expr.py index 5f6952ba1a..a4f5e54820 100644 --- a/narwhals/_dask/expr.py +++ b/narwhals/_dask/expr.py @@ -42,14 +42,14 @@ class DaskExpr( - LazyExpr["DaskLazyFrame", "dx.Series"], - DepthTrackingExpr["DaskLazyFrame", "dx.Series"], + LazyExpr["DaskLazyFrame", "dx.Series"], # pyright: ignore[reportInvalidTypeArguments] + DepthTrackingExpr["DaskLazyFrame", "dx.Series"], # pyright: ignore[reportInvalidTypeArguments] ): _implementation: Implementation = Implementation.DASK def __init__( self, - call: EvalSeries[DaskLazyFrame, dx.Series], + call: EvalSeries[DaskLazyFrame, dx.Series], # pyright: ignore[reportInvalidTypeForm] *, depth: int, function_name: str, diff --git a/narwhals/_dask/namespace.py b/narwhals/_dask/namespace.py index 0e08101253..518ab36b7e 100644 --- a/narwhals/_dask/namespace.py +++ b/narwhals/_dask/namespace.py @@ -304,7 +304,7 @@ def func(df: DaskLazyFrame) -> list[dx.Series]: ) -class DaskWhen(CompliantWhen[DaskLazyFrame, "dx.Series", DaskExpr]): +class DaskWhen(CompliantWhen[DaskLazyFrame, "dx.Series", DaskExpr]): # pyright: ignore[reportInvalidTypeArguments] @property def _then(self) -> type[DaskThen]: return DaskThen @@ -344,7 +344,7 @@ def __call__(self, df: DaskLazyFrame) -> Sequence[dx.Series]: return [then_series.where(condition, otherwise_series)] # pyright: ignore[reportArgumentType] -class DaskThen(CompliantThen[DaskLazyFrame, "dx.Series", DaskExpr, DaskWhen], DaskExpr): +class DaskThen(CompliantThen[DaskLazyFrame, "dx.Series", DaskExpr, DaskWhen], DaskExpr): # pyright: ignore[reportInvalidTypeArguments] _depth: int = 0 _scalar_kwargs: ScalarKwargs = {} # noqa: RUF012 _function_name: str = "whenthen" diff --git a/narwhals/_dask/selectors.py b/narwhals/_dask/selectors.py index 155276e06a..9fb6eeecb8 100644 --- a/narwhals/_dask/selectors.py +++ b/narwhals/_dask/selectors.py @@ -12,7 +12,7 @@ from narwhals._dask.dataframe import DaskLazyFrame # noqa: F401 -class DaskSelectorNamespace(LazySelectorNamespace["DaskLazyFrame", "dx.Series"]): +class DaskSelectorNamespace(LazySelectorNamespace["DaskLazyFrame", "dx.Series"]): # pyright: ignore[reportInvalidTypeArguments] @property def _selector(self) -> type[DaskSelector]: return DaskSelector diff --git a/narwhals/_ibis/expr.py b/narwhals/_ibis/expr.py index f148eacb93..cf970a3d86 100644 --- a/narwhals/_ibis/expr.py +++ b/narwhals/_ibis/expr.py @@ -34,16 +34,16 @@ from narwhals.typing import IntoDType, RankMethod, RollingInterpolationMethod ExprT = TypeVar("ExprT", bound=ir.Value) - IbisWindowFunction = WindowFunction[IbisLazyFrame, ir.Value] # pyright: ignore[reportInvalidTypeArguments] - IbisWindowInputs = WindowInputs[ir.Value] # pyright: ignore[reportInvalidTypeArguments] + IbisWindowFunction = WindowFunction[IbisLazyFrame, ir.Value] + IbisWindowInputs = WindowInputs[ir.Value] -class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): # pyright: ignore[reportInvalidTypeArguments] +class IbisExpr(SQLExpr["IbisLazyFrame", "ir.Value"]): _implementation = Implementation.IBIS def __init__( self, - call: EvalSeries[IbisLazyFrame, ir.Value], # pyright: ignore[reportInvalidTypeArguments] + call: EvalSeries[IbisLazyFrame, ir.Value], window_function: IbisWindowFunction | None = None, *, evaluate_output_names: EvalNames[IbisLazyFrame], diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 08a60be2e0..f05eed74d3 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -1,6 +1,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, Protocol, cast +# ruff: noqa: N806 +import operator as op +from typing import TYPE_CHECKING, Any, Callable, Literal, Protocol from narwhals._compliant.expr import LazyExpr from narwhals._compliant.typing import ( @@ -511,13 +513,14 @@ def exp(self) -> Self: def log(self, base: float) -> Self: def _log(expr: NativeExprT) -> NativeExprT: + F = self._function return self._when( expr < self._lit(0), self._lit(float("nan")), self._when( - cast("NativeExprT", expr == self._lit(0)), + expr == self._lit(0), self._lit(float("-inf")), - self._function("log", expr) / self._function("log", self._lit(base)), + op.truediv(F("log", expr), F("log", self._lit(base))), ), ) @@ -575,11 +578,10 @@ def diff(self) -> Self: def func( df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] ) -> Sequence[NativeExprT]: + F = self._function + window = self._window_expression return [ - expr - - self._window_expression( - self._function("lag", expr), inputs.partition_by, inputs.order_by - ) + op.sub(expr, window(F("lag", expr), inputs.partition_by, inputs.order_by)) for expr in self(df) ] @@ -604,15 +606,12 @@ def func( ) -> Sequence[NativeExprT]: # pyright checkers think the return type is `list[bool]` because of `==` return [ - cast( - "NativeExprT", - self._window_expression( - self._function("row_number"), - (*inputs.partition_by, expr), - inputs.order_by, - ) - == self._lit(1), + self._window_expression( + self._function("row_number"), + (*inputs.partition_by, expr), + inputs.order_by, ) + == self._lit(1) for expr in self(df) ] @@ -623,17 +622,14 @@ def func( df: SQLLazyFrameT, inputs: WindowInputs[NativeExprT] ) -> Sequence[NativeExprT]: return [ - cast( - "NativeExprT", - self._window_expression( - self._function("row_number"), - (*inputs.partition_by, expr), - inputs.order_by, - descending=[True] * len(inputs.order_by), - nulls_last=[True] * len(inputs.order_by), - ) - == self._lit(1), + self._window_expression( + self._function("row_number"), + (*inputs.partition_by, expr), + inputs.order_by, + descending=[True] * len(inputs.order_by), + nulls_last=[True] * len(inputs.order_by), ) + == self._lit(1) for expr in self(df) ] @@ -663,20 +659,27 @@ def _rank( "nulls_last": nulls_last, } count_window_kwargs: dict[str, Any] = {"partition_by": (*partition_by, expr)} + window = self._window_expression + F = self._function if method == "max": - rank_expr = ( - self._window_expression(func, **window_kwargs) - + self._window_expression(count_expr, **count_window_kwargs) - - self._lit(1) + rank_expr = op.sub( + op.add( + window(func, **window_kwargs), + window(count_expr, **count_window_kwargs), + ), + self._lit(1), ) elif method == "average": - rank_expr = self._window_expression(func, **window_kwargs) + ( - self._window_expression(count_expr, **count_window_kwargs) - - self._lit(1) - ) / self._lit(2.0) + rank_expr = op.add( + window(func, **window_kwargs), + op.truediv( + op.sub(window(count_expr, **count_window_kwargs), self._lit(1)), + self._lit(2.0), + ), + ) else: - rank_expr = self._window_expression(func, **window_kwargs) - return self._when(~self._function("isnull", expr), rank_expr) # type: ignore[operator] + rank_expr = window(func, **window_kwargs) + return self._when(~F("isnull", expr), rank_expr) # type: ignore[operator] def _unpartitioned_rank(expr: NativeExprT) -> NativeExprT: return _rank(expr, descending=[descending], nulls_last=[True]) @@ -705,11 +708,9 @@ def is_unique(self) -> Self: def _is_unique( expr: NativeExprT, *partition_by: str | NativeExprT ) -> NativeExprT: - return cast( - "NativeExprT", - self._window_expression(self._count_star(), (expr, *partition_by)) - == self._lit(1), - ) + return self._window_expression( + self._count_star(), (expr, *partition_by) + ) == self._lit(1) def _unpartitioned_is_unique(expr: NativeExprT) -> NativeExprT: return _is_unique(expr) From e6f76409e7a2aac1eeb4567b1962395371247f4c Mon Sep 17 00:00:00 2001 From: ym-pett Date: Fri, 8 Aug 2025 10:12:57 +0100 Subject: [PATCH 29/30] took out wip comment --- narwhals/_sql/expr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index f05eed74d3..07549e6706 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -499,7 +499,6 @@ def round(self, decimals: int) -> Self: lambda expr: self._function("round", expr, self._lit(decimals)) ) - # WIP: trying new NativeExprT def sqrt(self) -> Self: def _sqrt(expr: NativeExprT) -> NativeExprT: return self._when( From 462cf8ad8f67403db41fc1cd8b6919bd2504334a Mon Sep 17 00:00:00 2001 From: Marco Edward Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:20:31 +0100 Subject: [PATCH 30/30] remove `isin` --- narwhals/_compliant/expr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/narwhals/_compliant/expr.py b/narwhals/_compliant/expr.py index 358169a374..fe5bdd777b 100644 --- a/narwhals/_compliant/expr.py +++ b/narwhals/_compliant/expr.py @@ -62,7 +62,6 @@ class NativeExpr(Protocol): """ def between(self, *args: Any, **kwds: Any) -> Any: ... - def isin(self, *args: Any, **kwds: Any) -> Any: ... # NOTE: None of these are annotated for `dx.Series`, but are added imperatively # Probably better to define a sub-protocol for `NativeSQLExpr`