Skip to content

Commit fafbcbd

Browse files
[backport 2.3.x] BUG(CoW): also raise for chained assignment for .at / .iat (#62074) (#62115)
1 parent 3ac64a7 commit fafbcbd

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

pandas/core/indexing.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,22 @@ def __getitem__(self, key):
25752575
return super().__getitem__(key)
25762576

25772577
def __setitem__(self, key, value) -> None:
2578+
if not PYPY and using_copy_on_write():
2579+
if sys.getrefcount(self.obj) <= 2:
2580+
warnings.warn(
2581+
_chained_assignment_msg, ChainedAssignmentError, stacklevel=2
2582+
)
2583+
elif not PYPY and not using_copy_on_write():
2584+
ctr = sys.getrefcount(self.obj)
2585+
ref_count = 2
2586+
if not warn_copy_on_write() and _check_cacher(self.obj):
2587+
# see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221
2588+
ref_count += 1
2589+
if ctr <= ref_count:
2590+
warnings.warn(
2591+
_chained_assignment_warning_msg, FutureWarning, stacklevel=2
2592+
)
2593+
25782594
if self.ndim == 2 and not self._axes_are_unique:
25792595
# GH#33041 fall back to .loc
25802596
if not isinstance(key, tuple) or not all(is_scalar(x) for x in key):
@@ -2599,6 +2615,25 @@ def _convert_key(self, key):
25992615
raise ValueError("iAt based indexing can only have integer indexers")
26002616
return key
26012617

2618+
def __setitem__(self, key, value) -> None:
2619+
if not PYPY and using_copy_on_write():
2620+
if sys.getrefcount(self.obj) <= 2:
2621+
warnings.warn(
2622+
_chained_assignment_msg, ChainedAssignmentError, stacklevel=2
2623+
)
2624+
elif not PYPY and not using_copy_on_write():
2625+
ctr = sys.getrefcount(self.obj)
2626+
ref_count = 2
2627+
if not warn_copy_on_write() and _check_cacher(self.obj):
2628+
# see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221
2629+
ref_count += 1
2630+
if ctr <= ref_count:
2631+
warnings.warn(
2632+
_chained_assignment_warning_msg, FutureWarning, stacklevel=2
2633+
)
2634+
2635+
return super().__setitem__(key, value)
2636+
26022637

26032638
def _tuplify(ndim: int, loc: Hashable) -> tuple[Hashable | slice, ...]:
26042639
"""

pandas/tests/copy_view/test_chained_assignment_deprecation.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,83 @@ def test_frame_setitem(indexer, using_copy_on_write):
172172
with option_context("chained_assignment", "warn"):
173173
with tm.raises_chained_assignment_error(extra_warnings=extra_warnings):
174174
df[0:3][indexer] = 10
175+
176+
177+
@pytest.mark.parametrize(
178+
"indexer", [0, [0, 1], slice(0, 2), np.array([True, False, True])]
179+
)
180+
def test_series_iloc_setitem(indexer):
181+
df = DataFrame({"a": [1, 2, 3], "b": 1})
182+
183+
with option_context("chained_assignment", "warn"):
184+
with tm.raises_chained_assignment_error():
185+
df["a"].iloc[indexer] = 0
186+
187+
188+
@pytest.mark.parametrize(
189+
"indexer", [0, [0, 1], slice(0, 2), np.array([True, False, True])]
190+
)
191+
def test_frame_iloc_setitem(indexer, using_copy_on_write):
192+
df = DataFrame({"a": [1, 2, 3, 4, 5], "b": 1})
193+
194+
extra_warnings = () if using_copy_on_write else (SettingWithCopyWarning,)
195+
196+
with option_context("chained_assignment", "warn"):
197+
with tm.raises_chained_assignment_error(extra_warnings=extra_warnings):
198+
df[0:3].iloc[indexer] = 10
199+
200+
201+
@pytest.mark.parametrize(
202+
"indexer", [0, [0, 1], slice(0, 2), np.array([True, False, True])]
203+
)
204+
def test_series_loc_setitem(indexer):
205+
df = DataFrame({"a": [1, 2, 3], "b": 1})
206+
207+
with option_context("chained_assignment", "warn"):
208+
with tm.raises_chained_assignment_error():
209+
df["a"].loc[indexer] = 0
210+
211+
212+
@pytest.mark.parametrize(
213+
"indexer", [0, [0, 1], (0, "a"), slice(0, 2), np.array([True, False, True])]
214+
)
215+
def test_frame_loc_setitem(indexer, using_copy_on_write):
216+
df = DataFrame({"a": [1, 2, 3, 4, 5], "b": 1})
217+
218+
extra_warnings = () if using_copy_on_write else (SettingWithCopyWarning,)
219+
220+
with option_context("chained_assignment", "warn"):
221+
with tm.raises_chained_assignment_error(extra_warnings=extra_warnings):
222+
df[0:3].loc[indexer] = 10
223+
224+
225+
def test_series_at_setitem():
226+
df = DataFrame({"a": [1, 2, 3], "b": 1})
227+
228+
with option_context("chained_assignment", "warn"):
229+
with tm.raises_chained_assignment_error():
230+
df["a"].at[0] = 0
231+
232+
233+
def test_frame_at_setitem():
234+
df = DataFrame({"a": [1, 2, 3, 4, 5], "b": 1})
235+
236+
with option_context("chained_assignment", "warn"):
237+
with tm.raises_chained_assignment_error():
238+
df[0:3].at[0, "a"] = 10
239+
240+
241+
def test_series_iat_setitem():
242+
df = DataFrame({"a": [1, 2, 3], "b": 1})
243+
244+
with option_context("chained_assignment", "warn"):
245+
with tm.raises_chained_assignment_error():
246+
df["a"].iat[0] = 0
247+
248+
249+
def test_frame_iat_setitem():
250+
df = DataFrame({"a": [1, 2, 3, 4, 5], "b": 1})
251+
252+
with option_context("chained_assignment", "warn"):
253+
with tm.raises_chained_assignment_error():
254+
df[0:3].iat[0, 0] = 10

0 commit comments

Comments
 (0)