Skip to content

Commit 0d22488

Browse files
authored
CoW: Avoid warning if temporary object is a copy (#56211)
1 parent dd7c34f commit 0d22488

File tree

8 files changed

+64
-24
lines changed

8 files changed

+64
-24
lines changed

pandas/core/frame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8903,7 +8903,7 @@ def update(
89038903
ChainedAssignmentError,
89048904
stacklevel=2,
89058905
)
8906-
elif not PYPY and not using_copy_on_write():
8906+
elif not PYPY and not using_copy_on_write() and self._is_view_after_cow_rules():
89078907
if sys.getrefcount(self) <= REF_COUNT:
89088908
warnings.warn(
89098909
_chained_assignment_warning_method_msg,

pandas/core/generic.py

+48-8
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,14 @@ def _get_cleaned_column_resolvers(self) -> dict[Hashable, Series]:
668668
def _info_axis(self) -> Index:
669669
return getattr(self, self._info_axis_name)
670670

671+
def _is_view_after_cow_rules(self):
672+
# Only to be used in cases of chained assignment checks, this is a
673+
# simplified check that assumes that either the whole object is a view
674+
# or a copy
675+
if len(self._mgr.blocks) == 0: # type: ignore[union-attr]
676+
return False
677+
return self._mgr.blocks[0].refs.has_reference() # type: ignore[union-attr]
678+
671679
@property
672680
def shape(self) -> tuple[int, ...]:
673681
"""
@@ -7285,7 +7293,11 @@ def fillna(
72857293
ChainedAssignmentError,
72867294
stacklevel=2,
72877295
)
7288-
elif not PYPY and not using_copy_on_write():
7296+
elif (
7297+
not PYPY
7298+
and not using_copy_on_write()
7299+
and self._is_view_after_cow_rules()
7300+
):
72897301
ctr = sys.getrefcount(self)
72907302
ref_count = REF_COUNT
72917303
if isinstance(self, ABCSeries) and _check_cacher(self):
@@ -7567,7 +7579,11 @@ def ffill(
75677579
ChainedAssignmentError,
75687580
stacklevel=2,
75697581
)
7570-
elif not PYPY and not using_copy_on_write():
7582+
elif (
7583+
not PYPY
7584+
and not using_copy_on_write()
7585+
and self._is_view_after_cow_rules()
7586+
):
75717587
ctr = sys.getrefcount(self)
75727588
ref_count = REF_COUNT
75737589
if isinstance(self, ABCSeries) and _check_cacher(self):
@@ -7750,7 +7766,11 @@ def bfill(
77507766
ChainedAssignmentError,
77517767
stacklevel=2,
77527768
)
7753-
elif not PYPY and not using_copy_on_write():
7769+
elif (
7770+
not PYPY
7771+
and not using_copy_on_write()
7772+
and self._is_view_after_cow_rules()
7773+
):
77547774
ctr = sys.getrefcount(self)
77557775
ref_count = REF_COUNT
77567776
if isinstance(self, ABCSeries) and _check_cacher(self):
@@ -7916,7 +7936,11 @@ def replace(
79167936
ChainedAssignmentError,
79177937
stacklevel=2,
79187938
)
7919-
elif not PYPY and not using_copy_on_write():
7939+
elif (
7940+
not PYPY
7941+
and not using_copy_on_write()
7942+
and self._is_view_after_cow_rules()
7943+
):
79207944
ctr = sys.getrefcount(self)
79217945
ref_count = REF_COUNT
79227946
if isinstance(self, ABCSeries) and _check_cacher(self):
@@ -8357,7 +8381,11 @@ def interpolate(
83578381
ChainedAssignmentError,
83588382
stacklevel=2,
83598383
)
8360-
elif not PYPY and not using_copy_on_write():
8384+
elif (
8385+
not PYPY
8386+
and not using_copy_on_write()
8387+
and self._is_view_after_cow_rules()
8388+
):
83618389
ctr = sys.getrefcount(self)
83628390
ref_count = REF_COUNT
83638391
if isinstance(self, ABCSeries) and _check_cacher(self):
@@ -8995,7 +9023,11 @@ def clip(
89959023
ChainedAssignmentError,
89969024
stacklevel=2,
89979025
)
8998-
elif not PYPY and not using_copy_on_write():
9026+
elif (
9027+
not PYPY
9028+
and not using_copy_on_write()
9029+
and self._is_view_after_cow_rules()
9030+
):
89999031
ctr = sys.getrefcount(self)
90009032
ref_count = REF_COUNT
90019033
if isinstance(self, ABCSeries) and hasattr(self, "_cacher"):
@@ -10943,7 +10975,11 @@ def where(
1094310975
ChainedAssignmentError,
1094410976
stacklevel=2,
1094510977
)
10946-
elif not PYPY and not using_copy_on_write():
10978+
elif (
10979+
not PYPY
10980+
and not using_copy_on_write()
10981+
and self._is_view_after_cow_rules()
10982+
):
1094710983
ctr = sys.getrefcount(self)
1094810984
ref_count = REF_COUNT
1094910985
if isinstance(self, ABCSeries) and hasattr(self, "_cacher"):
@@ -11022,7 +11058,11 @@ def mask(
1102211058
ChainedAssignmentError,
1102311059
stacklevel=2,
1102411060
)
11025-
elif not PYPY and not using_copy_on_write():
11061+
elif (
11062+
not PYPY
11063+
and not using_copy_on_write()
11064+
and self._is_view_after_cow_rules()
11065+
):
1102611066
ctr = sys.getrefcount(self)
1102711067
ref_count = REF_COUNT
1102811068
if isinstance(self, ABCSeries) and hasattr(self, "_cacher"):

pandas/core/series.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3580,7 +3580,7 @@ def update(self, other: Series | Sequence | Mapping) -> None:
35803580
ChainedAssignmentError,
35813581
stacklevel=2,
35823582
)
3583-
elif not PYPY and not using_copy_on_write():
3583+
elif not PYPY and not using_copy_on_write() and self._is_view_after_cow_rules():
35843584
ctr = sys.getrefcount(self)
35853585
ref_count = REF_COUNT
35863586
if _check_cacher(self):

pandas/tests/copy_view/test_clip.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ def test_clip_chained_inplace(using_copy_on_write):
9292
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
9393
df["a"].clip(1, 2, inplace=True)
9494

95-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
95+
with tm.assert_produces_warning(None):
9696
with option_context("mode.chained_assignment", None):
9797
df[["a"]].clip(1, 2, inplace=True)
9898

99-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
99+
with tm.assert_produces_warning(None):
100100
with option_context("mode.chained_assignment", None):
101101
df[df["a"] > 1].clip(1, 2, inplace=True)

pandas/tests/copy_view/test_interp_fillna.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,11 @@ def test_fillna_chained_assignment(using_copy_on_write):
367367
df[["a"]].fillna(100, inplace=True)
368368
tm.assert_frame_equal(df, df_orig)
369369
else:
370-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
370+
with tm.assert_produces_warning(None):
371371
with option_context("mode.chained_assignment", None):
372372
df[["a"]].fillna(100, inplace=True)
373373

374-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
374+
with tm.assert_produces_warning(None):
375375
with option_context("mode.chained_assignment", None):
376376
df[df.a > 5].fillna(100, inplace=True)
377377

@@ -395,10 +395,10 @@ def test_interpolate_chained_assignment(using_copy_on_write, func):
395395
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
396396
getattr(df["a"], func)(inplace=True)
397397

398-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
398+
with tm.assert_produces_warning(None):
399399
with option_context("mode.chained_assignment", None):
400400
getattr(df[["a"]], func)(inplace=True)
401401

402-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
402+
with tm.assert_produces_warning(None):
403403
with option_context("mode.chained_assignment", None):
404404
getattr(df[df["a"] > 1], func)(inplace=True)

pandas/tests/copy_view/test_methods.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1562,11 +1562,11 @@ def test_chained_where_mask(using_copy_on_write, func):
15621562
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
15631563
getattr(df["a"], func)(df["a"] > 2, 5, inplace=True)
15641564

1565-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
1565+
with tm.assert_produces_warning(None):
15661566
with option_context("mode.chained_assignment", None):
15671567
getattr(df[["a"]], func)(df["a"] > 2, 5, inplace=True)
15681568

1569-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
1569+
with tm.assert_produces_warning(None):
15701570
with option_context("mode.chained_assignment", None):
15711571
getattr(df[df["a"] > 1], func)(df["a"] > 2, 5, inplace=True)
15721572

@@ -1840,11 +1840,11 @@ def test_update_chained_assignment(using_copy_on_write):
18401840
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
18411841
df["a"].update(ser2)
18421842

1843-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
1843+
with tm.assert_produces_warning(None):
18441844
with option_context("mode.chained_assignment", None):
18451845
df[["a"]].update(ser2.to_frame())
18461846

1847-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
1847+
with tm.assert_produces_warning(None):
18481848
with option_context("mode.chained_assignment", None):
18491849
df[df["a"] > 1].update(ser2.to_frame())
18501850

pandas/tests/copy_view/test_replace.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -401,11 +401,11 @@ def test_replace_chained_assignment(using_copy_on_write):
401401
df[["a"]].replace(1, 100, inplace=True)
402402
tm.assert_frame_equal(df, df_orig)
403403
else:
404-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
404+
with tm.assert_produces_warning(None):
405405
with option_context("mode.chained_assignment", None):
406406
df[["a"]].replace(1, 100, inplace=True)
407407

408-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
408+
with tm.assert_produces_warning(None):
409409
with option_context("mode.chained_assignment", None):
410410
df[df.a > 5].replace(1, 100, inplace=True)
411411

pandas/tests/indexing/multiindex/test_chaining_and_caching.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ def test_detect_chained_assignment(using_copy_on_write, warn_copy_on_write):
3434
with tm.raises_chained_assignment_error():
3535
zed["eyes"]["right"].fillna(value=555, inplace=True)
3636
elif warn_copy_on_write:
37-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
37+
with tm.assert_produces_warning(None):
3838
zed["eyes"]["right"].fillna(value=555, inplace=True)
3939
else:
4040
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
4141
with pytest.raises(SettingWithCopyError, match=msg):
42-
with tm.assert_produces_warning(FutureWarning, match="inplace method"):
42+
with tm.assert_produces_warning(None):
4343
zed["eyes"]["right"].fillna(value=555, inplace=True)
4444

4545

0 commit comments

Comments
 (0)