summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/src/multiarray/multiarray_tests.c.src42
-rw-r--r--numpy/core/tests/test_multiarray.py60
2 files changed, 102 insertions, 0 deletions
diff --git a/numpy/core/src/multiarray/multiarray_tests.c.src b/numpy/core/src/multiarray/multiarray_tests.c.src
index bd0366bd5..a22319cfe 100644
--- a/numpy/core/src/multiarray/multiarray_tests.c.src
+++ b/numpy/core/src/multiarray/multiarray_tests.c.src
@@ -556,6 +556,42 @@ fail:
return NULL;
}
+/* check no elison for avoided increfs */
+static PyObject *
+incref_elide(PyObject *dummy, PyObject *args)
+{
+ PyObject *arg = NULL, *res, *tup;
+ if (!PyArg_ParseTuple(args, "O", &arg)) {
+ return NULL;
+ }
+
+ /* refcount 1 array but should not be elided */
+ arg = PyArray_NewCopy((PyArrayObject*)arg, NPY_KEEPORDER);
+ res = PyNumber_Add(arg, arg);
+
+ /* return original copy, should be equal to input */
+ tup = PyTuple_Pack(2, arg, res);
+ Py_DECREF(arg);
+ Py_DECREF(res);
+ return tup;
+}
+
+/* check no elison for get from list without incref */
+static PyObject *
+incref_elide_l(PyObject *dummy, PyObject *args)
+{
+ PyObject *arg = NULL, *r, *res;
+ if (!PyArg_ParseTuple(args, "O", &arg)) {
+ return NULL;
+ }
+ /* get item without increasing refcount, item may still be on the python
+ * stack but above the inaccessible top */
+ r = PyList_GetItem(arg, 4);
+ res = PyNumber_Add(r, r);
+
+ return res;
+}
+
#if !defined(NPY_PY3K)
static PyObject *
@@ -839,6 +875,12 @@ static PyMethodDef Multiarray_TestsMethods[] = {
{"test_inplace_increment",
inplace_increment,
METH_VARARGS, NULL},
+ {"incref_elide",
+ incref_elide,
+ METH_VARARGS, NULL},
+ {"incref_elide_l",
+ incref_elide_l,
+ METH_VARARGS, NULL},
#if !defined(NPY_PY3K)
{"test_int_subclass",
int_subclass,
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index cb5c0095c..57fae23aa 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -1695,6 +1695,66 @@ class TestMethods(TestCase):
class TestBinop(object):
+ def test_inplace(self):
+ # test refcount 1 inplace conversion
+ assert_array_almost_equal(np.array([0.5]) * np.array([1.0, 2.0]),
+ [0.5, 1.0])
+
+ d = np.array([0.5, 0.5])[::2]
+ assert_array_almost_equal(d * (d * np.array([1.0, 2.0])),
+ [0.25, 0.5])
+
+ a = np.array([0.5])
+ b = np.array([0.5])
+ c = a + b
+ c = a - b
+ c = a * b
+ c = a / b
+ assert_equal(a, b)
+ assert_almost_equal(c, 1.)
+
+ c = a + b * 2. / b * a - a / b
+ assert_equal(a, b)
+ assert_equal(c, 0.5)
+
+ # true divide
+ a = np.array([5])
+ b = np.array([3])
+ c = (a * a) / b
+
+ assert_almost_equal(c, 25 / 3)
+ assert_equal(a, 5)
+ assert_equal(b, 3)
+
+ def test_extension_incref_elide(self):
+ # test extension (e.g. cython) calling PyNumber_* slots without
+ # increasing the reference counts
+ #
+ # def incref_elide(a):
+ # d = input.copy() # refcount 1
+ # return d, d + d # PyNumber_Add without increasing refcount
+ from numpy.core.multiarray_tests import incref_elide
+ d = np.ones(5)
+ orig, res = incref_elide(d)
+ # the return original should not be changed to an inplace operation
+ assert_array_equal(orig, d)
+ assert_array_equal(res, d + d)
+
+ def test_extension_incref_elide_stack(self):
+ # scanning if the refcount == 1 object is on the python stack to check
+ # that we are called directly from python is flawed as object may still
+ # be above the stack pointer and we have no access to the top of it
+ #
+ # def incref_elide_l(d):
+ # return l[4] + l[4] # PyNumber_Add without increasing refcount
+ from numpy.core.multiarray_tests import incref_elide_l
+ # padding with 1 makes sure the object on the stack is not overwriten
+ l = [1, 1, 1, 1, np.ones(5)]
+ res = incref_elide_l(l)
+ # the return original should not be changed to an inplace operation
+ assert_array_equal(l[4], np.ones(5))
+ assert_array_equal(res, l[4] + l[4])
+
def test_ufunc_override_rop_precedence(self):
# Check that __rmul__ and other right-hand operations have
# precedence over __numpy_ufunc__