From df7a868fcdbd0c1babf40533733be97aa6fcbf94 Mon Sep 17 00:00:00 2001 From: Niranjan Krishna Date: Sat, 10 May 2025 21:59:53 +0530 Subject: [PATCH 1/3] Fix Series._slice to free memory by copying underlying data --- pandas/core/series.py | 2 +- pandas/tests/series/methods/test_slice.py | 27 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 pandas/tests/series/methods/test_slice.py diff --git a/pandas/core/series.py b/pandas/core/series.py index 5ed094349caaa..91926a53a8559 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -928,7 +928,7 @@ def _slice(self, slobj: slice, axis: AxisInt = 0) -> Series: # axis kwarg is retained for compat with NDFrame method # _slice is *always* positional mgr = self._mgr.get_slice(slobj, axis=axis) - out = self._constructor_from_mgr(mgr, axes=mgr.axes) + out = self._constructor_from_mgr(mgr, axes=mgr.axes).copy(deep=True) out._name = self._name return out.__finalize__(self) diff --git a/pandas/tests/series/methods/test_slice.py b/pandas/tests/series/methods/test_slice.py new file mode 100644 index 0000000000000..f5b920acb149b --- /dev/null +++ b/pandas/tests/series/methods/test_slice.py @@ -0,0 +1,27 @@ +import gc +import pandas as pd +from pandas import Series + + +class Dummy: + def __init__(self, val): + self.val = val + + +def count_dummy_instances(): + gc.collect() + return sum(1 for obj in gc.get_objects() if isinstance(obj, Dummy)) + + +def test_slicing_releases_dummy_instances(): + """Ensure that slicing a Series does not retain references to all original Dummy instances.""" + NDATA = 100_000 + baseline = count_dummy_instances() + a = Series([Dummy(i) for i in range(NDATA)]) + a = a[-1:] + gc.collect() + after = count_dummy_instances() + retained = after - baseline + assert ( + retained <= 1 + ), f"{retained} Dummy instances were retained; expected at most 1" From cd96abf179cd88c7e8a3344bbd944ffffc309fa6 Mon Sep 17 00:00:00 2001 From: Niranjan Krishna Date: Sat, 10 May 2025 22:15:45 +0530 Subject: [PATCH 2/3] Reduced docstring length to comply with formatting --- pandas/tests/series/methods/test_slice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/series/methods/test_slice.py b/pandas/tests/series/methods/test_slice.py index f5b920acb149b..3b4ca12a54ed3 100644 --- a/pandas/tests/series/methods/test_slice.py +++ b/pandas/tests/series/methods/test_slice.py @@ -14,7 +14,7 @@ def count_dummy_instances(): def test_slicing_releases_dummy_instances(): - """Ensure that slicing a Series does not retain references to all original Dummy instances.""" + """Ensure slicing a Series does not retain references to original Dummy instances.""" NDATA = 100_000 baseline = count_dummy_instances() a = Series([Dummy(i) for i in range(NDATA)]) From e787447c640f192fd128f12bb054ced7b158519d Mon Sep 17 00:00:00 2001 From: Niranjan Krishna Date: Sat, 10 May 2025 22:21:30 +0530 Subject: [PATCH 3/3] Apply formatting fixes to comply with Ruff and isort --- pandas/tests/series/methods/test_slice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/tests/series/methods/test_slice.py b/pandas/tests/series/methods/test_slice.py index 3b4ca12a54ed3..459696f8f20b7 100644 --- a/pandas/tests/series/methods/test_slice.py +++ b/pandas/tests/series/methods/test_slice.py @@ -1,5 +1,5 @@ import gc -import pandas as pd + from pandas import Series @@ -14,7 +14,7 @@ def count_dummy_instances(): def test_slicing_releases_dummy_instances(): - """Ensure slicing a Series does not retain references to original Dummy instances.""" + """Ensure Series slicing does not retain references to original Dummy data.""" NDATA = 100_000 baseline = count_dummy_instances() a = Series([Dummy(i) for i in range(NDATA)]) @@ -22,6 +22,6 @@ def test_slicing_releases_dummy_instances(): gc.collect() after = count_dummy_instances() retained = after - baseline - assert ( - retained <= 1 - ), f"{retained} Dummy instances were retained; expected at most 1" + assert retained <= 1, ( + f"{retained} Dummy instances were retained; expected at most 1" + )