From 9aefaa4e511e3e3c806a3c1a90ab04a815715035 Mon Sep 17 00:00:00 2001 From: Alex Glass Date: Thu, 8 Feb 2024 11:21:22 -0500 Subject: [PATCH 1/4] Fix pd.concat to accept None values as input. --- pandas-stubs/core/reshape/concat.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas-stubs/core/reshape/concat.pyi b/pandas-stubs/core/reshape/concat.pyi index f684a63f5..0238def1b 100644 --- a/pandas-stubs/core/reshape/concat.pyi +++ b/pandas-stubs/core/reshape/concat.pyi @@ -24,7 +24,7 @@ from pandas._typing import ( @overload def concat( - objs: Iterable[DataFrame] | Mapping[HashableT1, DataFrame], + objs: Iterable[DataFrame | None] | Mapping[HashableT1, DataFrame], *, axis: AxisIndex = ..., join: Literal["inner", "outer"] = ..., @@ -38,7 +38,7 @@ def concat( ) -> DataFrame: ... @overload def concat( - objs: Iterable[Series] | Mapping[HashableT1, Series], + objs: Iterable[Series | None] | Mapping[HashableT1, Series], *, axis: AxisIndex = ..., join: Literal["inner", "outer"] = ..., @@ -52,7 +52,7 @@ def concat( ) -> Series: ... @overload def concat( - objs: Iterable[Series | DataFrame] | Mapping[HashableT1, Series | DataFrame], + objs: Iterable[Series | DataFrame | None] | Mapping[HashableT1, Series | DataFrame], *, axis: AxisColumn, join: Literal["inner", "outer"] = ..., From 574caf5de47c9f0076d22cf6bf7cb281fc0de45d Mon Sep 17 00:00:00 2001 From: Alex Glass Date: Fri, 9 Feb 2024 19:04:54 -0500 Subject: [PATCH 2/4] Add test --- tests/test_pandas.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_pandas.py b/tests/test_pandas.py index ba154a8fb..76ff49604 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -49,6 +49,21 @@ def test_types_to_datetime() -> None: ) +def test_types_concat_none() -> None: + """Test concatenation with None values.""" + s1: pd.Series | None = None + s2: pd.Series = pd.Series([7, -5, 10]) + + check(assert_type(pd.concat([s1, s2]), pd.Series), pd.Series) + + df1: pd.DataFrame | None = None + df2: pd.DataFrame = pd.DataFrame({"a": [7, -5, 10]}) + + check(assert_type(pd.concat([df1, df2]), pd.DataFrame), pd.DataFrame) + + check(assert_type(pd.concat([s1, df1, df2]), pd.DataFrame), pd.DataFrame) + + def test_types_concat() -> None: s: pd.Series = pd.Series([0, 1, -10]) s2: pd.Series = pd.Series([7, -5, 10]) From 66df23018d885722b0e4702fd5277de96acbe9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20W=C3=B6rtwein?= Date: Sun, 18 Feb 2024 16:44:41 -0500 Subject: [PATCH 3/4] a few changes --- pandas-stubs/core/reshape/concat.pyi | 27 ++++++++++++++++++++++---- tests/test_pandas.py | 29 +++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/pandas-stubs/core/reshape/concat.pyi b/pandas-stubs/core/reshape/concat.pyi index 0238def1b..54de1be32 100644 --- a/pandas-stubs/core/reshape/concat.pyi +++ b/pandas-stubs/core/reshape/concat.pyi @@ -12,8 +12,10 @@ from pandas import ( DataFrame, Series, ) +from typing_extensions import Never from pandas._typing import ( + Axis, AxisColumn, AxisIndex, HashableT1, @@ -24,9 +26,23 @@ from pandas._typing import ( @overload def concat( - objs: Iterable[DataFrame | None] | Mapping[HashableT1, DataFrame], + objs: Iterable[None] | Mapping[HashableT1, None], *, - axis: AxisIndex = ..., + axis: Axis = ..., + join: Literal["inner", "outer"] = ..., + ignore_index: bool = ..., + keys: Iterable[HashableT2] = ..., + levels: Sequence[list[HashableT3] | tuple[HashableT3, ...]] = ..., + names: list[HashableT4] = ..., + verify_integrity: bool = ..., + sort: bool = ..., + copy: bool = ..., +) -> Never: ... +@overload +def concat( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] + objs: Iterable[DataFrame | None] | Mapping[HashableT1, DataFrame | None], + *, + axis: Axis = ..., join: Literal["inner", "outer"] = ..., ignore_index: bool = ..., keys: Iterable[HashableT2] = ..., @@ -38,7 +54,7 @@ def concat( ) -> DataFrame: ... @overload def concat( - objs: Iterable[Series | None] | Mapping[HashableT1, Series], + objs: Iterable[Series | None] | Mapping[HashableT1, Series | None], *, axis: AxisIndex = ..., join: Literal["inner", "outer"] = ..., @@ -52,7 +68,10 @@ def concat( ) -> Series: ... @overload def concat( - objs: Iterable[Series | DataFrame | None] | Mapping[HashableT1, Series | DataFrame], + objs: ( + Iterable[Series | DataFrame | None] + | Mapping[HashableT1, Series | DataFrame | None] + ), *, axis: AxisColumn, join: Literal["inner", "outer"] = ..., diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 76ff49604..fc9712f12 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -17,7 +17,10 @@ # TODO: github.com/pandas-dev/pandas/issues/55023 import pytest -from typing_extensions import assert_type +from typing_extensions import ( + Never, + assert_type, +) from pandas._libs.missing import NAType from pandas._libs.tslibs import NaTType @@ -51,17 +54,25 @@ def test_types_to_datetime() -> None: def test_types_concat_none() -> None: """Test concatenation with None values.""" - s1: pd.Series | None = None - s2: pd.Series = pd.Series([7, -5, 10]) - - check(assert_type(pd.concat([s1, s2]), pd.Series), pd.Series) + series = pd.Series([7, -5, 10]) + df = pd.DataFrame({"a": [7, -5, 10]}) - df1: pd.DataFrame | None = None - df2: pd.DataFrame = pd.DataFrame({"a": [7, -5, 10]}) + check(assert_type(pd.concat([None, series]), pd.Series), pd.Series) + check(assert_type(pd.concat([None, df]), pd.DataFrame), pd.DataFrame) + check( + assert_type(pd.concat([None, series, df], axis=1), pd.DataFrame), pd.DataFrame + ) - check(assert_type(pd.concat([df1, df2]), pd.DataFrame), pd.DataFrame) + check(assert_type(pd.concat({"a": None, "b": series}), pd.Series), pd.Series) + check(assert_type(pd.concat({"a": None, "b": df}), pd.DataFrame), pd.DataFrame) + check( + assert_type(pd.concat({"a": None, "b": series, "c": df}, axis=1), pd.DataFrame), + pd.DataFrame, + ) - check(assert_type(pd.concat([s1, df1, df2]), pd.DataFrame), pd.DataFrame) + if TYPE_CHECKING_INVALID_USAGE: + assert_type(pd.concat({"a": None}), Never) + assert_type(pd.concat([None]), Never) def test_types_concat() -> None: From 21f217556cfee0dcc8eeff068db97b22425f144f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torsten=20W=C3=B6rtwein?= Date: Tue, 20 Feb 2024 21:44:38 -0500 Subject: [PATCH 4/4] comment --- tests/test_pandas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_pandas.py b/tests/test_pandas.py index fc9712f12..71551bbd6 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -71,6 +71,7 @@ def test_types_concat_none() -> None: ) if TYPE_CHECKING_INVALID_USAGE: + # using assert_type as otherwise the second call would not be type-checked assert_type(pd.concat({"a": None}), Never) assert_type(pd.concat([None]), Never)