From befef005820bc470d86fb72c06092f2dada9bd1d Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Thu, 13 Nov 2025 15:04:31 +0000 Subject: [PATCH 1/8] Type Index methods: `is_`, `ravel`, `take`, `repeat`, `to_flat_index`, `to_frame`, `set_names`, `rename`, remove undocumented `__copy__`, `__deepcopy__`, `__array_wrap__` --- pandas-stubs/core/indexes/base.pyi | 34 ++++++++++++++++------------- pandas-stubs/core/indexes/multi.pyi | 2 +- tests/indexes/test_indexes.py | 13 +++++++++++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 1866e9464..87b4a1d0f 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -3,7 +3,7 @@ from collections.abc import ( Callable, Hashable, Iterable, - MutableMapping, + Mapping, Sequence, ) from datetime import ( @@ -104,6 +104,7 @@ from pandas._typing import ( SequenceNotStr, SliceType, SupportsDType, + TakeIndexer, TimedeltaDtypeArg, TimestampDtypeArg, np_1darray, @@ -350,40 +351,37 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): Index, ]: ... @final - def is_(self, other) -> bool: ... + def is_(self, other: object) -> bool: ... def __len__(self) -> int: ... def __array__( self, dtype: _str | np.dtype = ..., copy: bool | None = ... ) -> np_1darray: ... - def __array_wrap__(self, result, context=...): ... @property def dtype(self) -> DtypeObj: ... @final - def ravel(self, order: _str = ...): ... + def ravel(self, order: _str = "C") -> Self: ... def view(self, cls=...): ... def astype(self, dtype: DtypeArg, copy: bool = True) -> Index: ... def take( self, - indices, + indices: TakeIndexer, axis: int = 0, allow_fill: bool = True, fill_value: Scalar | None = None, **kwargs: Any, + ) -> Self: ... + def repeat( + self, repeats: int | AnyArrayLikeInt | Sequence[int], axis: None = None ): ... - def repeat(self, repeats, axis=...): ... def copy(self, name: Hashable = ..., deep: bool = False) -> Self: ... - @final - def __copy__(self, **kwargs: Any): ... - @final - def __deepcopy__(self, memo: MutableMapping[int, Any] | None = None) -> Self: ... def format( self, name: bool = ..., formatter: Callable | None = ..., na_rep: _str = ... ) -> list[_str]: ... - def to_flat_index(self): ... + def to_flat_index(self) -> Index: ... def to_series( self, index: Index | None = None, name: Hashable | None = None ) -> Series[S1]: ... - def to_frame(self, index: bool = True, name=...) -> DataFrame: ... + def to_frame(self, index: bool = True, name: Hashable = ...) -> DataFrame: ... @property def name(self) -> Hashable | None: ... @name.setter @@ -392,11 +390,17 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def names(self) -> list[Hashable | None]: ... @names.setter def names(self, names: SequenceNotStr[Hashable | None]) -> None: ... - def set_names(self, names, *, level=..., inplace: bool = ...): ... + def set_names( + self, + names: Hashable | Sequence[Hashable] | Mapping[Any, Hashable], + *, + level: Level | Sequence[Level] | None = None, + inplace: bool = False, + ) -> Self: ... @overload - def rename(self, name, *, inplace: Literal[False] = False) -> Self: ... + def rename(self, name: Hashable, *, inplace: Literal[False] = False) -> Self: ... @overload - def rename(self, name, *, inplace: Literal[True]) -> None: ... + def rename(self, name: Hashable, *, inplace: Literal[True]) -> None: ... @property def nlevels(self) -> int: ... def get_level_values(self, level: int | _str) -> Index: ... diff --git a/pandas-stubs/core/indexes/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index d44b42bd6..a7076ade4 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -109,7 +109,7 @@ class MultiIndex(Index): def dropna(self, how: AnyAll = "any") -> Self: ... def get_level_values(self, level: str | int) -> Index: ... def unique(self, level=...): ... - def to_frame( + def to_frame( # type: ignore[override] self, index: bool = True, name: list[HashableT] = ..., diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 8647f6dde..f75e86fd9 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -1523,3 +1523,16 @@ def test_datetimeindex_where() -> None: val_range = pd.RangeIndex(2).where(pd.Series([True, False]), 3) check(assert_type(val_range, pd.Index), pd.RangeIndex) + + +def test_index_set_names() -> None: + """Test Index.where with multiple types of other GH1419.""" + idx = pd.Index([1, 2]) + check(assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.int64) + check( + assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), pd.Index, np.int64 + ) + + mi = pd.MultiIndex.from_arrays([[1, 2, 3], [4, 5, 6]], names=["elk", "owl"]) + mi.set_names(["beluga", "pig"]) + mi.set_names({"elk": "beluga", "owl": "pig"}) From 4934542df100a7bfa22c71e92710482a23418c30 Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:01:30 +0000 Subject: [PATCH 2/8] fix typing --- pandas-stubs/core/indexes/multi.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/core/indexes/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index a7076ade4..76ece4021 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -109,7 +109,7 @@ class MultiIndex(Index): def dropna(self, how: AnyAll = "any") -> Self: ... def get_level_values(self, level: str | int) -> Index: ... def unique(self, level=...): ... - def to_frame( # type: ignore[override] + def to_frame( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] self, index: bool = True, name: list[HashableT] = ..., From 96f67d9a591fe3163e8d8c8032fc34dcc2e2c23f Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:28:18 +0000 Subject: [PATCH 3/8] only allow for `Mapping` `names` in `set_names` for `MultiIndex` --- pandas-stubs/core/indexes/base.pyi | 9 ++++----- pandas-stubs/core/indexes/multi.pyi | 11 ++++++++++- tests/indexes/test_indexes.py | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 87b4a1d0f..e29561eb3 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -3,7 +3,6 @@ from collections.abc import ( Callable, Hashable, Iterable, - Mapping, Sequence, ) from datetime import ( @@ -82,6 +81,7 @@ from pandas._typing import ( AnyArrayLikeInt, ArrayLike, AxesData, + Axis, CategoryDtypeArg, DropKeep, Dtype, @@ -365,19 +365,18 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def take( self, indices: TakeIndexer, - axis: int = 0, + axis: Axis = 0, allow_fill: bool = True, fill_value: Scalar | None = None, **kwargs: Any, ) -> Self: ... def repeat( self, repeats: int | AnyArrayLikeInt | Sequence[int], axis: None = None - ): ... + ) -> Self: ... def copy(self, name: Hashable = ..., deep: bool = False) -> Self: ... def format( self, name: bool = ..., formatter: Callable | None = ..., na_rep: _str = ... ) -> list[_str]: ... - def to_flat_index(self) -> Index: ... def to_series( self, index: Index | None = None, name: Hashable | None = None ) -> Series[S1]: ... @@ -392,7 +391,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def names(self, names: SequenceNotStr[Hashable | None]) -> None: ... def set_names( self, - names: Hashable | Sequence[Hashable] | Mapping[Any, Hashable], + names: Hashable | Sequence[Hashable], *, level: Level | Sequence[Level] | None = None, inplace: bool = False, diff --git a/pandas-stubs/core/indexes/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index 76ece4021..544ff061a 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -2,9 +2,11 @@ from collections.abc import ( Callable, Hashable, Iterable, + Mapping, Sequence, ) from typing import ( + Any, final, overload, ) @@ -115,7 +117,7 @@ class MultiIndex(Index): name: list[HashableT] = ..., allow_duplicates: bool = False, ) -> pd.DataFrame: ... - def to_flat_index(self): ... + def to_flat_index(self) -> Index: ... def remove_unused_levels(self): ... @property def nlevels(self) -> int: ... @@ -163,3 +165,10 @@ class MultiIndex(Index): def insert(self, loc, item): ... def delete(self, loc): ... def isin(self, values, level=...) -> np_1darray_bool: ... + def set_names( + self, + names: Hashable | Sequence[Hashable] | Mapping[Any, Hashable], + *, + level: Level | Sequence[Level] | None = None, + inplace: bool = False, + ) -> Self: ... diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index f75e86fd9..4ee757aea 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -1529,6 +1529,7 @@ def test_index_set_names() -> None: """Test Index.where with multiple types of other GH1419.""" idx = pd.Index([1, 2]) check(assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.int64) + check(assert_type(idx.set_names((0,)), "pd.Index[int]"), pd.Index, np.int64) check( assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), pd.Index, np.int64 ) From 68fc4291ac640de7c3b2691287a2d9c706b85b82 Mon Sep 17 00:00:00 2001 From: Marco Edward Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:21:24 +0000 Subject: [PATCH 4/8] Apply suggestions from code review Co-authored-by: Yi-Fan Wang --- tests/indexes/test_indexes.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 4ee757aea..48e9d68bc 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -1528,12 +1528,13 @@ def test_datetimeindex_where() -> None: def test_index_set_names() -> None: """Test Index.where with multiple types of other GH1419.""" idx = pd.Index([1, 2]) - check(assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.int64) - check(assert_type(idx.set_names((0,)), "pd.Index[int]"), pd.Index, np.int64) + check(assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.integer) + check(assert_type(idx.set_names((0,)), "pd.Index[int]"), pd.Index, np.integer) check( - assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), pd.Index, np.int64 + assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), pd.Index, np.integer ) mi = pd.MultiIndex.from_arrays([[1, 2, 3], [4, 5, 6]], names=["elk", "owl"]) - mi.set_names(["beluga", "pig"]) - mi.set_names({"elk": "beluga", "owl": "pig"}) + check(assert_type(mi.set_names(["beluga", "pig"]), pd.MultiIndex), pd.MultiIndex) + check(assert_type(mi.set_names({"elk": "beluga", "owl": "pig"}), pd.MultiIndex), pd.MultiIndex) + check(assert_type(pd.Index([(1,)]).set_names(1), pd.MultiIndex), pd.MultiIndex, tuple) From 3c1154f801f12f0c8cf155e43ba9a0a9f708c4a7 Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:22:05 +0000 Subject: [PATCH 5/8] lint --- tests/indexes/test_indexes.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 48e9d68bc..bbdbb0203 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -1528,13 +1528,22 @@ def test_datetimeindex_where() -> None: def test_index_set_names() -> None: """Test Index.where with multiple types of other GH1419.""" idx = pd.Index([1, 2]) - check(assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.integer) + check( + assert_type(idx.set_names("chinchilla"), "pd.Index[int]"), pd.Index, np.integer + ) check(assert_type(idx.set_names((0,)), "pd.Index[int]"), pd.Index, np.integer) check( - assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), pd.Index, np.integer + assert_type(idx.set_names(["chinchilla"]), "pd.Index[int]"), + pd.Index, + np.integer, ) mi = pd.MultiIndex.from_arrays([[1, 2, 3], [4, 5, 6]], names=["elk", "owl"]) check(assert_type(mi.set_names(["beluga", "pig"]), pd.MultiIndex), pd.MultiIndex) - check(assert_type(mi.set_names({"elk": "beluga", "owl": "pig"}), pd.MultiIndex), pd.MultiIndex) - check(assert_type(pd.Index([(1,)]).set_names(1), pd.MultiIndex), pd.MultiIndex, tuple) + check( + assert_type(mi.set_names({"elk": "beluga", "owl": "pig"}), pd.MultiIndex), + pd.MultiIndex, + ) + check( + assert_type(pd.Index([(1,)]).set_names(1), pd.MultiIndex), pd.MultiIndex, tuple + ) From bfdf31c5edae3c909b68a1e13565c59ab63a7a2b Mon Sep 17 00:00:00 2001 From: Marco Edward Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:22:09 +0000 Subject: [PATCH 6/8] Update pandas-stubs/core/indexes/base.pyi Co-authored-by: Yi-Fan Wang --- pandas-stubs/core/indexes/base.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index e29561eb3..d02d63d98 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -380,7 +380,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def to_series( self, index: Index | None = None, name: Hashable | None = None ) -> Series[S1]: ... - def to_frame(self, index: bool = True, name: Hashable = ...) -> DataFrame: ... + def to_frame(self, index: bool = True, name: Hashable = lib.no_default) -> DataFrame: ... @property def name(self) -> Hashable | None: ... @name.setter From f275b516584c5e7aa92bc7f4a32a6914fe066881 Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:26:01 +0000 Subject: [PATCH 7/8] fix no_default usage --- pandas-stubs/core/indexes/base.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index d02d63d98..e29561eb3 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -380,7 +380,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def to_series( self, index: Index | None = None, name: Hashable | None = None ) -> Series[S1]: ... - def to_frame(self, index: bool = True, name: Hashable = lib.no_default) -> DataFrame: ... + def to_frame(self, index: bool = True, name: Hashable = ...) -> DataFrame: ... @property def name(self) -> Hashable | None: ... @name.setter From eb9c67ca1ffaa334f89000221becb4399cc91e58 Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:37:12 +0000 Subject: [PATCH 8/8] fix the extra `pd.Index([(1,)])` case --- tests/indexes/test_indexes.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index bbdbb0203..45cd97fc1 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -1544,6 +1544,5 @@ def test_index_set_names() -> None: assert_type(mi.set_names({"elk": "beluga", "owl": "pig"}), pd.MultiIndex), pd.MultiIndex, ) - check( - assert_type(pd.Index([(1,)]).set_names(1), pd.MultiIndex), pd.MultiIndex, tuple - ) + mi = cast("pd.MultiIndex", pd.Index([(1,)])) + check(assert_type(mi.set_names(1), pd.MultiIndex), pd.MultiIndex, tuple)