From 516510275b921b3fe01c67f8bb5ac00971a65883 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Mon, 4 Dec 2023 09:29:32 +0100 Subject: [PATCH] CoW warning mode: enable chained assignment warning for DataFrame setitem in default mode (#56230) --- pandas/core/frame.py | 17 +++++++------ .../test_chained_assignment_deprecation.py | 24 +++++++++++++++++-- .../tests/indexing/multiindex/test_setitem.py | 6 +++-- .../indexing/test_chaining_and_caching.py | 3 ++- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 008c1e0d10ba4..847f514451add 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4230,15 +4230,14 @@ def __setitem__(self, key, value) -> None: warnings.warn( _chained_assignment_msg, ChainedAssignmentError, stacklevel=2 ) - # elif not PYPY and not using_copy_on_write(): - elif not PYPY and warn_copy_on_write(): - if sys.getrefcount(self) <= 3: # and ( - # warn_copy_on_write() - # or ( - # not warn_copy_on_write() - # and self._mgr.blocks[0].refs.has_reference() - # ) - # ): + elif not PYPY and not using_copy_on_write(): + if sys.getrefcount(self) <= 3 and ( + warn_copy_on_write() + or ( + not warn_copy_on_write() + and any(b.refs.has_reference() for b in self._mgr.blocks) # type: ignore[union-attr] + ) + ): warnings.warn( _chained_assignment_warning_msg, FutureWarning, stacklevel=2 ) diff --git a/pandas/tests/copy_view/test_chained_assignment_deprecation.py b/pandas/tests/copy_view/test_chained_assignment_deprecation.py index 7b08d9b80fc9b..32d0f55f67185 100644 --- a/pandas/tests/copy_view/test_chained_assignment_deprecation.py +++ b/pandas/tests/copy_view/test_chained_assignment_deprecation.py @@ -1,9 +1,15 @@ import numpy as np import pytest -from pandas.errors import ChainedAssignmentError +from pandas.errors import ( + ChainedAssignmentError, + SettingWithCopyWarning, +) -from pandas import DataFrame +from pandas import ( + DataFrame, + option_context, +) import pandas._testing as tm @@ -85,3 +91,17 @@ def test_series_setitem(indexer, using_copy_on_write): else: assert record[0].category == FutureWarning assert "ChainedAssignmentError" in record[0].message.args[0] + + +@pytest.mark.filterwarnings("ignore::pandas.errors.SettingWithCopyWarning") +@pytest.mark.parametrize( + "indexer", ["a", ["a", "b"], slice(0, 2), np.array([True, False, True])] +) +def test_frame_setitem(indexer, using_copy_on_write): + df = DataFrame({"a": [1, 2, 3, 4, 5], "b": 1}) + + extra_warnings = () if using_copy_on_write else (SettingWithCopyWarning,) + + with option_context("chained_assignment", "warn"): + with tm.raises_chained_assignment_error(extra_warnings=extra_warnings): + df[0:3][indexer] = 10 diff --git a/pandas/tests/indexing/multiindex/test_setitem.py b/pandas/tests/indexing/multiindex/test_setitem.py index e613f1102b03b..53ad4d6b41687 100644 --- a/pandas/tests/indexing/multiindex/test_setitem.py +++ b/pandas/tests/indexing/multiindex/test_setitem.py @@ -548,7 +548,8 @@ def test_frame_setitem_copy_raises( else: msg = "A value is trying to be set on a copy of a slice from a DataFrame" with pytest.raises(SettingWithCopyError, match=msg): - df["foo"]["one"] = 2 + with tm.raises_chained_assignment_error(): + df["foo"]["one"] = 2 def test_frame_setitem_copy_no_write( @@ -563,7 +564,8 @@ def test_frame_setitem_copy_no_write( else: msg = "A value is trying to be set on a copy of a slice from a DataFrame" with pytest.raises(SettingWithCopyError, match=msg): - df["foo"]["one"] = 2 + with tm.raises_chained_assignment_error(): + df["foo"]["one"] = 2 result = df tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/indexing/test_chaining_and_caching.py b/pandas/tests/indexing/test_chaining_and_caching.py index 230c575e6ed10..88cac9b16f8f7 100644 --- a/pandas/tests/indexing/test_chaining_and_caching.py +++ b/pandas/tests/indexing/test_chaining_and_caching.py @@ -452,7 +452,8 @@ def test_detect_chained_assignment_undefined_column( df.iloc[0:5]["group"] = "a" else: with pytest.raises(SettingWithCopyError, match=msg): - df.iloc[0:5]["group"] = "a" + with tm.raises_chained_assignment_error(): + df.iloc[0:5]["group"] = "a" @pytest.mark.arm_slow def test_detect_chained_assignment_changing_dtype(