Skip to content

Commit deb77c2

Browse files
committed
chore(typing): Spec out CompliantDataFrame
- Variance of `CompliantSeriesT_co` changed due to how it is used - Made a lot of the types more generic - They can be refined by the implementations - Some will handle coverting to narrower types, where that backend demands it - **Main missing part** is `CompliantExpr` - Need to spec `CompliantSeries` first - Otherwise, introduces lots of errors for `Polars*`
1 parent 209e4e6 commit deb77c2

File tree

3 files changed

+122
-15
lines changed

3 files changed

+122
-15
lines changed

narwhals/_compliant/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from narwhals._compliant.typing import CompliantExprT
2020
from narwhals._compliant.typing import CompliantFrameT
2121
from narwhals._compliant.typing import CompliantSeriesOrNativeExprT_co
22-
from narwhals._compliant.typing import CompliantSeriesT_co
22+
from narwhals._compliant.typing import CompliantSeriesT
2323
from narwhals._compliant.typing import EagerDataFrameT
2424
from narwhals._compliant.typing import EagerSeriesT
2525
from narwhals._compliant.typing import IntoCompliantExpr
@@ -35,7 +35,7 @@
3535
"CompliantSelectorNamespace",
3636
"CompliantSeries",
3737
"CompliantSeriesOrNativeExprT_co",
38-
"CompliantSeriesT_co",
38+
"CompliantSeriesT",
3939
"EagerDataFrame",
4040
"EagerDataFrameT",
4141
"EagerExpr",

narwhals/_compliant/dataframe.py

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,152 @@
44
from typing import TYPE_CHECKING
55
from typing import Any
66
from typing import Iterator
7+
from typing import Literal
78
from typing import Mapping
89
from typing import Protocol
910
from typing import Sequence
11+
from typing import Sized
1012
from typing import TypeVar
13+
from typing import overload
1114

12-
from narwhals._compliant.typing import CompliantSeriesT_co
15+
from narwhals._compliant.typing import CompliantSeriesT
1316
from narwhals._compliant.typing import EagerSeriesT
1417
from narwhals._expression_parsing import evaluate_output_names_and_aliases
1518

1619
if TYPE_CHECKING:
20+
from io import BytesIO
21+
from pathlib import Path
22+
23+
import pandas as pd
24+
import polars as pl
25+
import pyarrow as pa
1726
from typing_extensions import Self
1827
from typing_extensions import TypeIs
1928

2029
from narwhals._compliant.expr import EagerExpr
2130
from narwhals.dtypes import DType
31+
from narwhals.typing import SizeUnit
32+
from narwhals.typing import _2DArray
33+
from narwhals.utils import Implementation
2234

2335
__all__ = ["CompliantDataFrame", "CompliantLazyFrame", "EagerDataFrame"]
2436

2537
T = TypeVar("T")
2638

2739

28-
class CompliantDataFrame(Protocol[CompliantSeriesT_co]):
40+
class CompliantDataFrame(Sized, Protocol[CompliantSeriesT]):
2941
def __narwhals_dataframe__(self) -> Self: ...
3042
def __narwhals_namespace__(self) -> Any: ...
31-
def simple_select(
32-
self, *column_names: str
33-
) -> Self: ... # `select` where all args are column names.
43+
def __array__(self, dtype: Any, *, copy: bool | None) -> _2DArray: ...
44+
def __getitem__(self, item: Any) -> CompliantSeriesT | Self: ...
45+
def simple_select(self, *column_names: str) -> Self:
46+
"""`select` where all args are column names."""
47+
...
48+
3449
def aggregate(self, *exprs: Any) -> Self: # pragma: no cover
35-
... # `select` where all args are aggregations or literals
36-
# (so, no broadcasting is necessary).
50+
"""`select` where all args are aggregations or literals.
51+
52+
(so, no broadcasting is necessary).
53+
"""
54+
...
3755

3856
@property
3957
def columns(self) -> Sequence[str]: ...
4058
@property
4159
def schema(self) -> Mapping[str, DType]: ...
42-
def get_column(self, name: str) -> CompliantSeriesT_co: ...
43-
def iter_columns(self) -> Iterator[CompliantSeriesT_co]: ...
60+
@property
61+
def shape(self) -> tuple[int, int]: ...
62+
def clone(self) -> Self: ...
63+
def collect(
64+
self, backend: Implementation | None, **kwargs: Any
65+
) -> CompliantDataFrame[Any]: ...
66+
def collect_schema(self) -> Mapping[str, DType]: ...
67+
def drop(self, columns: Sequence[str], strict: bool) -> Self: ... # noqa: FBT001
68+
def drop_nulls(self, subset: Sequence[str] | None) -> Self: ...
69+
def estimated_size(self, unit: SizeUnit) -> int | float: ...
70+
def filter(self, predicate: Any) -> Self: ...
71+
def gather_every(self, n: int, offset: int) -> Self: ...
72+
def get_column(self, name: str) -> CompliantSeriesT: ...
73+
def group_by(self, *keys: str, drop_null_keys: bool) -> Any: ...
74+
def head(self, n: int) -> Self: ...
75+
def item(self, row: int | None, column: int | str | None) -> Any: ...
76+
def iter_columns(self) -> Iterator[CompliantSeriesT]: ...
77+
def iter_rows(
78+
self, *, named: bool, buffer_size: int
79+
) -> Iterator[tuple[Any, ...]] | Iterator[Mapping[str, Any]]: ...
80+
def is_unique(self) -> CompliantSeriesT: ...
81+
def join(
82+
self: Self,
83+
other: Self,
84+
*,
85+
how: Literal["left", "inner", "cross", "anti", "semi"],
86+
left_on: Sequence[str] | None,
87+
right_on: Sequence[str] | None,
88+
suffix: str,
89+
) -> Self: ...
90+
def join_asof(
91+
self: Self,
92+
other: Self,
93+
*,
94+
left_on: str | None,
95+
right_on: str | None,
96+
by_left: Sequence[str] | None,
97+
by_right: Sequence[str] | None,
98+
strategy: Literal["backward", "forward", "nearest"],
99+
suffix: str,
100+
) -> Self: ...
101+
def lazy(self, *, backend: Implementation | None) -> CompliantLazyFrame: ...
102+
def rename(self, mapping: Mapping[str, str]) -> Self: ...
103+
def row(self, index: int) -> tuple[Any, ...]: ...
104+
def rows(
105+
self, *, named: bool
106+
) -> Sequence[tuple[Any, ...]] | Sequence[Mapping[str, Any]]: ...
107+
def sample(
108+
self,
109+
n: int | None,
110+
*,
111+
fraction: float | None,
112+
with_replacement: bool,
113+
seed: int | None,
114+
) -> Self: ...
115+
def select(self, *exprs: Any) -> Self: ...
116+
def sort(
117+
self, *by: str, descending: bool | Sequence[bool], nulls_last: bool
118+
) -> Self: ...
119+
def tail(self, n: int) -> Self: ...
120+
def to_arrow(self) -> pa.Table: ...
121+
def to_numpy(self) -> _2DArray: ...
122+
def to_pandas(self) -> pd.DataFrame: ...
123+
def to_polars(self) -> pl.DataFrame: ...
124+
@overload
125+
def to_dict(self, *, as_series: Literal[True]) -> dict[str, CompliantSeriesT]: ...
126+
@overload
127+
def to_dict(self, *, as_series: Literal[False]) -> dict[str, list[Any]]: ...
128+
def to_dict(
129+
self, *, as_series: bool
130+
) -> dict[str, CompliantSeriesT] | dict[str, list[Any]]: ...
131+
def unique(
132+
self,
133+
subset: Sequence[str] | None,
134+
*,
135+
keep: Literal["any", "first", "last", "none"],
136+
maintain_order: bool | None,
137+
) -> Self: ...
138+
def unpivot(
139+
self,
140+
on: Sequence[str] | None,
141+
index: Sequence[str] | None,
142+
variable_name: str,
143+
value_name: str,
144+
) -> Self: ...
145+
def with_columns(self, *exprs: Any) -> Self: ...
146+
def with_row_index(self, name: str) -> Self: ...
147+
@overload
148+
def write_csv(self, file: None) -> str: ...
149+
@overload
150+
def write_csv(self, file: str | Path | BytesIO) -> None: ...
151+
def write_csv(self, file: str | Path | BytesIO | None) -> str | None: ...
152+
def write_parquet(self, file: str | Path | BytesIO) -> None: ...
44153

45154

46155
class CompliantLazyFrame(Protocol):

narwhals/_compliant/typing.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@
2525
"CompliantDataFrameT",
2626
"CompliantFrameT",
2727
"CompliantLazyFrameT",
28-
"CompliantSeriesT_co",
28+
"CompliantSeriesT",
2929
"IntoCompliantExpr",
3030
]
3131
NativeExprT_co = TypeVar("NativeExprT_co", bound="NativeExpr", covariant=True)
32-
CompliantSeriesT_co = TypeVar(
33-
"CompliantSeriesT_co", bound="CompliantSeries", covariant=True
34-
)
32+
CompliantSeriesT = TypeVar("CompliantSeriesT", bound="CompliantSeries")
3533
CompliantSeriesOrNativeExprT_co = TypeVar(
3634
"CompliantSeriesOrNativeExprT_co",
3735
bound="CompliantSeries | NativeExpr",

0 commit comments

Comments
 (0)