Skip to content

Commit 62f8a1f

Browse files
authored
feat(typing): Backport generic Series to v1 (#2110)
* feat(typing): Backport generic `Series` to `v1` Related: - #1412 - #1930 - https://narwhals-dev.github.io/narwhals/backcompat/#breaking-changes-carried-out-so-far * fix(typing): Remove `mypy`-breaking `Any` That now would trigger **15** `[var-annotated]` warnings in our tests: ```py tests/utils_test.py:39: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pd.Series([1, 2, 3], index=[2, 1, 0]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/utils_test.py:65: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pd.Series([1, 2, 3], index=[2, 2, 0]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/utils_test.py:72: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/utils_test.py:171: error: Need type annotation for "index" [var-annotated] index = nw.from_native(pd.Series([0, 1, 2]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/utils_test.py:198: error: Need type annotation for "series" [var-annotated] series = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/utils_test.py:215: error: Need type annotation for "pandas_series" [var-annotated] pandas_series = nw.from_native( ^ tests/utils_test.py:232: error: Need type annotation for "series" [var-annotated] series = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/stable_api_test.py:136: error: Need type annotation for "stable_df" [var-annotated] stable_df = nw_v1.from_native(pl.Series(), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/dtypes_test.py:152: error: Need type annotation for "result" [var-annotated] result = nw.from_native(s, series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/dtypes_test.py:182: error: Need type annotation for "snw" [var-annotated] snw = nw.from_native(s, series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/translate/from_native_test.py:140: error: Need type annotation for "res" [var-annotated] res = nw.from_native(obj, series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/series_only/hist_test.py:277: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/series_only/arrow_c_stream_test.py:18: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/series_only/arrow_c_stream_test.py:31: error: Need type annotation for "s" [var-annotated] s = nw.from_native(pl.Series([1, 2, 3]), series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ tests/expr_and_series/cast_test.py:172: error: Need type annotation for "s" [var-annotated] s = nw.from_native(s_pd, series_only=True) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Found 15 errors in 7 files (checked 345 source files) ``` * fix(typing): Try with a default for downstream Hoping to fix: - https://github.com/narwhals-dev/narwhals/actions/runs/13567019217/job/37922302040?pr=2110 - https://github.com/narwhals-dev/narwhals/actions/runs/13567019217/job/37922302040?pr=2110 * test(typing): add repro for bug found in `altair` #2110 (comment)
1 parent 5a1ec57 commit 62f8a1f

File tree

4 files changed

+297
-257
lines changed

4 files changed

+297
-257
lines changed

narwhals/stable/v1/__init__.py

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from typing import Iterable
88
from typing import Literal
99
from typing import Sequence
10-
from typing import TypeVar
1110
from typing import overload
1211
from warnings import warn
1312

@@ -86,6 +85,7 @@
8685
from typing import Mapping
8786

8887
from typing_extensions import Self
88+
from typing_extensions import TypeVar
8989

9090
from narwhals.dtypes import DType
9191
from narwhals.functions import ArrowStreamExportable
@@ -95,7 +95,13 @@
9595
from narwhals.typing import _1DArray
9696
from narwhals.typing import _2DArray
9797

98-
T = TypeVar("T")
98+
IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries", default=Any)
99+
T = TypeVar("T", default=Any)
100+
else:
101+
from typing import TypeVar
102+
103+
IntoSeriesT = TypeVar("IntoSeriesT", bound="IntoSeries")
104+
T = TypeVar("T")
99105

100106

101107
class DataFrame(NwDataFrame[IntoDataFrameT]):
@@ -127,7 +133,7 @@ class DataFrame(NwDataFrame[IntoDataFrameT]):
127133
# annotations are correct.
128134

129135
@property
130-
def _series(self: Self) -> type[Series]:
136+
def _series(self: Self) -> type[Series[Any]]:
131137
return Series
132138

133139
@property
@@ -138,7 +144,7 @@ def _lazyframe(self: Self) -> type[LazyFrame[Any]]:
138144
def __getitem__( # type: ignore[overload-overlap]
139145
self: Self,
140146
item: str | tuple[slice | Sequence[int] | _1DArray, int | str],
141-
) -> Series: ...
147+
) -> Series[Any]: ...
142148
@overload
143149
def __getitem__(
144150
self: Self,
@@ -192,16 +198,18 @@ def lazy(
192198
# Not sure what mypy is complaining about, probably some fancy
193199
# thing that I need to understand category theory for
194200
@overload # type: ignore[override]
195-
def to_dict(self: Self, *, as_series: Literal[True] = ...) -> dict[str, Series]: ...
201+
def to_dict(
202+
self: Self, *, as_series: Literal[True] = ...
203+
) -> dict[str, Series[Any]]: ...
196204
@overload
197205
def to_dict(self: Self, *, as_series: Literal[False]) -> dict[str, list[Any]]: ...
198206
@overload
199207
def to_dict(
200208
self: Self, *, as_series: bool
201-
) -> dict[str, Series] | dict[str, list[Any]]: ...
209+
) -> dict[str, Series[Any]] | dict[str, list[Any]]: ...
202210
def to_dict(
203211
self: Self, *, as_series: bool = True
204-
) -> dict[str, Series] | dict[str, list[Any]]:
212+
) -> dict[str, Series[Any]] | dict[str, list[Any]]:
205213
"""Convert DataFrame to a dictionary mapping column name to values.
206214
207215
Arguments:
@@ -213,15 +221,15 @@ def to_dict(
213221
"""
214222
return super().to_dict(as_series=as_series) # type: ignore[return-value]
215223

216-
def is_duplicated(self: Self) -> Series:
224+
def is_duplicated(self: Self) -> Series[Any]:
217225
r"""Get a mask of all duplicated rows in this DataFrame.
218226
219227
Returns:
220228
A new Series.
221229
"""
222230
return super().is_duplicated() # type: ignore[return-value]
223231

224-
def is_unique(self: Self) -> Series:
232+
def is_unique(self: Self) -> Series[Any]:
225233
r"""Get a mask of all unique rows in this DataFrame.
226234
227235
Returns:
@@ -341,7 +349,7 @@ def tail(self, n: int = 5) -> Self: # pragma: no cover
341349
return super().tail(n)
342350

343351

344-
class Series(NwSeries[Any]):
352+
class Series(NwSeries[IntoSeriesT]):
345353
"""Narwhals Series, backed by a native series.
346354
347355
!!! warning
@@ -1102,16 +1110,20 @@ def _stableify(obj: NwDataFrame[IntoFrameT]) -> DataFrame[IntoFrameT]: ...
11021110
@overload
11031111
def _stableify(obj: NwLazyFrame[IntoFrameT]) -> LazyFrame[IntoFrameT]: ...
11041112
@overload
1105-
def _stableify(obj: NwSeries[Any]) -> Series: ...
1113+
def _stableify(obj: NwSeries[IntoSeriesT]) -> Series[IntoSeriesT]: ...
11061114
@overload
11071115
def _stableify(obj: NwExpr) -> Expr: ...
11081116
@overload
11091117
def _stableify(obj: Any) -> Any: ...
11101118

11111119

11121120
def _stableify(
1113-
obj: NwDataFrame[IntoFrameT] | NwLazyFrame[IntoFrameT] | NwSeries[Any] | NwExpr | Any,
1114-
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series | Expr | Any:
1121+
obj: NwDataFrame[IntoFrameT]
1122+
| NwLazyFrame[IntoFrameT]
1123+
| NwSeries[IntoSeriesT]
1124+
| NwExpr
1125+
| Any,
1126+
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series[IntoSeriesT] | Expr | Any:
11151127
if isinstance(obj, NwDataFrame):
11161128
return DataFrame(
11171129
obj._compliant_frame._change_version(Version.V1),
@@ -1134,26 +1146,26 @@ def _stableify(
11341146

11351147
@overload
11361148
def from_native(
1137-
native_object: IntoDataFrameT | IntoSeries,
1149+
native_object: IntoDataFrameT | IntoSeriesT,
11381150
*,
11391151
strict: Literal[False],
11401152
eager_only: Literal[False] = ...,
11411153
eager_or_interchange_only: Literal[True],
11421154
series_only: Literal[False] = ...,
11431155
allow_series: Literal[True],
1144-
) -> DataFrame[IntoDataFrameT] | Series: ...
1156+
) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ...
11451157

11461158

11471159
@overload
11481160
def from_native(
1149-
native_object: IntoDataFrameT | IntoSeries,
1161+
native_object: IntoDataFrameT | IntoSeriesT,
11501162
*,
11511163
strict: Literal[False],
11521164
eager_only: Literal[True],
11531165
eager_or_interchange_only: Literal[False] = ...,
11541166
series_only: Literal[False] = ...,
11551167
allow_series: Literal[True],
1156-
) -> DataFrame[IntoDataFrameT] | Series: ...
1168+
) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ...
11571169

11581170

11591171
@overload
@@ -1206,26 +1218,26 @@ def from_native(
12061218

12071219
@overload
12081220
def from_native(
1209-
native_object: IntoFrameT | IntoSeries,
1221+
native_object: IntoFrameT | IntoSeriesT,
12101222
*,
12111223
strict: Literal[False],
12121224
eager_only: Literal[False] = ...,
12131225
eager_or_interchange_only: Literal[False] = ...,
12141226
series_only: Literal[False] = ...,
12151227
allow_series: Literal[True],
1216-
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series: ...
1228+
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series[IntoSeriesT]: ...
12171229

12181230

12191231
@overload
12201232
def from_native(
1221-
native_object: IntoSeries,
1233+
native_object: IntoSeriesT,
12221234
*,
12231235
strict: Literal[False],
12241236
eager_only: Literal[False] = ...,
12251237
eager_or_interchange_only: Literal[False] = ...,
12261238
series_only: Literal[True],
12271239
allow_series: None = ...,
1228-
) -> Series: ...
1240+
) -> Series[IntoSeriesT]: ...
12291241

12301242

12311243
@overload
@@ -1285,19 +1297,19 @@ def from_native(
12851297
eager_or_interchange_only: Literal[False] = ...,
12861298
series_only: Literal[False] = ...,
12871299
allow_series: Literal[True],
1288-
) -> DataFrame[Any] | LazyFrame[Any] | Series: ...
1300+
) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ...
12891301

12901302

12911303
@overload
12921304
def from_native(
1293-
native_object: IntoSeries | Any, # remain `Any` for downstream compatibility
1305+
native_object: IntoSeriesT,
12941306
*,
12951307
strict: Literal[True] = ...,
12961308
eager_only: Literal[False] = ...,
12971309
eager_or_interchange_only: Literal[False] = ...,
12981310
series_only: Literal[True],
12991311
allow_series: None = ...,
1300-
) -> Series: ...
1312+
) -> Series[IntoSeriesT]: ...
13011313

13021314

13031315
@overload
@@ -1326,14 +1338,14 @@ def from_native(
13261338

13271339
@overload
13281340
def from_native(
1329-
native_object: IntoDataFrameT | IntoSeries,
1341+
native_object: IntoDataFrameT | IntoSeriesT,
13301342
*,
13311343
pass_through: Literal[True],
13321344
eager_only: Literal[True],
13331345
eager_or_interchange_only: Literal[False] = ...,
13341346
series_only: Literal[False] = ...,
13351347
allow_series: Literal[True],
1336-
) -> DataFrame[IntoDataFrameT] | Series: ...
1348+
) -> DataFrame[IntoDataFrameT] | Series[IntoSeriesT]: ...
13371349

13381350

13391351
@overload
@@ -1386,26 +1398,26 @@ def from_native(
13861398

13871399
@overload
13881400
def from_native(
1389-
native_object: IntoFrameT | IntoSeries,
1401+
native_object: IntoFrameT | IntoSeriesT,
13901402
*,
13911403
pass_through: Literal[True],
13921404
eager_only: Literal[False] = ...,
13931405
eager_or_interchange_only: Literal[False] = ...,
13941406
series_only: Literal[False] = ...,
13951407
allow_series: Literal[True],
1396-
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series: ...
1408+
) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series[IntoSeriesT]: ...
13971409

13981410

13991411
@overload
14001412
def from_native(
1401-
native_object: IntoSeries,
1413+
native_object: IntoSeriesT,
14021414
*,
14031415
pass_through: Literal[True],
14041416
eager_only: Literal[False] = ...,
14051417
eager_or_interchange_only: Literal[False] = ...,
14061418
series_only: Literal[True],
14071419
allow_series: None = ...,
1408-
) -> Series: ...
1420+
) -> Series[IntoSeriesT]: ...
14091421

14101422

14111423
@overload
@@ -1465,19 +1477,19 @@ def from_native(
14651477
eager_or_interchange_only: Literal[False] = ...,
14661478
series_only: Literal[False] = ...,
14671479
allow_series: Literal[True],
1468-
) -> DataFrame[Any] | LazyFrame[Any] | Series: ...
1480+
) -> DataFrame[Any] | LazyFrame[Any] | Series[Any]: ...
14691481

14701482

14711483
@overload
14721484
def from_native(
1473-
native_object: IntoSeries,
1485+
native_object: IntoSeriesT,
14741486
*,
14751487
pass_through: Literal[False] = ...,
14761488
eager_only: Literal[False] = ...,
14771489
eager_or_interchange_only: Literal[False] = ...,
14781490
series_only: Literal[True],
14791491
allow_series: None = ...,
1480-
) -> Series: ...
1492+
) -> Series[IntoSeriesT]: ...
14811493

14821494

14831495
@overload
@@ -1506,15 +1518,15 @@ def from_native(
15061518

15071519

15081520
def from_native(
1509-
native_object: IntoFrameT | IntoFrame | IntoSeries | T,
1521+
native_object: IntoFrameT | IntoFrame | IntoSeriesT | IntoSeries | T,
15101522
*,
15111523
strict: bool | None = None,
15121524
pass_through: bool | None = None,
15131525
eager_only: bool = False,
15141526
eager_or_interchange_only: bool = False,
15151527
series_only: bool = False,
15161528
allow_series: bool | None = None,
1517-
) -> LazyFrame[IntoFrameT] | DataFrame[IntoFrameT] | Series | T:
1529+
) -> LazyFrame[IntoFrameT] | DataFrame[IntoFrameT] | Series[IntoSeriesT] | T:
15181530
"""Convert `native_object` to Narwhals Dataframe, Lazyframe, or Series.
15191531
15201532
Arguments:
@@ -1595,7 +1607,9 @@ def to_native(
15951607
narwhals_object: LazyFrame[IntoFrameT], *, strict: Literal[True] = ...
15961608
) -> IntoFrameT: ...
15971609
@overload
1598-
def to_native(narwhals_object: Series, *, strict: Literal[True] = ...) -> Any: ...
1610+
def to_native(
1611+
narwhals_object: Series[IntoSeriesT], *, strict: Literal[True] = ...
1612+
) -> IntoSeriesT: ...
15991613
@overload
16001614
def to_native(narwhals_object: Any, *, strict: bool) -> Any: ...
16011615
@overload
@@ -1607,17 +1621,21 @@ def to_native(
16071621
narwhals_object: LazyFrame[IntoFrameT], *, pass_through: Literal[False] = ...
16081622
) -> IntoFrameT: ...
16091623
@overload
1610-
def to_native(narwhals_object: Series, *, pass_through: Literal[False] = ...) -> Any: ...
1624+
def to_native(
1625+
narwhals_object: Series[IntoSeriesT], *, pass_through: Literal[False] = ...
1626+
) -> IntoSeriesT: ...
16111627
@overload
16121628
def to_native(narwhals_object: Any, *, pass_through: bool) -> Any: ...
16131629

16141630

16151631
def to_native(
1616-
narwhals_object: DataFrame[IntoDataFrameT] | LazyFrame[IntoFrameT] | Series,
1632+
narwhals_object: DataFrame[IntoDataFrameT]
1633+
| LazyFrame[IntoFrameT]
1634+
| Series[IntoSeriesT],
16171635
*,
16181636
strict: bool | None = None,
16191637
pass_through: bool | None = None,
1620-
) -> IntoFrameT | Any:
1638+
) -> IntoFrameT | IntoSeriesT | Any:
16211639
"""Convert Narwhals object to native one.
16221640
16231641
Arguments:
@@ -2124,7 +2142,7 @@ def new_series(
21242142
dtype: DType | type[DType] | None = None,
21252143
*,
21262144
native_namespace: ModuleType,
2127-
) -> Series:
2145+
) -> Series[Any]:
21282146
"""Instantiate Narwhals Series from iterable (e.g. list or array).
21292147
21302148
Arguments:

0 commit comments

Comments
 (0)