Skip to content

Commit d98907b

Browse files
sebergcharris
authored andcommitted
BUG: Fix memory leak for subclass slicing
A slice object was created if __getslice__/__setslice__ was used by an inherited subclass (through python or directly) but not decref'd. Closes numpygh-10066
1 parent 3fa0096 commit d98907b

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

numpy/core/src/multiarray/methods.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,7 +2444,7 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args))
24442444
static PyObject *
24452445
array_getslice(PyArrayObject *self, PyObject *args)
24462446
{
2447-
PyObject *start, *stop, *slice;
2447+
PyObject *start, *stop, *slice, *result;
24482448
if (!PyArg_ParseTuple(args, "OO:__getslice__", &start, &stop)) {
24492449
return NULL;
24502450
}
@@ -2455,7 +2455,9 @@ array_getslice(PyArrayObject *self, PyObject *args)
24552455
}
24562456

24572457
/* Deliberately delegate to subclasses */
2458-
return PyObject_GetItem((PyObject *)self, slice);
2458+
result = PyObject_GetItem((PyObject *)self, slice);
2459+
Py_DECREF(slice);
2460+
return result;
24592461
}
24602462

24612463
static PyObject *
@@ -2473,9 +2475,10 @@ array_setslice(PyArrayObject *self, PyObject *args)
24732475

24742476
/* Deliberately delegate to subclasses */
24752477
if (PyObject_SetItem((PyObject *)self, slice, value) < 0) {
2478+
Py_DECREF(slice);
24762479
return NULL;
24772480
}
2478-
2481+
Py_DECREF(slice);
24792482
Py_RETURN_NONE;
24802483
}
24812484

numpy/core/tests/test_indexing.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from itertools import product
1111
from numpy.testing import (
1212
TestCase, run_module_suite, assert_, assert_equal, assert_raises,
13-
assert_array_equal, assert_warns, HAS_REFCOUNT
13+
assert_array_equal, assert_warns, dec, HAS_REFCOUNT, suppress_warnings
1414
)
1515

1616

@@ -617,6 +617,54 @@ def __array_finalize__(self, old):
617617
assert_array_equal(new_s.old, s)
618618

619619
class TestFancyIndexingCast(TestCase):
620+
@dec.skipif(not HAS_REFCOUNT)
621+
def test_slice_decref_getsetslice(self):
622+
# See gh-10066, a temporary slice object should be discarted.
623+
# This test is only really interesting on Python 2 since
624+
# it goes through `__set/getslice__` here and can probably be
625+
# removed. Use 0:7 to make sure it is never None:7.
626+
class KeepIndexObject(np.ndarray):
627+
def __getitem__(self, indx):
628+
self.indx = indx
629+
if indx == slice(0, 7):
630+
raise ValueError
631+
632+
def __setitem__(self, indx, val):
633+
self.indx = indx
634+
if indx == slice(0, 4):
635+
raise ValueError
636+
637+
k = np.array([1]).view(KeepIndexObject)
638+
k[0:5]
639+
assert_equal(k.indx, slice(0, 5))
640+
assert_equal(sys.getrefcount(k.indx), 2)
641+
try:
642+
k[0:7]
643+
raise AssertionError
644+
except ValueError:
645+
# The exception holds a reference to the slice so clear on Py2
646+
if hasattr(sys, 'exc_clear'):
647+
with suppress_warnings() as sup:
648+
sup.filter(DeprecationWarning)
649+
sys.exc_clear()
650+
assert_equal(k.indx, slice(0, 7))
651+
assert_equal(sys.getrefcount(k.indx), 2)
652+
653+
k[0:3] = 6
654+
assert_equal(k.indx, slice(0, 3))
655+
assert_equal(sys.getrefcount(k.indx), 2)
656+
try:
657+
k[0:4] = 2
658+
raise AssertionError
659+
except ValueError:
660+
# The exception holds a reference to the slice so clear on Py2
661+
if hasattr(sys, 'exc_clear'):
662+
with suppress_warnings() as sup:
663+
sup.filter(DeprecationWarning)
664+
sys.exc_clear()
665+
assert_equal(k.indx, slice(0, 4))
666+
assert_equal(sys.getrefcount(k.indx), 2)
667+
620668
def test_boolean_index_cast_assign(self):
621669
# Setup the boolean index and float arrays.
622670
shape = (8, 63)
@@ -1103,6 +1151,7 @@ def test_1d(self):
11031151
for index in self.complex_indices:
11041152
self._check_single_index(a, index)
11051153

1154+
11061155
class TestFloatNonIntegerArgument(TestCase):
11071156
"""
11081157
These test that ``TypeError`` is raised when you try to use

0 commit comments

Comments
 (0)