Skip to content

Commit 6e27efc

Browse files
authored
BUG: Arithmetic inplace ops not respecting CoW (#51403)
1 parent fbefd52 commit 6e27efc

File tree

5 files changed

+42
-7
lines changed

5 files changed

+42
-7
lines changed

doc/source/whatsnew/v2.0.0.rst

+3
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ Copy-on-Write improvements
259259
- :meth:`DataFrame.replace` will now respect the Copy-on-Write mechanism
260260
when ``inplace=True``.
261261

262+
- Arithmetic operations that can be inplace, e.g. ``ser *= 2`` will now respect the
263+
Copy-on-Write mechanism.
264+
262265
Copy-on-Write can be enabled through one of
263266

264267
.. code-block:: python

pandas/core/generic.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -11737,7 +11737,11 @@ def _inplace_method(self, other, op):
1173711737
and is_dtype_equal(result.dtype, self.dtype)
1173811738
):
1173911739
# GH#36498 this inplace op can _actually_ be inplace.
11740-
self._values[:] = result._values
11740+
# Item "ArrayManager" of "Union[ArrayManager, SingleArrayManager,
11741+
# BlockManager, SingleBlockManager]" has no attribute "setitem_inplace"
11742+
self._mgr.setitem_inplace( # type: ignore[union-attr]
11743+
slice(None), result._values
11744+
)
1174111745
return self
1174211746

1174311747
# Delete cacher

pandas/tests/copy_view/test_methods.py

+20
Original file line numberDiff line numberDiff line change
@@ -1479,3 +1479,23 @@ def test_xs_multiindex(using_copy_on_write, using_array_manager, key, level, axi
14791479
result.iloc[0, 0] = 0
14801480

14811481
tm.assert_frame_equal(df, df_orig)
1482+
1483+
1484+
def test_inplace_arithmetic_series():
1485+
ser = Series([1, 2, 3])
1486+
data = get_array(ser)
1487+
ser *= 2
1488+
assert np.shares_memory(get_array(ser), data)
1489+
tm.assert_numpy_array_equal(data, get_array(ser))
1490+
1491+
1492+
def test_inplace_arithmetic_series_with_reference(using_copy_on_write):
1493+
ser = Series([1, 2, 3])
1494+
ser_orig = ser.copy()
1495+
view = ser[:]
1496+
ser *= 2
1497+
if using_copy_on_write:
1498+
assert not np.shares_memory(get_array(ser), get_array(view))
1499+
tm.assert_series_equal(ser_orig, view)
1500+
else:
1501+
assert np.shares_memory(get_array(ser), get_array(view))

pandas/tests/frame/test_arithmetic.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -1988,17 +1988,22 @@ def test_arith_list_of_arraylike_raise(to_add):
19881988
to_add + df
19891989

19901990

1991-
def test_inplace_arithmetic_series_update():
1991+
def test_inplace_arithmetic_series_update(using_copy_on_write):
19921992
# https://github.com/pandas-dev/pandas/issues/36373
19931993
df = DataFrame({"A": [1, 2, 3]})
1994+
df_orig = df.copy()
19941995
series = df["A"]
19951996
vals = series._values
19961997

19971998
series += 1
1998-
assert series._values is vals
1999+
if using_copy_on_write:
2000+
assert series._values is not vals
2001+
tm.assert_frame_equal(df, df_orig)
2002+
else:
2003+
assert series._values is vals
19992004

2000-
expected = DataFrame({"A": [2, 3, 4]})
2001-
tm.assert_frame_equal(df, expected)
2005+
expected = DataFrame({"A": [2, 3, 4]})
2006+
tm.assert_frame_equal(df, expected)
20022007

20032008

20042009
def test_arithemetic_multiindex_align():

pandas/tests/indexing/test_loc.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1105,9 +1105,12 @@ def test_identity_slice_returns_new_object(self, using_copy_on_write):
11051105
else:
11061106
assert all(sliced_series[:3] == [7, 8, 9])
11071107

1108-
@pytest.mark.xfail(reason="accidental fix reverted - GH37497")
1109-
def test_loc_copy_vs_view(self):
1108+
def test_loc_copy_vs_view(self, request, using_copy_on_write):
11101109
# GH 15631
1110+
1111+
if not using_copy_on_write:
1112+
mark = pytest.mark.xfail(reason="accidental fix reverted - GH37497")
1113+
request.node.add_marker(mark)
11111114
x = DataFrame(zip(range(3), range(3)), columns=["a", "b"])
11121115

11131116
y = x.copy()

0 commit comments

Comments
 (0)