Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions narwhals/_plan/_guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,8 @@ def is_into_expr_column(obj: Any) -> TypeIs[IntoExprColumn]:
return isinstance(obj, (str, _expr().Expr, _series().Series))


def is_column_name_or_selector(
obj: Any, *, allow_expr: bool = False
) -> TypeIs[ColumnNameOrSelector]:
tps = (str, _selectors().Selector) if not allow_expr else (str, _expr().Expr)
return isinstance(obj, tps)
def is_column_name_or_selector(obj: Any) -> TypeIs[ColumnNameOrSelector]:
return isinstance(obj, (str, _selectors().Selector))


def is_compliant_series(
Expand Down
11 changes: 4 additions & 7 deletions narwhals/_plan/_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
is_expr,
is_into_expr_column,
is_iterable_reject,
is_selector,
)
from narwhals._plan.common import flatten_hash_safe
from narwhals._plan.exceptions import (
Expand Down Expand Up @@ -147,14 +146,12 @@ def parse_into_selector_ir(
def _parse_into_selector(
input: ColumnNameOrSelector | Expr, /, *, require_all: bool = True
) -> Selector:
if is_selector(input):
selector = input
if is_expr(input):
selector = input.meta.as_selector()
elif isinstance(input, str):
import narwhals._plan.selectors as cs

selector = cs.by_name(input, require_all=require_all)
elif is_expr(input):
selector = input.meta.as_selector()
else:
msg = f"cannot turn {qualified_type_name(input)!r} into a selector"
raise TypeError(msg)
Expand Down Expand Up @@ -257,14 +254,14 @@ def _parse_into_iter_selector_ir(

if not _is_empty_sequence(first_input):
if _is_iterable(first_input) and not isinstance(first_input, str):
if more_inputs: # pragma: no cover
if more_inputs:
raise invalid_into_expr_error(first_input, more_inputs, {})
else:
for into in first_input: # type: ignore[var-annotated]
yield parse_into_selector_ir(into)
else:
yield parse_into_selector_ir(first_input)
for into in more_inputs: # pragma: no cover
for into in more_inputs:
yield parse_into_selector_ir(into)


Expand Down
43 changes: 42 additions & 1 deletion tests/plan/compliant_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
import pytest

from narwhals._plan import selectors as ncs
from narwhals.exceptions import ColumnNotFoundError, InvalidOperationError
from narwhals.exceptions import (
ColumnNotFoundError,
InvalidIntoExprError,
InvalidOperationError,
)

pytest.importorskip("pyarrow")
pytest.importorskip("numpy")
Expand All @@ -22,6 +26,7 @@
dataframe,
first,
last,
re_compile,
series,
)

Expand Down Expand Up @@ -549,6 +554,12 @@ def test_drop_strict() -> None:
assert df.drop(ncs.last(), "z", strict=False).collect_schema().names() == ["a"]


def test_drop_invalid(data_small_dh: Data) -> None:
df = dataframe(data_small_dh)
with pytest.raises(InvalidOperationError):
df.drop(ncs.first().first())


def test_drop_nulls(data_small_dh: Data) -> None:
df = dataframe(data_small_dh)
expected: Data = {"d": [], "e": [], "f": [], "g": [], "h": []}
Expand All @@ -575,6 +586,36 @@ def test_drop_nulls_invalid(data_small_dh: Data) -> None:
df.drop_nulls(ncs.by_index(-999))


def test_sort() -> None:
data_sort = {
"a": [8, 8, 1, None, 1, 1],
"b": [None, 0.9, 3.0, 0.9, None, None],
"c": [1, 1, 1, 2, 1, 2],
"idx": [0, 1, 2, 3, 4, 5],
}
result = dataframe(data_sort).sort(
ncs.first(), "c", ncs.float(), descending=[True, False, False], nulls_last=True
)
expected = {
"a": [8, 8, 1, 1, 1, None],
"b": [0.9, None, 3.0, None, None, 0.9],
"c": [1, 1, 1, 1, 2, 2],
"idx": [1, 0, 2, 4, 5, 3],
}
assert_equal_data(result, expected)


def test_sort_invalid(data_small_dh: Data) -> None:
df = dataframe(data_small_dh)
with pytest.raises(ColumnNotFoundError):
df.sort(())
with pytest.raises(
InvalidIntoExprError,
match=re_compile(r"passing both iterable and positional inputs is not supported"),
):
df.sort(["d"], ["e", "f"]) # type: ignore[arg-type]


DROP_ROW_1: Data = {
"d": [7, 8],
"e": [9, 7],
Expand Down
11 changes: 1 addition & 10 deletions tests/plan/frame_unique_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,7 @@ def test_unique_invalid_keep(data: Data) -> None:
(nwp.nth(5).meta.as_selector(), ColumnNotFoundError),
(["a", "b", "A"], ColumnNotFoundError),
(nwp.col("a").first(), InvalidOperationError),
pytest.param(
ncs.first().last(),
InvalidOperationError,
# TODO @dangotbanned: Fix this in another PR
# Need to be stricter on the Selector check
marks=pytest.mark.xfail(
reason="narwhals/_plan/_expansion.py:160: 'Last' object has no attribute 'iter_expand_names'",
raises=AttributeError,
),
),
(ncs.first().last(), InvalidOperationError),
],
)
def test_unique_invalid_subset(data: Data, subset: Any, err: type[Exception]) -> None:
Expand Down
Loading