From a370cab05e3a7e9f270ff5ee7e08207db118b18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20W=C3=B6rtwein?= Date: Tue, 26 Dec 2023 16:32:21 -0500 Subject: [PATCH] itertuples (#842) * Fix itertuples * _PandasNamedTuple * 3.9 * adjust old test * return Scalar --- pandas-stubs/core/frame.pyi | 5 ++++- tests/test_frame.py | 39 +++++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 759c06de..2de05988 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -249,7 +249,7 @@ class DataFrame(NDFrame, OpsMixin): def iterrows(self) -> Iterable[tuple[Hashable, Series]]: ... def itertuples( self, index: _bool = ..., name: _str | None = ... - ) -> Iterable[tuple[Any, ...]]: ... + ) -> Iterable[_PandasNamedTuple]: ... def __len__(self) -> int: ... @overload def dot(self, other: DataFrame | ArrayLike) -> DataFrame: ... @@ -2279,3 +2279,6 @@ class DataFrame(NDFrame, OpsMixin): ) -> Self: ... def __truediv__(self, other: float | DataFrame | Series | Sequence) -> Self: ... def __rtruediv__(self, other: float | DataFrame | Series | Sequence) -> Self: ... + +class _PandasNamedTuple(tuple[Any, ...]): + def __getattr__(self, field: str) -> Scalar: ... diff --git a/tests/test_frame.py b/tests/test_frame.py index 8eb23285..46f844c5 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -33,7 +33,10 @@ from pandas.core.resample import Resampler # noqa: F401 from pandas.core.series import Series import pytest -from typing_extensions import assert_type +from typing_extensions import ( + TypeAlias, + assert_type, +) import xarray as xr from pandas._typing import Scalar @@ -47,6 +50,11 @@ from pandas.io.formats.style import Styler from pandas.io.parsers import TextFileReader +if TYPE_CHECKING: + from pandas.core.frame import _PandasNamedTuple +else: + _PandasNamedTuple: TypeAlias = tuple + DF = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) @@ -439,9 +447,23 @@ def test_types_iterrows() -> None: def test_types_itertuples() -> None: df = pd.DataFrame(data={"col1": [2, 1], "col2": [3, 4]}) - res1: Iterable[tuple[Any, ...]] = df.itertuples() - res2: Iterable[tuple[Any, ...]] = df.itertuples(index=False, name="Foobar") - res3: Iterable[tuple[Any, ...]] = df.itertuples(index=False, name=None) + check( + assert_type(df.itertuples(), Iterable[_PandasNamedTuple]), + Iterable, + _PandasNamedTuple, + ) + check( + assert_type( + df.itertuples(index=False, name="Foobar"), Iterable[_PandasNamedTuple] + ), + Iterable, + _PandasNamedTuple, + ) + check( + assert_type(df.itertuples(index=False, name=None), Iterable[_PandasNamedTuple]), + Iterable, + _PandasNamedTuple, + ) def test_types_sum() -> None: @@ -2962,3 +2984,12 @@ def test_frame_setitem_na() -> None: df["x"] = df["y"] + pd.Timedelta(days=3) df.loc[ind, :] = pd.NaT df.iloc[[0, 2], :] = pd.NaT + + +def test_itertuples() -> None: + # GH 822 + df = pd.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]}) + + for item in df.itertuples(): + check(assert_type(item, _PandasNamedTuple), tuple) + assert_type(item.a, Scalar)