summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2017-11-26 13:04:58 -0700
committerGitHub <noreply@github.com>2017-11-26 13:04:58 -0700
commit984bc91367f9b525eadef14c48c759999bc4adfc (patch)
tree93f45fb39759b66b4b6590e14e3ba75bfb757b26 /numpy
parent8c441fae4bf1b101444d06fc5dcfdd29e66a9ba4 (diff)
parent4e19f408de900f958441af4ec8a458f5ce6473eb (diff)
downloadnumpy-984bc91367f9b525eadef14c48c759999bc4adfc.tar.gz
Merge pull request #10068 from seberg/issue-10066
BUG: Fix memory leak for subclass slicing
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/methods.c9
-rw-r--r--numpy/core/tests/test_indexing.py51
2 files changed, 56 insertions, 4 deletions
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 3f461b375..2c958989f 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -2452,7 +2452,7 @@ array_complex(PyArrayObject *self, PyObject *NPY_UNUSED(args))
static PyObject *
array_getslice(PyArrayObject *self, PyObject *args)
{
- PyObject *start, *stop, *slice;
+ PyObject *start, *stop, *slice, *result;
if (!PyArg_ParseTuple(args, "OO:__getslice__", &start, &stop)) {
return NULL;
}
@@ -2463,7 +2463,9 @@ array_getslice(PyArrayObject *self, PyObject *args)
}
/* Deliberately delegate to subclasses */
- return PyObject_GetItem((PyObject *)self, slice);
+ result = PyObject_GetItem((PyObject *)self, slice);
+ Py_DECREF(slice);
+ return result;
}
static PyObject *
@@ -2481,9 +2483,10 @@ array_setslice(PyArrayObject *self, PyObject *args)
/* Deliberately delegate to subclasses */
if (PyObject_SetItem((PyObject *)self, slice, value) < 0) {
+ Py_DECREF(slice);
return NULL;
}
-
+ Py_DECREF(slice);
Py_RETURN_NONE;
}
diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py
index 7cfb81da7..df9eca627 100644
--- a/numpy/core/tests/test_indexing.py
+++ b/numpy/core/tests/test_indexing.py
@@ -10,7 +10,7 @@ from numpy.core.multiarray_tests import array_indexing
from itertools import product
from numpy.testing import (
run_module_suite, assert_, assert_equal, assert_raises,
- assert_array_equal, assert_warns, HAS_REFCOUNT
+ assert_array_equal, assert_warns, dec, HAS_REFCOUNT, suppress_warnings,
)
@@ -622,6 +622,55 @@ class TestSubclasses(object):
assert_array_equal(new_s.finalize_status, new_s)
assert_array_equal(new_s.old, s)
+ @dec.skipif(not HAS_REFCOUNT)
+ def test_slice_decref_getsetslice(self):
+ # See gh-10066, a temporary slice object should be discarted.
+ # This test is only really interesting on Python 2 since
+ # it goes through `__set/getslice__` here and can probably be
+ # removed. Use 0:7 to make sure it is never None:7.
+ class KeepIndexObject(np.ndarray):
+ def __getitem__(self, indx):
+ self.indx = indx
+ if indx == slice(0, 7):
+ raise ValueError
+
+ def __setitem__(self, indx, val):
+ self.indx = indx
+ if indx == slice(0, 4):
+ raise ValueError
+
+ k = np.array([1]).view(KeepIndexObject)
+ k[0:5]
+ assert_equal(k.indx, slice(0, 5))
+ assert_equal(sys.getrefcount(k.indx), 2)
+ try:
+ k[0:7]
+ raise AssertionError
+ except ValueError:
+ # The exception holds a reference to the slice so clear on Py2
+ if hasattr(sys, 'exc_clear'):
+ with suppress_warnings() as sup:
+ sup.filter(DeprecationWarning)
+ sys.exc_clear()
+ assert_equal(k.indx, slice(0, 7))
+ assert_equal(sys.getrefcount(k.indx), 2)
+
+ k[0:3] = 6
+ assert_equal(k.indx, slice(0, 3))
+ assert_equal(sys.getrefcount(k.indx), 2)
+ try:
+ k[0:4] = 2
+ raise AssertionError
+ except ValueError:
+ # The exception holds a reference to the slice so clear on Py2
+ if hasattr(sys, 'exc_clear'):
+ with suppress_warnings() as sup:
+ sup.filter(DeprecationWarning)
+ sys.exc_clear()
+ assert_equal(k.indx, slice(0, 4))
+ assert_equal(sys.getrefcount(k.indx), 2)
+
+
class TestFancyIndexingCast(object):
def test_boolean_index_cast_assign(self):
# Setup the boolean index and float arrays.