From 59875e3de95fb70c3ba33a33f4798e2993c48eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 26 Aug 2025 16:08:38 -0400 Subject: [PATCH 1/4] GH1336 Reconcile Series.index and DataFrame.index --- pandas-stubs/_libs/tslibs/timestamps.pyi | 5 ++--- pandas-stubs/core/frame.pyi | 7 ++----- pandas-stubs/core/indexes/base.pyi | 3 --- pandas-stubs/core/series.pyi | 2 +- tests/test_scalars.py | 5 +---- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/pandas-stubs/_libs/tslibs/timestamps.pyi b/pandas-stubs/_libs/tslibs/timestamps.pyi index 6146d0b3..f74b98d4 100644 --- a/pandas-stubs/_libs/tslibs/timestamps.pyi +++ b/pandas-stubs/_libs/tslibs/timestamps.pyi @@ -19,7 +19,6 @@ from typing import ( import numpy as np from pandas import ( DatetimeIndex, - Index, TimedeltaIndex, ) from pandas.core.series import ( @@ -257,7 +256,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __eq__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __eq__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __eq__(self, other: np_ndarray[ShapeT, np.datetime64]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload @@ -267,7 +266,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __ne__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __ne__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: np_ndarray[ShapeT, np.datetime64]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 34ba0547..c1afb7a5 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -39,10 +39,7 @@ from pandas.core.arraylike import OpsMixin from pandas.core.generic import NDFrame from pandas.core.groupby.generic import DataFrameGroupBy from pandas.core.indexers import BaseIndexer -from pandas.core.indexes.base import ( - Index, - UnknownIndex, -) +from pandas.core.indexes.base import Index from pandas.core.indexes.category import CategoricalIndex from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.interval import IntervalIndex @@ -1763,7 +1760,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): @property # mypy complains if we use Index[Any] instead of UnknownIndex here, even though # the latter is aliased to the former ¯\_(ツ)_/¯. - def index(self) -> UnknownIndex: ... + def index(self) -> Index: ... @index.setter def index(self, idx: Index) -> None: ... @property diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index b7bb8a48..b193dccf 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -15,7 +15,6 @@ from typing import ( ClassVar, Generic, Literal, - TypeAlias, final, overload, type_check_only, @@ -508,8 +507,6 @@ class Index(IndexOpsMixin[S1]): ) -> Self: ... def infer_objects(self, copy: bool = True) -> Self: ... -UnknownIndex: TypeAlias = Index[Any] - @type_check_only class _IndexSubclassBase(Index[S1], Generic[S1, GenericT_co]): @overload diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index f5bea7bf..768e29b7 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -495,7 +495,7 @@ class Series(IndexOpsMixin[S1], NDFrame): self, repeats: int | list[int], axis: AxisIndex | None = 0 ) -> Series[S1]: ... @property - def index(self) -> Index | MultiIndex: ... + def index(self) -> Index: ... @index.setter def index(self, idx: Index) -> None: ... @overload diff --git a/tests/test_scalars.py b/tests/test_scalars.py index 92b29f4b..ca0817a4 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -1287,7 +1287,7 @@ def test_timestamp_cmp_series() -> None: def test_timestamp_cmp_index() -> None: ts = pd.Timestamp(year=2000, month=3, day=24, hour=12, minute=27) dt_idx = pd.DatetimeIndex(["2000-1-1"]) - # DatetimeIndex, but the type checker thinks it is UnknownIndex. + # DatetimeIndex, but the type checker thinks it is Index[Any]. un_idx = pd.DataFrame({"a": [1]}, index=dt_idx).index # >, <= @@ -1322,9 +1322,6 @@ def test_timestamp_cmp_index() -> None: eq_dt1 = check(assert_type(ts == dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) ne_dt1 = check(assert_type(ts != dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) assert (eq_dt1 != ne_dt1).all() - eq_un1 = check(assert_type(ts == un_idx, np_1darray[np.bool]), np_1darray[np.bool]) - ne_un1 = check(assert_type(ts != un_idx, np_1darray[np.bool]), np_1darray[np.bool]) - assert (eq_un1 != ne_un1).all() # ==, != (ts on the rhs, use == and != of lhs) eq_rhs_dt1 = check( From 53573be0b85785a7ac1ec3cdc241d8c1fc20bf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 26 Aug 2025 16:13:37 -0400 Subject: [PATCH 2/4] GH1336 Remove comment --- pandas-stubs/core/frame.pyi | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index c1afb7a5..10bff7b2 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -1758,8 +1758,6 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): @property def iloc(self) -> _iLocIndexerFrame[Self]: ... @property - # mypy complains if we use Index[Any] instead of UnknownIndex here, even though - # the latter is aliased to the former ¯\_(ツ)_/¯. def index(self) -> Index: ... @index.setter def index(self, idx: Index) -> None: ... From aa846a1cab7bba986e14cda6717695a56cf23ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 26 Aug 2025 21:20:53 -0400 Subject: [PATCH 3/4] GH1336 PR Feedback --- pandas-stubs/_libs/tslibs/period.pyi | 4 ++-- pandas-stubs/_libs/tslibs/timedeltas.pyi | 4 ++-- pandas-stubs/_libs/tslibs/timestamps.pyi | 5 +++-- tests/test_scalars.py | 3 +++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pandas-stubs/_libs/tslibs/period.pyi b/pandas-stubs/_libs/tslibs/period.pyi index 603ae742..b658270e 100644 --- a/pandas-stubs/_libs/tslibs/period.pyi +++ b/pandas-stubs/_libs/tslibs/period.pyi @@ -102,7 +102,7 @@ class Period(PeriodMixin): @overload def __eq__(self, other: Period) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __eq__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __eq__(self, other: PeriodSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload @@ -154,7 +154,7 @@ class Period(PeriodMixin): @overload def __ne__(self, other: Period) -> bool: ... # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] @overload - def __ne__(self, other: PeriodIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: PeriodSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload diff --git a/pandas-stubs/_libs/tslibs/timedeltas.pyi b/pandas-stubs/_libs/tslibs/timedeltas.pyi index 54c0509e..e73b3cc3 100644 --- a/pandas-stubs/_libs/tslibs/timedeltas.pyi +++ b/pandas-stubs/_libs/tslibs/timedeltas.pyi @@ -311,7 +311,7 @@ class Timedelta(timedelta): @overload def __eq__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __eq__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __eq__( # type: ignore[overload-overlap] self, other: np_ndarray[ShapeT, np.timedelta64] @@ -324,7 +324,7 @@ class Timedelta(timedelta): @overload def __ne__(self, other: TimedeltaSeries | Series[pd.Timedelta]) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __ne__(self, other: TimedeltaIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __ne__( # type: ignore[overload-overlap] self, other: np_ndarray[ShapeT, np.timedelta64] diff --git a/pandas-stubs/_libs/tslibs/timestamps.pyi b/pandas-stubs/_libs/tslibs/timestamps.pyi index f74b98d4..ff3a0be5 100644 --- a/pandas-stubs/_libs/tslibs/timestamps.pyi +++ b/pandas-stubs/_libs/tslibs/timestamps.pyi @@ -21,6 +21,7 @@ from pandas import ( DatetimeIndex, TimedeltaIndex, ) +from pandas.core.indexes.base import Index from pandas.core.series import ( Series, TimedeltaSeries, @@ -256,7 +257,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __eq__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __eq__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __eq__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __eq__(self, other: np_ndarray[ShapeT, np.datetime64]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload @@ -266,7 +267,7 @@ class Timestamp(datetime, SupportsIndex): @overload def __ne__(self, other: TimestampSeries) -> Series[bool]: ... # type: ignore[overload-overlap] @overload - def __ne__(self, other: DatetimeIndex) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] + def __ne__(self, other: Index) -> np_1darray[np.bool]: ... # type: ignore[overload-overlap] @overload def __ne__(self, other: np_ndarray[ShapeT, np.datetime64]) -> np_ndarray[ShapeT, np.bool]: ... # type: ignore[overload-overlap] @overload diff --git a/tests/test_scalars.py b/tests/test_scalars.py index ca0817a4..8d98cd34 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -1322,6 +1322,9 @@ def test_timestamp_cmp_index() -> None: eq_dt1 = check(assert_type(ts == dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) ne_dt1 = check(assert_type(ts != dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) assert (eq_dt1 != ne_dt1).all() + eq_un1 = check(assert_type(ts == un_idx, np_1darray[np.bool]), np_1darray[np.bool]) # type: ignore[assert-type] + ne_un1 = check(assert_type(ts != un_idx, np_1darray[np.bool]), np_1darray[np.bool]) # type: ignore[assert-type] + assert (eq_un1 != ne_un1).all() # ==, != (ts on the rhs, use == and != of lhs) eq_rhs_dt1 = check( From c83c631d3a5a38252c96379d03a7a97bb84d05f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Tue, 26 Aug 2025 21:22:56 -0400 Subject: [PATCH 4/4] GH1336 PR Feedback --- tests/test_scalars.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_scalars.py b/tests/test_scalars.py index 8d98cd34..7feefad3 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -1322,6 +1322,7 @@ def test_timestamp_cmp_index() -> None: eq_dt1 = check(assert_type(ts == dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) ne_dt1 = check(assert_type(ts != dt_idx, np_1darray[np.bool]), np_1darray[np.bool]) assert (eq_dt1 != ne_dt1).all() + # there is a mypy bug where ts.__eq__(Index) gets revealed as Any and not np_1darray eq_un1 = check(assert_type(ts == un_idx, np_1darray[np.bool]), np_1darray[np.bool]) # type: ignore[assert-type] ne_un1 = check(assert_type(ts != un_idx, np_1darray[np.bool]), np_1darray[np.bool]) # type: ignore[assert-type] assert (eq_un1 != ne_un1).all()