diff --git a/narwhals/_arrow/selectors.py b/narwhals/_arrow/selectors.py index 48e837ec7..36feb5d56 100644 --- a/narwhals/_arrow/selectors.py +++ b/narwhals/_arrow/selectors.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING from typing import Any -from typing import NoReturn from typing import Sequence from narwhals._arrow.expr import ArrowExpr @@ -178,12 +177,3 @@ def __invert__(self: Self) -> ArrowSelector: ).all() - self ) - - def __rsub__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError - - def __rand__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError - - def __ror__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError diff --git a/narwhals/_dask/selectors.py b/narwhals/_dask/selectors.py index 703e24860..9e6cc6302 100644 --- a/narwhals/_dask/selectors.py +++ b/narwhals/_dask/selectors.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING from typing import Any -from typing import NoReturn from narwhals._dask.expr import DaskExpr from narwhals.utils import import_dtypes_module @@ -186,12 +185,3 @@ def __invert__(self: Self) -> DaskSelector: ).all() - self ) - - def __rsub__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError - - def __rand__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError - - def __ror__(self: Self, other: Any) -> NoReturn: - raise NotImplementedError diff --git a/narwhals/_pandas_like/selectors.py b/narwhals/_pandas_like/selectors.py index c4ddfff36..b3518283f 100644 --- a/narwhals/_pandas_like/selectors.py +++ b/narwhals/_pandas_like/selectors.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING from typing import Any -from typing import NoReturn from narwhals._pandas_like.expr import PandasLikeExpr from narwhals.utils import import_dtypes_module @@ -189,12 +188,3 @@ def __invert__(self: Self) -> PandasSelector: ).all() - self ) - - def __rsub__(self, other: Any) -> NoReturn: - raise NotImplementedError - - def __rand__(self, other: Any) -> NoReturn: - raise NotImplementedError - - def __ror__(self, other: Any) -> NoReturn: - raise NotImplementedError diff --git a/narwhals/selectors.py b/narwhals/selectors.py index e67424281..dabf6f83f 100644 --- a/narwhals/selectors.py +++ b/narwhals/selectors.py @@ -1,12 +1,39 @@ from __future__ import annotations +from typing import TYPE_CHECKING from typing import Any +from typing import NoReturn from narwhals.expr import Expr from narwhals.utils import flatten +if TYPE_CHECKING: + from typing_extensions import Self -class Selector(Expr): ... + +class Selector(Expr): + def _to_expr(self: Self) -> Expr: + return Expr( + to_compliant_expr=self._to_compliant_expr, + is_order_dependent=self._is_order_dependent, + changes_length=self._changes_length, + aggregates=self._aggregates, + ) + + def __add__(self: Self, other: Any) -> Expr: # type: ignore[override] + if isinstance(other, Selector): + msg = "unsupported operand type(s) for op: ('Selector' + 'Selector')" + raise TypeError(msg) + return self._to_expr() + other # type: ignore[no-any-return] + + def __rsub__(self: Self, other: Any) -> NoReturn: + raise NotImplementedError + + def __rand__(self: Self, other: Any) -> NoReturn: + raise NotImplementedError + + def __ror__(self: Self, other: Any) -> NoReturn: + raise NotImplementedError def by_dtype(*dtypes: Any) -> Expr: diff --git a/tests/selectors_test.py b/tests/selectors_test.py index 80aa64803..36ea15a4f 100644 --- a/tests/selectors_test.py +++ b/tests/selectors_test.py @@ -1,7 +1,7 @@ from __future__ import annotations -import pandas as pd -import pyarrow as pa +import re + import pytest import narwhals.stable.v1 as nw @@ -103,16 +103,17 @@ def test_set_ops( assert sorted(result) == expected -@pytest.mark.parametrize("invalid_constructor", [pd.DataFrame, pa.table]) -def test_set_ops_invalid( - invalid_constructor: Constructor, request: pytest.FixtureRequest -) -> None: - if "duckdb" in str(invalid_constructor): - request.applymarker(pytest.mark.xfail) - df = nw.from_native(invalid_constructor(data)) +def test_set_ops_invalid(constructor: Constructor) -> None: + df = nw.from_native(constructor(data)) with pytest.raises((NotImplementedError, ValueError)): df.select(1 - numeric()) with pytest.raises((NotImplementedError, ValueError)): df.select(1 | numeric()) with pytest.raises((NotImplementedError, ValueError)): df.select(1 & numeric()) + + with pytest.raises( + TypeError, + match=re.escape("unsupported operand type(s) for op: ('Selector' + 'Selector')"), + ): + df.select(boolean() + numeric())