Skip to content

feat(index): append #1282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pandas-stubs/core/indexes/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,10 @@ 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[S1] | Sequence[Index[S1]]) -> Self: ...
@overload
def append(self, other: Index | Sequence[Index]) -> Index: ...
def putmask(self, mask, value): ...
def equals(self, other) -> bool: ...
@final
Expand Down
2 changes: 1 addition & 1 deletion pandas-stubs/core/indexes/multi.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
48 changes: 48 additions & 0 deletions tests/test_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import datetime as dt
from typing import (
TYPE_CHECKING,
Any,
Union,
cast,
)

import numpy as np
Expand Down Expand Up @@ -1028,6 +1030,52 @@ def test_getitem() -> None:
check(assert_type(i0[[0, 2]], "pd.Index[str]"), pd.Index, str)


def test_append_mix() -> None:
"""Test pd.Index.append with mixed types"""
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( # type: ignore[assert-type]
third.append([]), "pd.Index[int | str]"
),
pd.Index,
)
check(
assert_type( # type: ignore[assert-type]
third.append(cast("list[Index[Any]]", [])), "pd.Index[int | str]"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change these to pd.Index in the assert_type statements, and then I don't think you need the # type: ignore statements., i.e., remove the union part of the generic paramter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added comments in fe5f1a9.

  • We have the following overloads
    @overload
    def append(self, other: Index[S1] | Sequence[Index[S1]]) -> Self: ...
    @overload
    def append(self, other: Index | Sequence[Index]) -> Index: ...
  • pyright uses the first overload and gives Index[str | int]
  • mypy uses the second overload and gives Index[Any]
  • Even if I add the following as the first overload
    @overload
    def append(self, other: Sequence[Never]) -> Self: ...
    mypy still uses the last overload and gives Index[Any]

I tend to believe this is a mypy bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After all, Index(["a", 1]) was created as the type Index[str | int] from the beginning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about 1c75df6? I am trying to get rid of Any by using constraint instead of bound in the TypeVar.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is fine. Have to wonder if we should change Index to be based on C2 instead of S1, but that can be another PR.

),
pd.Index,
)
check(assert_type(third.append([first]), pd.Index), pd.Index)
check(assert_type(third.append([first, second]), 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_range_index_range() -> None:
"""Test that pd.RangeIndex can be initialized from range."""
iri = pd.RangeIndex(range(5))
Expand Down
Loading