diff options
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 260 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 51 |
2 files changed, 206 insertions, 105 deletions
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 7637d68e6..e419a6611 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -73,11 +73,16 @@ static int _does_loop_use_arrays(void *data); static int +_extract_pyvals(PyObject *ref, char *name, int *bufsize, + int *errmask, PyObject **errobj); + +static int assign_reduce_identity_zero(PyArrayObject *result, void *data); static int assign_reduce_identity_one(PyArrayObject *result, void *data); + /* * fpstatus is the ufunc_formatted hardware status * errmask is the handling mask specified by the user. @@ -225,6 +230,53 @@ PyUFunc_clearfperr() PyUFunc_getfperr(); } + +#if USE_USE_DEFAULTS==1 +static int PyUFunc_NUM_NODEFAULTS = 0; +#endif +static PyObject *PyUFunc_PYVALS_NAME = NULL; + +static PyObject * +_get_global_ext_obj(char * name) +{ + PyObject *thedict; + PyObject *ref = NULL; + +#if USE_USE_DEFAULTS==1 + if (PyUFunc_NUM_NODEFAULTS != 0) { +#endif + if (PyUFunc_PYVALS_NAME == NULL) { + PyUFunc_PYVALS_NAME = PyUString_InternFromString(UFUNC_PYVALS_NAME); + } + thedict = PyThreadState_GetDict(); + if (thedict == NULL) { + thedict = PyEval_GetBuiltins(); + } + ref = PyDict_GetItem(thedict, PyUFunc_PYVALS_NAME); +#if USE_USE_DEFAULTS==1 + } +#endif + + return ref; +} + + +static int +_get_bufsize_errmask(PyObject * extobj, char * ufunc_name, + int *buffersize, int *errormask) +{ + /* Get the buffersize and errormask */ + if (extobj == NULL) { + extobj = _get_global_ext_obj(ufunc_name); + } + if (_extract_pyvals(extobj, ufunc_name, + buffersize, errormask, NULL) < 0) { + return -1; + } + + return 0; +} + /* * This function analyzes the input arguments * and determines an appropriate __array_prepare__ function to call @@ -363,16 +415,12 @@ _find_array_prepare(PyObject *args, PyObject *kwds, return; } -#if USE_USE_DEFAULTS==1 -static int PyUFunc_NUM_NODEFAULTS = 0; -#endif -static PyObject *PyUFunc_PYVALS_NAME = NULL; - - /* * Extracts some values from the global pyvals tuple. + * all destinations may be NULL, in which case they are not retrieved * ref - should hold the global tuple * name - is the name of the ufunc (ufuncobj->name) + * * bufsize - receives the buffer size to use * errmask - receives the bitmask for error handling * errobj - receives the python object to call with the error, @@ -384,62 +432,74 @@ _extract_pyvals(PyObject *ref, char *name, int *bufsize, { PyObject *retval; - *errobj = NULL; + if (ref == NULL) { + *errmask = UFUNC_ERR_DEFAULT; + *errobj = Py_BuildValue("NO", PyBytes_FromString(name), Py_None); + *bufsize = NPY_BUFSIZE; + return 0; + } + if (!PyList_Check(ref) || (PyList_GET_SIZE(ref)!=3)) { PyErr_Format(PyExc_TypeError, "%s must be a length 3 list.", UFUNC_PYVALS_NAME); return -1; } - *bufsize = PyInt_AsLong(PyList_GET_ITEM(ref, 0)); - if ((*bufsize == -1) && PyErr_Occurred()) { - return -1; - } - if ((*bufsize < NPY_MIN_BUFSIZE) || - (*bufsize > NPY_MAX_BUFSIZE) || - (*bufsize % 16 != 0)) { - PyErr_Format(PyExc_ValueError, - "buffer size (%d) is not in range " - "(%"NPY_INTP_FMT" - %"NPY_INTP_FMT") or not a multiple of 16", - *bufsize, (npy_intp) NPY_MIN_BUFSIZE, - (npy_intp) NPY_MAX_BUFSIZE); - return -1; - } - - *errmask = PyInt_AsLong(PyList_GET_ITEM(ref, 1)); - if (*errmask < 0) { - if (PyErr_Occurred()) { + if (bufsize != NULL) { + *bufsize = PyInt_AsLong(PyList_GET_ITEM(ref, 0)); + if ((*bufsize == -1) && PyErr_Occurred()) { + return -1; + } + if ((*bufsize < NPY_MIN_BUFSIZE) || + (*bufsize > NPY_MAX_BUFSIZE) || + (*bufsize % 16 != 0)) { + PyErr_Format(PyExc_ValueError, + "buffer size (%d) is not in range " + "(%"NPY_INTP_FMT" - %"NPY_INTP_FMT") or not a multiple of 16", + *bufsize, (npy_intp) NPY_MIN_BUFSIZE, + (npy_intp) NPY_MAX_BUFSIZE); return -1; } - PyErr_Format(PyExc_ValueError, - "invalid error mask (%d)", - *errmask); - return -1; } - retval = PyList_GET_ITEM(ref, 2); - if (retval != Py_None && !PyCallable_Check(retval)) { - PyObject *temp; - temp = PyObject_GetAttrString(retval, "write"); - if (temp == NULL || !PyCallable_Check(temp)) { - PyErr_SetString(PyExc_TypeError, - "python object must be callable or have " \ - "a callable write method"); - Py_XDECREF(temp); + if (errmask != NULL) { + *errmask = PyInt_AsLong(PyList_GET_ITEM(ref, 1)); + if (*errmask < 0) { + if (PyErr_Occurred()) { + return -1; + } + PyErr_Format(PyExc_ValueError, + "invalid error mask (%d)", + *errmask); return -1; } - Py_DECREF(temp); } - *errobj = Py_BuildValue("NO", PyBytes_FromString(name), retval); - if (*errobj == NULL) { - return -1; + if (errobj != NULL) { + *errobj = NULL; + retval = PyList_GET_ITEM(ref, 2); + if (retval != Py_None && !PyCallable_Check(retval)) { + PyObject *temp; + temp = PyObject_GetAttrString(retval, "write"); + if (temp == NULL || !PyCallable_Check(temp)) { + PyErr_SetString(PyExc_TypeError, + "python object must be callable or have " \ + "a callable write method"); + Py_XDECREF(temp); + return -1; + } + Py_DECREF(temp); + } + + *errobj = Py_BuildValue("NO", PyBytes_FromString(name), retval); + if (*errobj == NULL) { + return -1; + } } return 0; } - /*UFUNC_API * * On return, if errobj is populated with a non-NULL value, the caller @@ -449,28 +509,8 @@ NPY_NO_EXPORT int PyUFunc_GetPyValues(char *name, int *bufsize, int *errmask, PyObject **errobj) { PyObject *thedict; - PyObject *ref = NULL; + PyObject *ref = _get_global_ext_obj(name); -#if USE_USE_DEFAULTS==1 - if (PyUFunc_NUM_NODEFAULTS != 0) { -#endif - if (PyUFunc_PYVALS_NAME == NULL) { - PyUFunc_PYVALS_NAME = PyUString_InternFromString(UFUNC_PYVALS_NAME); - } - thedict = PyThreadState_GetDict(); - if (thedict == NULL) { - thedict = PyEval_GetBuiltins(); - } - ref = PyDict_GetItem(thedict, PyUFunc_PYVALS_NAME); -#if USE_USE_DEFAULTS==1 - } -#endif - if (ref == NULL) { - *errmask = UFUNC_ERR_DEFAULT; - *errobj = Py_BuildValue("NO", PyBytes_FromString(name), Py_None); - *bufsize = NPY_BUFSIZE; - return 0; - } return _extract_pyvals(ref, name, bufsize, errmask, errobj); } @@ -1715,6 +1755,45 @@ make_arr_prep_args(npy_intp nin, PyObject *args, PyObject *kwds) } } +/* + * check the floating point status + * - errmask: mask of status to check + * - extobj: ufunc pyvals object + * may be null, in which case the thread global one is fetched + * - ufunc_name: name of ufunc + */ +static int +_check_ufunc_fperr(int errmask, PyObject *extobj, char* ufunc_name) { + int fperr; + PyObject *errobj = NULL; + int ret; + int first = 1; + + if (!errmask) { + return 0; + } + fperr = PyUFunc_getfperr(); + if (!fperr) { + return 0; + } + + /* Get error object globals */ + if (extobj == NULL) { + extobj = _get_global_ext_obj(ufunc_name); + } + if (_extract_pyvals(extobj, ufunc_name, + NULL, NULL, &errobj) < 0) { + Py_XDECREF(errobj); + return -1; + } + + ret = PyUFunc_handlefperr(errmask, errobj, fperr, &first); + Py_XDECREF(errobj); + + return ret; +} + + static int PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, PyObject *args, PyObject *kwds, @@ -1740,8 +1819,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; - PyObject *errobj = NULL; - int first_error = 1; /* The selected inner loop */ PyUFuncGenericFunction innerloop = NULL; @@ -1944,20 +2021,10 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, core_dim_ixs_size += ufunc->core_num_dims[i]; } - /* Get the buffersize, errormask, and error object globals */ - if (extobj == NULL) { - if (PyUFunc_GetPyValues(ufunc_name, - &buffersize, &errormask, &errobj) < 0) { - retval = -1; - goto fail; - } - } - else { - if (_extract_pyvals(extobj, ufunc_name, - &buffersize, &errormask, &errobj) < 0) { - retval = -1; - goto fail; - } + /* Get the buffersize and errormask */ + if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { + retval = -1; + goto fail; } NPY_UF_DBG_PRINT("Finding inner loop\n"); @@ -2203,8 +2270,8 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } /* Check whether any errors occurred during the loop */ - if (PyErr_Occurred() || (errormask && - PyUFunc_checkfperr(errormask, errobj, &first_error))) { + if (PyErr_Occurred() || + _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { retval = -1; goto fail; } @@ -2216,7 +2283,6 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, Py_XDECREF(dtypes[i]); Py_XDECREF(arr_prep[i]); } - Py_XDECREF(errobj); Py_XDECREF(type_tup); Py_XDECREF(arr_prep_args); @@ -2234,7 +2300,6 @@ fail: Py_XDECREF(dtypes[i]); Py_XDECREF(arr_prep[i]); } - Py_XDECREF(errobj); Py_XDECREF(type_tup); Py_XDECREF(arr_prep_args); @@ -2263,8 +2328,6 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc, /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; - PyObject *errobj = NULL; - int first_error = 1; /* The mask provided in the 'where=' parameter */ PyArrayObject *wheremask = NULL; @@ -2326,20 +2389,10 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc, need_fancy = 1; } - /* Get the buffersize, errormask, and error object globals */ - if (extobj == NULL) { - if (PyUFunc_GetPyValues(ufunc_name, - &buffersize, &errormask, &errobj) < 0) { - retval = -1; - goto fail; - } - } - else { - if (_extract_pyvals(extobj, ufunc_name, - &buffersize, &errormask, &errobj) < 0) { - retval = -1; - goto fail; - } + /* Get the buffersize and errormask */ + if (_get_bufsize_errmask(extobj, ufunc_name, &buffersize, &errormask) < 0) { + retval = -1; + goto fail; } NPY_UF_DBG_PRINT("Finding inner loop\n"); @@ -2449,18 +2502,18 @@ PyUFunc_GenericFunction(PyUFuncObject *ufunc, } /* Check whether any errors occurred during the loop */ - if (PyErr_Occurred() || (errormask && - PyUFunc_checkfperr(errormask, errobj, &first_error))) { + if (PyErr_Occurred() || + _check_ufunc_fperr(errormask, extobj, ufunc_name) < 0) { retval = -1; goto fail; } + /* The caller takes ownership of all the references in op */ for (i = 0; i < nop; ++i) { Py_XDECREF(dtypes[i]); Py_XDECREF(arr_prep[i]); } - Py_XDECREF(errobj); Py_XDECREF(type_tup); Py_XDECREF(arr_prep_args); Py_XDECREF(wheremask); @@ -2477,7 +2530,6 @@ fail: Py_XDECREF(dtypes[i]); Py_XDECREF(arr_prep[i]); } - Py_XDECREF(errobj); Py_XDECREF(type_tup); Py_XDECREF(arr_prep_args); Py_XDECREF(wheremask); diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 913599e09..5a3de8edd 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -409,6 +409,36 @@ class TestSeterr(TestCase): seterr(divide='ignore') array([1.]) / array([0.]) + def test_errobj(self): + olderrobj = np.geterrobj() + self.called = 0 + try: + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + with errstate(divide='warn'): + np.seterrobj([20000, 1, None]) + array([1.]) / array([0.]) + self.assertEqual(len(w), 1) + + def log_err(*args): + self.called += 1 + extobj_err = args + assert (len(extobj_err) == 2) + assert ("divide" in extobj_err[0]) + + with errstate(divide='ignore'): + np.seterrobj([20000, 3, log_err]) + array([1.]) / array([0.]) + self.assertEqual(self.called, 1) + + np.seterrobj(olderrobj) + with errstate(divide='ignore'): + np.divide(1., 0., extobj=[20000, 3, log_err]) + self.assertEqual(self.called, 2) + finally: + np.seterrobj(olderrobj) + del self.called + class TestFloatExceptions(TestCase): def assert_raises_fpe(self, fpeerr, flop, x, y): @@ -433,7 +463,7 @@ class TestFloatExceptions(TestCase): self.assert_raises_fpe(fpeerr, flop, sc1, sc2[()]); self.assert_raises_fpe(fpeerr, flop, sc1[()], sc2[()]); - @dec.knownfailureif(True, "See ticket 1755") + @dec.knownfailureif(True, "See ticket #2350") def test_floating_exceptions(self): # Test basic arithmetic function errors with np.errstate(all='raise'): @@ -488,6 +518,25 @@ class TestFloatExceptions(TestCase): self.assert_raises_fpe(invalid, lambda a, b:a*b, ftype(0), ftype(np.inf)) + def test_warnings(self): + # test warning code path + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + with np.errstate(all="warn"): + np.divide(1, 0.) + self.assertEqual(len(w), 1) + self.assertTrue("divide by zero" in str(w[0].message)) + np.array(1e300) * np.array(1e300) + self.assertEqual(len(w), 2) + self.assertTrue("overflow" in str(w[-1].message)) + np.array(np.inf) - np.array(np.inf) + self.assertEqual(len(w), 3) + self.assertTrue("invalid value" in str(w[-1].message)) + np.array(1e-300) * np.array(1e-300) + self.assertEqual(len(w), 4) + self.assertTrue("underflow" in str(w[-1].message)) + + class TestTypes(TestCase): def check_promotion_cases(self, promote_func): #Tests that the scalars get coerced correctly. |