From ec7e1acab592be0e4a74c71ccea06c2ab54b4ccf Mon Sep 17 00:00:00 2001 From: Gioia Ballin Date: Fri, 8 Feb 2019 15:15:55 +0000 Subject: [PATCH 1/4] BUG: Fix regression in DataFrame.apply causing RecursionError --- pandas/core/dtypes/inference.py | 13 ++++++++----- pandas/tests/dtypes/test_inference.py | 8 +++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pandas/core/dtypes/inference.py b/pandas/core/dtypes/inference.py index 92972c83cea53..1a02623fa6072 100644 --- a/pandas/core/dtypes/inference.py +++ b/pandas/core/dtypes/inference.py @@ -397,12 +397,15 @@ def is_dict_like(obj): True >>> is_dict_like([1, 2, 3]) False + >>> is_dict_like(dict) + False + >>> is_dict_like(dict()) + True """ - for attr in ("__getitem__", "keys", "__contains__"): - if not hasattr(obj, attr): - return False - - return True + dict_like_attrs = ("__getitem__", "keys", "__contains__") + return (all(hasattr(obj, attr) for attr in dict_like_attrs) + # [GH 25196] exclude classes + and not isinstance(obj, type)) def is_named_tuple(obj): diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 89662b70a39ad..1a85befb151de 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -159,13 +159,15 @@ def test_is_nested_list_like_fails(obj): @pytest.mark.parametrize( - "ll", [{}, {'A': 1}, Series([1])]) + "ll", [{}, {'A': 1}, Series([1]), collections.defaultdict()]) def test_is_dict_like_passes(ll): assert inference.is_dict_like(ll) -@pytest.mark.parametrize( - "ll", ['1', 1, [1, 2], (1, 2), range(2), Index([1])]) +@pytest.mark.parametrize("ll", [ + '1', 1, [1, 2], (1, 2), range(2), Index([1]), + dict, collections.defaultdict +]) def test_is_dict_like_fails(ll): assert not inference.is_dict_like(ll) From 69ffb30da770f7e03b68a0525d1d48e0b73544bd Mon Sep 17 00:00:00 2001 From: Gioia Ballin Date: Fri, 8 Feb 2019 16:19:58 +0000 Subject: [PATCH 2/4] Add feedback from PR --- doc/source/whatsnew/v0.24.2.rst | 2 ++ pandas/tests/dtypes/test_inference.py | 2 +- pandas/tests/frame/test_apply.py | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index 73df504c89d5b..e4737a2b4b8f6 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -24,6 +24,8 @@ Fixed Regressions - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) +- Fixed regression in :meth:`DataFrame.apply` causing RecursionError when classes like ``dict`` were passed as argument. (:issue:`25196`) + .. _whatsnew_0242.enhancements: Enhancements diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py index 1a85befb151de..49a66efaffc11 100644 --- a/pandas/tests/dtypes/test_inference.py +++ b/pandas/tests/dtypes/test_inference.py @@ -166,7 +166,7 @@ def test_is_dict_like_passes(ll): @pytest.mark.parametrize("ll", [ '1', 1, [1, 2], (1, 2), range(2), Index([1]), - dict, collections.defaultdict + dict, collections.defaultdict, Series ]) def test_is_dict_like_fails(ll): assert not inference.is_dict_like(ll) diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index ade527a16c902..64ab95719bd04 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -318,6 +318,11 @@ def test_apply_reduce_Series(self, float_frame): result = float_frame.apply(np.mean, axis=1) assert_series_equal(result, expected) + def test_apply_reduce_rows_to_dict(self): + data = pd.DataFrame([[1, 2], [3, 4]]) + expected = pd.Series([{0: 1, 1: 3}, {0: 2, 1: 4}]) + assert_series_equal(data.apply(dict), expected) + def test_apply_differently_indexed(self): df = DataFrame(np.random.randn(20, 10)) From eb0b08e366b0f69b72fcfde6008fdc65a9126a65 Mon Sep 17 00:00:00 2001 From: Gioia Ballin Date: Fri, 8 Feb 2019 18:51:02 +0000 Subject: [PATCH 3/4] Add feedback after further code review --- doc/source/whatsnew/v0.24.2.rst | 2 +- pandas/tests/frame/test_apply.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index e4737a2b4b8f6..c3f8ceaabc659 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -24,7 +24,7 @@ Fixed Regressions - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) -- Fixed regression in :meth:`DataFrame.apply` causing RecursionError when classes like ``dict`` were passed as argument. (:issue:`25196`) +- Fixed regression in :meth:`DataFrame.apply` causing RecursionError when ``dict`` or ``Series`` were passed as argument. (:issue:`25196`) .. _whatsnew_0242.enhancements: diff --git a/pandas/tests/frame/test_apply.py b/pandas/tests/frame/test_apply.py index 64ab95719bd04..a4cd1aa3bacb6 100644 --- a/pandas/tests/frame/test_apply.py +++ b/pandas/tests/frame/test_apply.py @@ -319,9 +319,11 @@ def test_apply_reduce_Series(self, float_frame): assert_series_equal(result, expected) def test_apply_reduce_rows_to_dict(self): + # GH 25196 data = pd.DataFrame([[1, 2], [3, 4]]) expected = pd.Series([{0: 1, 1: 3}, {0: 2, 1: 4}]) - assert_series_equal(data.apply(dict), expected) + result = data.apply(dict) + assert_series_equal(result, expected) def test_apply_differently_indexed(self): df = DataFrame(np.random.randn(20, 10)) From 3a16481ba46fedf43680e27d56d8e2b2058d2815 Mon Sep 17 00:00:00 2001 From: Gioia Ballin Date: Fri, 8 Feb 2019 18:54:41 +0000 Subject: [PATCH 4/4] Add feedback after further code review 2 --- doc/source/whatsnew/v0.24.2.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.24.2.rst b/doc/source/whatsnew/v0.24.2.rst index c3f8ceaabc659..23d993923e697 100644 --- a/doc/source/whatsnew/v0.24.2.rst +++ b/doc/source/whatsnew/v0.24.2.rst @@ -21,10 +21,8 @@ Fixed Regressions ^^^^^^^^^^^^^^^^^ - Fixed regression in :meth:`DataFrame.all` and :meth:`DataFrame.any` where ``bool_only=True`` was ignored (:issue:`25101`) - - Fixed issue in ``DataFrame`` construction with passing a mixed list of mixed types could segfault. (:issue:`25075`) - -- Fixed regression in :meth:`DataFrame.apply` causing RecursionError when ``dict`` or ``Series`` were passed as argument. (:issue:`25196`) +- Fixed regression in :meth:`DataFrame.apply` causing ``RecursionError`` when ``dict``-like classes were passed as argument. (:issue:`25196`) .. _whatsnew_0242.enhancements: