From da808155495741cef07fe53c72dc1d9632ae5160 Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Fri, 29 Mar 2024 23:02:40 +0100 Subject: [PATCH 1/7] tentative solution for #896 --- pandas-stubs/core/frame.pyi | 15 ++++++++++++--- tests/test_frame.py | 13 +++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 7ce2f5d61..0adf4e005 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -272,13 +272,22 @@ class DataFrame(NDFrame, OpsMixin): def dot(self, other: Series) -> Series: ... def __matmul__(self, other): ... def __rmatmul__(self, other): ... + @overload + @classmethod + def from_dict( + cls, + data: dict[Any, Any], + orient: Literal["index"] = ..., + dtype: AstypeArg | None = ..., + columns: Axes | None = ..., + ) -> DataFrame: ... + @overload @classmethod def from_dict( cls, data: dict[Any, Any], - orient: Literal["columns", "index", "tight"] = ..., - dtype: _str = ..., - columns: list[_str] = ..., + orient: Literal["columns", "tight"] = ..., + dtype: AstypeArg | None = ..., ) -> DataFrame: ... def to_numpy( self, diff --git a/tests/test_frame.py b/tests/test_frame.py index af3183b93..e1010e15c 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1457,6 +1457,19 @@ def test_types_from_dict() -> None: }, orient="tight", ) + # added following #896 + data = {"l1": [1, 2, 3], "l2": [4, 5, 6]} + # testing `dtype` + pd.DataFrame.from_dict(data, orient="index", dtype="float") + pd.DataFrame.from_dict(data, orient="index", dtype=float) + pd.DataFrame.from_dict(data, orient="index", dtype=None) + # testing `columns` + pd.DataFrame.from_dict(data, orient="index", columns=["a", "b", "c"]) + pd.DataFrame.from_dict( + data, orient="index", columns=[1.0, 2, datetime.datetime.now()] + ) + # with pytest.raises(ValueError): + # pd.DataFrame.from_dict(data, orient='columns', columns=['a', 'b', 'c']) def test_pipe() -> None: From b4c85be1df85ab02396d19610ca16286089a2c11 Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Fri, 29 Mar 2024 23:13:10 +0100 Subject: [PATCH 2/7] remove Unnecessary "# pyright: ignore" rule error --- pandas-stubs/_libs/tslibs/timestamps.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/_libs/tslibs/timestamps.pyi b/pandas-stubs/_libs/tslibs/timestamps.pyi index af856ae6f..9df14b3d4 100644 --- a/pandas-stubs/_libs/tslibs/timestamps.pyi +++ b/pandas-stubs/_libs/tslibs/timestamps.pyi @@ -219,7 +219,7 @@ class Timestamp(datetime): @overload def __sub__(self, other: TimedeltaSeries) -> TimestampSeries: ... @overload - def __sub__( # pyright: ignore[reportIncompatibleMethodOverride] + def __sub__( self, other: npt.NDArray[np.timedelta64] ) -> npt.NDArray[np.datetime64]: ... @overload From 050c6f9f937b7becb2888387e0b9c271c1a3358c Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Fri, 29 Mar 2024 23:20:50 +0100 Subject: [PATCH 3/7] improving dataframe.from_dict test --- tests/test_frame.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_frame.py b/tests/test_frame.py index e1010e15c..c51fe589c 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1468,8 +1468,8 @@ def test_types_from_dict() -> None: pd.DataFrame.from_dict( data, orient="index", columns=[1.0, 2, datetime.datetime.now()] ) - # with pytest.raises(ValueError): - # pd.DataFrame.from_dict(data, orient='columns', columns=['a', 'b', 'c']) + with pytest.raises(ValueError): + pd.DataFrame.from_dict(data, orient="columns", columns=["a", "b", "c"]) # type: ignore[call-overload] # pyright: ignore[reportArgumentType] def test_pipe() -> None: From 729bdb5f819cff22a3abd342741275cd839fd8e2 Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Mon, 1 Apr 2024 22:13:14 +0200 Subject: [PATCH 4/7] check(assert_type every test --- tests/test_frame.py | 124 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 24 deletions(-) diff --git a/tests/test_frame.py b/tests/test_frame.py index c51fe589c..099d608b1 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1438,38 +1438,114 @@ def test_types_to_dict() -> None: def test_types_from_dict() -> None: - pd.DataFrame.from_dict({"col_1": [3, 2, 1, 0], "col_2": ["a", "b", "c", "d"]}) - pd.DataFrame.from_dict({1: [3, 2, 1, 0], 2: ["a", "b", "c", "d"]}) - pd.DataFrame.from_dict({"a": {1: 2}, "b": {3: 4, 1: 4}}, orient="index") - pd.DataFrame.from_dict({"a": {"row1": 2}, "b": {"row2": 4, "row1": 4}}) - pd.DataFrame.from_dict({"a": (1, 2, 3), "b": (2, 4, 5)}) - pd.DataFrame.from_dict( - data={"col_1": {"a": 1}, "col_2": {"a": 1, "b": 2}}, orient="columns" + check( + assert_type( + pd.DataFrame.from_dict( + {"col_1": [3, 2, 1, 0], "col_2": ["a", "b", "c", "d"]} + ), + pd.DataFrame, + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict({1: [3, 2, 1, 0], 2: ["a", "b", "c", "d"]}), + pd.DataFrame, + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict({"a": {1: 2}, "b": {3: 4, 1: 4}}, orient="index"), + pd.DataFrame, + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict({"a": {"row1": 2}, "b": {"row2": 4, "row1": 4}}), + pd.DataFrame, + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict({"a": (1, 2, 3), "b": (2, 4, 5)}), pd.DataFrame + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict( + data={"col_1": {"a": 1}, "col_2": {"a": 1, "b": 2}}, orient="columns" + ), + pd.DataFrame, + ), + pd.DataFrame, ) # orient param accepting "tight" added in 1.4.0 https://pandas.pydata.org/docs/whatsnew/v1.4.0.html - pd.DataFrame.from_dict( - data={ - "index": [("a", "b"), ("a", "c")], - "columns": [("x", 1), ("y", 2)], - "data": [[1, 3], [2, 4]], - "index_names": ["n1", "n2"], - "column_names": ["z1", "z2"], - }, - orient="tight", + check( + assert_type( + pd.DataFrame.from_dict( + data={ + "index": [("a", "b"), ("a", "c")], + "columns": [("x", 1), ("y", 2)], + "data": [[1, 3], [2, 4]], + "index_names": ["n1", "n2"], + "column_names": ["z1", "z2"], + }, + orient="tight", + ), + pd.DataFrame, + ), + pd.DataFrame, ) # added following #896 data = {"l1": [1, 2, 3], "l2": [4, 5, 6]} # testing `dtype` - pd.DataFrame.from_dict(data, orient="index", dtype="float") - pd.DataFrame.from_dict(data, orient="index", dtype=float) - pd.DataFrame.from_dict(data, orient="index", dtype=None) + check( + assert_type( + pd.DataFrame.from_dict(data, orient="index", dtype="float"), pd.DataFrame + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict(data, orient="index", dtype=float), pd.DataFrame + ), + pd.DataFrame, + ) + check( + assert_type( + pd.DataFrame.from_dict(data, orient="index", dtype=None), pd.DataFrame + ), + pd.DataFrame, + ) # testing `columns` - pd.DataFrame.from_dict(data, orient="index", columns=["a", "b", "c"]) - pd.DataFrame.from_dict( - data, orient="index", columns=[1.0, 2, datetime.datetime.now()] + check( + assert_type( + pd.DataFrame.from_dict(data, orient="index", columns=["a", "b", "c"]), + pd.DataFrame, + ), + pd.DataFrame, ) - with pytest.raises(ValueError): - pd.DataFrame.from_dict(data, orient="columns", columns=["a", "b", "c"]) # type: ignore[call-overload] # pyright: ignore[reportArgumentType] + check( + assert_type( + pd.DataFrame.from_dict( + data, orient="index", columns=[1.0, 2, datetime.datetime.now()] + ), + pd.DataFrame, + ), + pd.DataFrame, + ) + if TYPE_CHECKING_INVALID_USAGE: + check( + assert_type( # type: ignore[assert-type] + pd.DataFrame.from_dict(data, orient="columns", columns=["a", "b", "c"]), # type: ignore[call-overload] # pyright: ignore[reportArgumentType] + pd.DataFrame, + ), + pd.DataFrame, + ) def test_pipe() -> None: From bc05adef9112340636940500d5a0b4ab66f80c4d Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Mon, 1 Apr 2024 22:17:52 +0200 Subject: [PATCH 5/7] remove unwanted ellipsis --- pandas-stubs/core/frame.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 0adf4e005..44b3a080e 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -277,7 +277,7 @@ class DataFrame(NDFrame, OpsMixin): def from_dict( cls, data: dict[Any, Any], - orient: Literal["index"] = ..., + orient: Literal["index"], dtype: AstypeArg | None = ..., columns: Axes | None = ..., ) -> DataFrame: ... From f760452fc45cd3f2454adfadd26741ffecc5da7b Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Mon, 1 Apr 2024 22:33:20 +0200 Subject: [PATCH 6/7] Adding overload and test to cover when litteral is not used --- pandas-stubs/core/frame.pyi | 8 ++++++++ tests/test_frame.py | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 44b3a080e..401885314 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -289,6 +289,14 @@ class DataFrame(NDFrame, OpsMixin): orient: Literal["columns", "tight"] = ..., dtype: AstypeArg | None = ..., ) -> DataFrame: ... + @overload + @classmethod + def from_dict( + cls, + data: dict[Any, Any], + orient: str, + dtype: AstypeArg | None = ..., + ) -> DataFrame: ... def to_numpy( self, dtype: npt.DTypeLike | None = ..., diff --git a/tests/test_frame.py b/tests/test_frame.py index 099d608b1..813499578 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1546,6 +1546,15 @@ def test_types_from_dict() -> None: ), pd.DataFrame, ) + # check when not using Literal + orient_str = "index" + check( + assert_type( + pd.DataFrame.from_dict(data, orient=orient_str), + pd.DataFrame, + ), + pd.DataFrame, + ) def test_pipe() -> None: From 3b6f9b6a9a15a2d78056c0febca02919e62f37f9 Mon Sep 17 00:00:00 2001 From: Laurent Mutricy Date: Mon, 1 Apr 2024 23:44:18 +0200 Subject: [PATCH 7/7] Revert "Adding overload and test to cover when litteral is not used" This reverts commit f760452fc45cd3f2454adfadd26741ffecc5da7b. --- pandas-stubs/core/frame.pyi | 8 -------- tests/test_frame.py | 9 --------- 2 files changed, 17 deletions(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 401885314..44b3a080e 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -289,14 +289,6 @@ class DataFrame(NDFrame, OpsMixin): orient: Literal["columns", "tight"] = ..., dtype: AstypeArg | None = ..., ) -> DataFrame: ... - @overload - @classmethod - def from_dict( - cls, - data: dict[Any, Any], - orient: str, - dtype: AstypeArg | None = ..., - ) -> DataFrame: ... def to_numpy( self, dtype: npt.DTypeLike | None = ..., diff --git a/tests/test_frame.py b/tests/test_frame.py index 813499578..099d608b1 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1546,15 +1546,6 @@ def test_types_from_dict() -> None: ), pd.DataFrame, ) - # check when not using Literal - orient_str = "index" - check( - assert_type( - pd.DataFrame.from_dict(data, orient=orient_str), - pd.DataFrame, - ), - pd.DataFrame, - ) def test_pipe() -> None: