From 390e5d93cd5945e675f50929081d6e69ab126e74 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Fri, 18 Jul 2025 16:17:50 +0200 Subject: [PATCH 1/5] feat(index): append --- pandas-stubs/core/indexes/base.pyi | 7 ++++- pyproject.toml | 2 +- tests/test_indexes.py | 43 ++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 4f9ebd44..83151d3e 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -401,7 +401,12 @@ class Index(IndexOpsMixin[S1]): ) -> Self: ... @overload def __getitem__(self, idx: int | tuple[np_ndarray_anyint, ...]) -> S1: ... - def append(self, other): ... + @overload + def append(self, other: Index[Never]) -> Index: ... + @overload + def append(self, other: Index[S1] | Sequence[Index[S1]]) -> Index[S1]: ... + @overload + def append(self, other: Index | Sequence) -> Index: ... def putmask(self, mask, value): ... def equals(self, other) -> bool: ... @final diff --git a/pyproject.toml b/pyproject.toml index 71400163..d916bfa9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ mypy = "1.17.0" pandas = "2.3.0" pyarrow = ">=10.0.1" pytest = ">=7.1.2" -pyright = ">=1.1.400" +pyright = ">=1.1.403" ty = "^0.0.1a8" pyrefly = "^0.21.0" poethepoet = ">=0.16.5" diff --git a/tests/test_indexes.py b/tests/test_indexes.py index b3f566cd..d10717a3 100644 --- a/tests/test_indexes.py +++ b/tests/test_indexes.py @@ -1028,6 +1028,49 @@ def test_getitem() -> None: check(assert_type(i0[[0, 2]], "pd.Index[str]"), pd.Index, str) +def test_append_any() -> None: + """Test pd.Index.append that gives pd.Index[Any]""" + first = pd.Index([1]) + second = pd.Index(["a"]) + third = pd.Index([1, "a"]) + check(assert_type(first.append(second), pd.Index), pd.Index) + check(assert_type(first.append([second]), pd.Index), pd.Index) + + check(assert_type(first.append(third), pd.Index), pd.Index) + check(assert_type(first.append([third]), pd.Index), pd.Index) + check(assert_type(first.append([second, third]), pd.Index), pd.Index) + + check(assert_type(third.append([]), "pd.Index[str | int]"), pd.Index) # type: ignore[assert-type] + check(assert_type(third.append([first]), pd.Index), pd.Index) + + +def test_append_int() -> None: + """Test pd.Index[int].append""" + first = pd.Index([1]) + second = pd.Index([2]) + check(assert_type(first.append([]), "pd.Index[int]"), pd.Index, np.int64) + check(assert_type(first.append(second), "pd.Index[int]"), pd.Index, np.int64) + check(assert_type(first.append([second]), "pd.Index[int]"), pd.Index, np.int64) + + +def test_append_str() -> None: + """Test pd.Index[str].append""" + first = pd.Index(["str"]) + second = pd.Index(["rts"]) + check(assert_type(first.append([]), "pd.Index[str]"), pd.Index, str) + check(assert_type(first.append(second), "pd.Index[str]"), pd.Index, str) + check(assert_type(first.append([second]), "pd.Index[str]"), pd.Index, str) + + +def test_append_list_str() -> None: + """Test pd.Index[list[str]].append""" + first = pd.Index([["str", "rts"]]) + second = pd.Index([["srt", "trs"]]) + check(assert_type(first.append([]), "pd.Index[list[str]]"), pd.Index, list) + check(assert_type(first.append(second), "pd.Index[list[str]]"), pd.Index, list) + check(assert_type(first.append([second]), "pd.Index[list[str]]"), pd.Index, list) + + def test_range_index_range() -> None: """Test that pd.RangeIndex can be initialized from range.""" iri = pd.RangeIndex(range(5)) From 3a3405797429eb262949b6b9d3c7e6d6a0d301bd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Fri, 18 Jul 2025 17:17:34 +0200 Subject: [PATCH 2/5] fix(comment): https://github.com/pandas-dev/pandas-stubs/pull/1282#discussion_r2216245105 --- pandas-stubs/core/indexes/base.pyi | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 83151d3e..e9ccdb12 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -402,8 +402,6 @@ class Index(IndexOpsMixin[S1]): @overload def __getitem__(self, idx: int | tuple[np_ndarray_anyint, ...]) -> S1: ... @overload - def append(self, other: Index[Never]) -> Index: ... - @overload def append(self, other: Index[S1] | Sequence[Index[S1]]) -> Index[S1]: ... @overload def append(self, other: Index | Sequence) -> Index: ... From 45e0ad30fd18379d6b97f74d8e2a68ca37c3fa3a Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Fri, 18 Jul 2025 17:37:04 +0200 Subject: [PATCH 3/5] fix(comment): https://github.com/pandas-dev/pandas-stubs/pull/1282/files#r2216273709 --- pandas-stubs/core/indexes/base.pyi | 5 ++++- tests/test_indexes.py | 35 +++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index e9ccdb12..abb327b9 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -43,6 +43,7 @@ from typing_extensions import ( from pandas._libs.interval import _OrderableT from pandas._typing import ( S1, + S2, AnyAll, AxesData, DropKeep, @@ -402,7 +403,9 @@ class Index(IndexOpsMixin[S1]): @overload def __getitem__(self, idx: int | tuple[np_ndarray_anyint, ...]) -> S1: ... @overload - def append(self, other: Index[S1] | Sequence[Index[S1]]) -> Index[S1]: ... + def append(self, other: Index[S1] | Sequence[Index[S1]]) -> Self: ... + @overload + def append(self, other: Index[S2] | Sequence[Index[S2]]) -> Index[S1 | S2]: ... @overload def append(self, other: Index | Sequence) -> Index: ... def putmask(self, mask, value): ... diff --git a/tests/test_indexes.py b/tests/test_indexes.py index d10717a3..fd852647 100644 --- a/tests/test_indexes.py +++ b/tests/test_indexes.py @@ -3,7 +3,9 @@ import datetime as dt from typing import ( TYPE_CHECKING, + Any, Union, + cast, ) import numpy as np @@ -1028,20 +1030,37 @@ def test_getitem() -> None: check(assert_type(i0[[0, 2]], "pd.Index[str]"), pd.Index, str) -def test_append_any() -> None: +def test_append_mix() -> None: """Test pd.Index.append that gives pd.Index[Any]""" first = pd.Index([1]) second = pd.Index(["a"]) third = pd.Index([1, "a"]) - check(assert_type(first.append(second), pd.Index), pd.Index) - check(assert_type(first.append([second]), pd.Index), pd.Index) + check(assert_type(first.append(second), "pd.Index[int | str]"), pd.Index) + check(assert_type(first.append([second]), "pd.Index[int | str]"), pd.Index) - check(assert_type(first.append(third), pd.Index), pd.Index) - check(assert_type(first.append([third]), pd.Index), pd.Index) - check(assert_type(first.append([second, third]), pd.Index), pd.Index) + check(assert_type(first.append(third), "pd.Index[int | str]"), pd.Index) # type: ignore[assert-type] + check(assert_type(first.append([third]), "pd.Index[int | str]"), pd.Index) # type: ignore[assert-type] + check( + assert_type( # type: ignore[assert-type] + first.append([second, third]), # pyright: ignore[reportAssertTypeFailure] + "pd.Index[int | str]", + ), + pd.Index, + ) - check(assert_type(third.append([]), "pd.Index[str | int]"), pd.Index) # type: ignore[assert-type] - check(assert_type(third.append([first]), pd.Index), pd.Index) + check(assert_type(third.append([]), "pd.Index[int | str]"), pd.Index) # type: ignore[assert-type] + check( + assert_type(third.append(cast("list[Index[Any]]", [])), "pd.Index[int | str]"), # type: ignore[assert-type] + pd.Index, + ) + check(assert_type(third.append([first]), "pd.Index[int | str]"), pd.Index) # type: ignore[assert-type] + check( + assert_type( # type: ignore[assert-type] + third.append([first, second]), # pyright: ignore[reportAssertTypeFailure] + "pd.Index[int | str]", + ), + pd.Index, + ) def test_append_int() -> None: From 6c8ba76ac82bdc27b2c012a45d660a0f0488b6da Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Fri, 18 Jul 2025 17:49:19 +0200 Subject: [PATCH 4/5] fix(pyrefly): ignore --- 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 d313852d..5278d081 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -135,7 +135,7 @@ class MultiIndex(Index): def take( self, indices, axis: int = ..., allow_fill: bool = ..., fill_value=..., **kwargs ): ... - def append(self, other): ... + def append(self, other): ... # pyrefly: ignore def argsort(self, *args, **kwargs): ... def repeat(self, repeats, axis=...): ... @final From 3844062b0df37d810c10744b226d2aa9641c4286 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sat, 19 Jul 2025 20:05:25 +0200 Subject: [PATCH 5/5] fix(comment): https://github.com/pandas-dev/pandas-stubs/pull/1282#discussion_r2217319354 --- pandas-stubs/core/indexes/base.pyi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index abb327b9..0480ac05 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -15,6 +15,7 @@ from typing import ( ClassVar, Literal, TypeAlias, + TypeVar, final, overload, ) @@ -65,6 +66,8 @@ from pandas._typing import ( type_t, ) +_T_INDEX = TypeVar("_T_INDEX", bound=Index) # ty: ignore[unresolved-reference] + class InvalidIndexError(Exception): ... class Index(IndexOpsMixin[S1]): @@ -407,6 +410,8 @@ class Index(IndexOpsMixin[S1]): @overload def append(self, other: Index[S2] | Sequence[Index[S2]]) -> Index[S1 | S2]: ... @overload + def append(self, other: Sequence[_T_INDEX]) -> Self | _T_INDEX: ... + @overload def append(self, other: Index | Sequence) -> Index: ... def putmask(self, mask, value): ... def equals(self, other) -> bool: ...