diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2017-11-26 13:04:58 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-26 13:04:58 -0700 |
commit | 984bc91367f9b525eadef14c48c759999bc4adfc (patch) | |
tree | 93f45fb39759b66b4b6590e14e3ba75bfb757b26 /numpy | |
parent | 8c441fae4bf1b101444d06fc5dcfdd29e66a9ba4 (diff) | |
parent | 4e19f408de900f958441af4ec8a458f5ce6473eb (diff) | |
download | numpy-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.c | 9 | ||||
-rw-r--r-- | numpy/core/tests/test_indexing.py | 51 |
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. |