diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index aacf3f780c99..077d56c3df26 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -555,8 +555,14 @@ ufunc_get_name_cstr(PyUFuncObject *ufunc) { * Parses the positional and keyword arguments for a generic ufunc call. * * Note that if an error is returned, the caller must free the - * non-zero references in out_op. This - * function does not do its own clean-up. + * non-zero references in out_op. This function does not do its own clean-up. + * + * Note also that some of the outputs for keyword arguments contain + * borrowed references, while others are new references. In particular, + * out_extobj: borrowed ref. + * out_typetup: new ref. + * out_wheremask: new ref. + * out_axes: borrowed ref. */ static int get_ufunc_arguments(PyUFuncObject *ufunc, @@ -851,7 +857,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, if (dtype != NULL) { if (*out_typetup != NULL) { PyErr_SetString(PyExc_RuntimeError, - "cannot specify both 'sig' and 'dtype'"); + "cannot specify both 'signature' and 'dtype'"); goto fail; } *out_typetup = Py_BuildValue("(N)", dtype); @@ -965,7 +971,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, } if (*out_typetup != NULL) { PyErr_SetString(PyExc_RuntimeError, - "cannot specify both 'sig' and 'dtype'"); + "cannot specify both 'signature' and 'dtype'"); goto fail; } *out_typetup = value; @@ -1027,18 +1033,19 @@ get_ufunc_arguments(PyUFuncObject *ufunc, fail: Py_XDECREF(str_key_obj); - Py_XDECREF(*out_extobj); - *out_extobj = NULL; + /* + * Set output kwargs to NULL, XDECREF'ing those that were not borrowed. + */ Py_XDECREF(*out_typetup); *out_typetup = NULL; - if (out_axes != NULL) { - Py_XDECREF(*out_axes); - *out_axes = NULL; - } + *out_extobj = NULL; if (out_wheremask != NULL) { Py_XDECREF(*out_wheremask); *out_wheremask = NULL; } + if (out_axes != NULL) { + *out_axes = NULL; + } return -1; } @@ -2239,8 +2246,10 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, NPY_ORDER order = NPY_KEEPORDER; /* Use the default assignment casting rule */ NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; - /* When provided, extobj, typetup, and axes contain borrowed references */ - PyObject *extobj = NULL, *type_tup = NULL, *axes = NULL; + /* When provided, extobj and axes contain borrowed references */ + PyObject *extobj = NULL, *axes = NULL; + /* but type_tup has its own reference */ + PyObject *type_tup = NULL; int keepdims = -1; if (ufunc == NULL) { @@ -2772,8 +2781,10 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc, NPY_ORDER order = NPY_KEEPORDER; /* Use the default assignment casting rule */ NPY_CASTING casting = NPY_DEFAULT_ASSIGN_CASTING; - /* When provided, extobj and typetup contain borrowed references */ - PyObject *extobj = NULL, *type_tup = NULL; + /* When provided, extobj contains a borrowed reference */ + PyObject *extobj = NULL; + /* but type_tup has its own reference */ + PyObject *type_tup = NULL; if (ufunc == NULL) { PyErr_SetString(PyExc_ValueError, "function not supported"); diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index b7fda3f2e093..ac43464618a2 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -36,6 +36,10 @@ def test_sig_dtype(self): assert_raises(RuntimeError, np.add, 1, 2, signature='ii->i', dtype=int) + def test_extobj_refcount(self): + # Should not segfault with USE_DEBUG. + assert_raises(TypeError, np.add, 1, 2, extobj=[4096], parrot=True) + class TestUfunc(object): def test_pickle(self): @@ -717,6 +721,10 @@ def test_axes_argument(self): assert_raises(ValueError, mm, z, z, out=z[:, 0]) assert_raises(ValueError, mm, z[1], z, axes=[0, 1]) assert_raises(ValueError, mm, z, z, out=z[0], axes=[0, 1]) + # Regular ufuncs should not accept axes. + assert_raises(TypeError, np.add, 1., 1., axes=[0]) + # should be able to deal with bad unrelated kwargs. + assert_raises(TypeError, mm, z, z, axes=[0, 1], parrot=True) def test_keepdims_argument(self): # inner1d signature: '(i),(i)->()' @@ -784,6 +792,8 @@ def test_keepdims_argument(self): mm = umt.matrix_multiply assert_raises(TypeError, mm, a, b, keepdims=True) assert_raises(TypeError, mm, a, b, keepdims=False) + # Regular ufuncs should not accept keepdims. + assert_raises(TypeError, np.add, 1., 1., keepdims=False) def test_innerwt(self): a = np.arange(6).reshape((2, 3))