diff options
-rw-r--r-- | doc/source/reference/arrays.maskna.rst | 2 | ||||
-rw-r--r-- | doc/source/reference/c-api.array.rst | 24 | ||||
-rw-r--r-- | doc/source/reference/c-api.maskna.rst | 6 | ||||
-rw-r--r-- | numpy/core/code_generators/numpy_api.py | 2 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.c | 72 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.h | 6 | ||||
-rw-r--r-- | numpy/core/src/multiarray/methods.c | 18 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 8 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 14 |
9 files changed, 124 insertions, 28 deletions
diff --git a/doc/source/reference/arrays.maskna.rst b/doc/source/reference/arrays.maskna.rst index 29b38f131..2faabde83 100644 --- a/doc/source/reference/arrays.maskna.rst +++ b/doc/source/reference/arrays.maskna.rst @@ -58,7 +58,7 @@ as defined in the IEEE 754 floating point arithmetic specification. Most computations whose input is NA will output NA as well, a property known as propagation. Some operations, however, always produce the same result no matter what the value of the NA is. The clearest -example of this is with the logical operands *and* and *or*. Since both +example of this is with the logical operations *and* and *or*. Since both np.logical_or(True, True) and np.logical_or(False, True) are True, all possible boolean values on the left hand side produce the same answer. This means that np.logical_or(np.NA, True) can produce diff --git a/doc/source/reference/c-api.array.rst b/doc/source/reference/c-api.array.rst index eab2779a4..46a215a12 100644 --- a/doc/source/reference/c-api.array.rst +++ b/doc/source/reference/c-api.array.rst @@ -2752,6 +2752,19 @@ to. . No matter what is returned, you must DECREF the object returned by this routine in *address* when you are done with it. + If the input is an array with NA support, this will either raise + an error if it contains any NAs, or will make a copy of the array + without NA support if it does not contain any NAs. Use the function + :cfunc:`PyArray_AllowNAConverter` to support NA-arrays directly + and more efficiently. + +.. cfunction:: int PyArray_AllowConverter(PyObject* obj, PyObject** address) + + This is the same as :cfunc:`PyArray_Converter`, but allows arrays + with NA support to pass through untouched. This function was created + so that the existing converter could raise errors appropriately + for functions which have not been updated with NA support + .. cfunction:: int PyArray_OutputConverter(PyObject* obj, PyArrayObject** address) This is a default converter for output arrays given to @@ -2760,6 +2773,17 @@ to. *obj*) is TRUE then it is returned in *\*address* without incrementing its reference count. + If the output is an array with NA support, this will raise an error. + Use the function :cfunc:`PyArray_OutputAllowNAConverter` to support + NA-arrays directly. + +.. cfunction:: int PyArray_OutputAllowNAConverter(PyObject* obj, PyArrayObject** address) + + This is the same as :cfunc:`PyArray_OutputConverter`, but allows arrays + with NA support to pass through. This function was created + so that the existing output converter could raise errors appropriately + for functions which have not been updated with NA support + .. cfunction:: int PyArray_IntpConverter(PyObject* obj, PyArray_Dims* seq) Convert any Python sequence, *obj*, smaller than :cdata:`NPY_MAXDIMS` diff --git a/doc/source/reference/c-api.maskna.rst b/doc/source/reference/c-api.maskna.rst index 374998141..6abb624eb 100644 --- a/doc/source/reference/c-api.maskna.rst +++ b/doc/source/reference/c-api.maskna.rst @@ -308,9 +308,9 @@ consisting of:: static char *kwlist[] = {"a", "b", "out", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&|O&", kwlist, - &PyArray_Converter, &a, - &PyArray_Converter, &b, - &PyArray_OutputConverter, &out)) { + &PyArray_AllowNAConverter, &a, + &PyArray_AllowNAConverter, &b, + &PyArray_OutputAllowNAConverter, &out)) { return NULL; } diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py index ac5c12e24..ca89c28ec 100644 --- a/numpy/core/code_generators/numpy_api.py +++ b/numpy/core/code_generators/numpy_api.py @@ -342,6 +342,8 @@ multiarray_funcs_api = { 'NpyNA_GetPayload': 302, 'NpyNA_FromObject': 303, 'NpyNA_FromDTypeAndPayload': 304, + 'PyArray_AllowNAConverter': 305, + 'PyArray_OutputAllowNAConverter': 306, } ufunc_types_api = { diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 0e95bf671..7823b6960 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -29,12 +29,46 @@ * PyArg_ParseTuple. It will immediately return an object of array type * or will convert to a NPY_ARRAY_CARRAY any other object. * + * This function will not allow an array which supports NA through, + * to allow code which doesn't support NA to continue working as is. + * * If you use PyArray_Converter, you must DECREF the array when finished * as you get a new reference to it. */ NPY_NO_EXPORT int PyArray_Converter(PyObject *object, PyObject **address) { + if (PyArray_Check(object) && !PyArray_HASMASKNA((PyArrayObject *)object)) { + *address = object; + Py_INCREF(object); + return PY_SUCCEED; + } + else { + *address = PyArray_FromAny(object, NULL, 0, 0, + NPY_ARRAY_CARRAY, NULL); + if (*address == NULL) { + return PY_FAIL; + } + return PY_SUCCEED; + } +} + +/*NUMPY_API + * + * Useful to pass as converter function for O& processing in PyArgs_ParseTuple. + * + * This conversion function can be used with the "O&" argument for + * PyArg_ParseTuple. It will immediately return an object of array type + * or will convert to a NPY_ARRAY_CARRAY any other object. + * + * This function allows NA-arrays through. + * + * If you use PyArray_AllowNAConverter, you must DECREF the array when finished + * as you get a new reference to it. + */ +NPY_NO_EXPORT int +PyArray_AllowNAConverter(PyObject *object, PyObject **address) +{ if (PyArray_Check(object)) { *address = object; Py_INCREF(object); @@ -62,6 +96,36 @@ PyArray_OutputConverter(PyObject *object, PyArrayObject **address) return PY_SUCCEED; } if (PyArray_Check(object)) { + if (PyArray_HASMASKNA((PyArrayObject *)object)) { + PyErr_SetString(PyExc_TypeError, + "this operation does not yet support output " + "arrays with NA support"); + *address = NULL; + return PY_FAIL; + } + *address = (PyArrayObject *)object; + return PY_SUCCEED; + } + else { + PyErr_SetString(PyExc_TypeError, + "output must be an array"); + *address = NULL; + return PY_FAIL; + } +} + +/*NUMPY_API + * Useful to pass as converter function for O& processing in + * PyArgs_ParseTuple for output arrays + */ +NPY_NO_EXPORT int +PyArray_OutputAllowNAConverter(PyObject *object, PyArrayObject **address) +{ + if (object == NULL || object == Py_None) { + *address = NULL; + return PY_SUCCEED; + } + if (PyArray_Check(object)) { *address = (PyArrayObject *)object; return PY_SUCCEED; } @@ -105,9 +169,9 @@ PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq) "expected sequence object with len >= 0"); return PY_FAIL; } - if (len > MAX_DIMS) { - PyErr_Format(PyExc_ValueError, "sequence too large; " \ - "must be smaller than %d", MAX_DIMS); + if (len > NPY_MAXDIMS) { + PyErr_Format(PyExc_ValueError, "sequence too large; " + "must be smaller than %d", NPY_MAXDIMS); return PY_FAIL; } if (len > 0) { @@ -185,7 +249,7 @@ NPY_NO_EXPORT int PyArray_AxisConverter(PyObject *obj, int *axis) { if (obj == Py_None) { - *axis = MAX_DIMS; + *axis = NPY_MAXDIMS; } else { *axis = (int) PyInt_AsLong(obj); diff --git a/numpy/core/src/multiarray/conversion_utils.h b/numpy/core/src/multiarray/conversion_utils.h index 3ebd6ebf0..e344a19b0 100644 --- a/numpy/core/src/multiarray/conversion_utils.h +++ b/numpy/core/src/multiarray/conversion_utils.h @@ -2,12 +2,6 @@ #define _NPY_PRIVATE_CONVERSION_UTILS_H_ NPY_NO_EXPORT int -PyArray_Converter(PyObject *object, PyObject **address); - -NPY_NO_EXPORT int -PyArray_OutputConverter(PyObject *object, PyArrayObject **address); - -NPY_NO_EXPORT int PyArray_IntpConverter(PyObject *obj, PyArray_Dims *seq); NPY_NO_EXPORT int diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c index 7503a5c0a..8960d8a11 100644 --- a/numpy/core/src/multiarray/methods.c +++ b/numpy/core/src/multiarray/methods.c @@ -133,7 +133,7 @@ array_take(PyArrayObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&O&", kwlist, &indices, PyArray_AxisConverter, &dimension, - PyArray_OutputConverter, &out, + PyArray_OutputAllowNAConverter, &out, PyArray_ClipmodeConverter, &mode)) return NULL; @@ -382,7 +382,7 @@ array_ptp(PyArrayObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O&", kwlist, PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) + PyArray_OutputAllowNAConverter, &out)) return NULL; return PyArray_Ptp(self, axis, out); @@ -2007,7 +2007,7 @@ array_any(PyArrayObject *array, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO&ii:any", kwlist, &axis_in, - &PyArray_OutputConverter, &out, + &PyArray_OutputAllowNAConverter, &out, &skipna, &keepdims)) { return NULL; @@ -2043,7 +2043,7 @@ array_all(PyArrayObject *array, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO&ii:all", kwlist, &axis_in, - &PyArray_OutputConverter, &out, + &PyArray_OutputAllowNAConverter, &out, &skipna, &keepdims)) { return NULL; @@ -2087,7 +2087,7 @@ array_compress(PyArrayObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O&O&", kwlist, &condition, PyArray_AxisConverter, &axis, - PyArray_OutputConverter, &out)) { + PyArray_OutputAllowNAConverter, &out)) { return NULL; } return PyArray_Return( @@ -2119,7 +2119,7 @@ array_trace(PyArrayObject *self, PyObject *args, PyObject *kwds) &axis1, &axis2, PyArray_DescrConverter2, &dtype, - PyArray_OutputConverter, &out)) { + PyArray_OutputAllowNAConverter, &out)) { Py_XDECREF(dtype); return NULL; } @@ -2142,7 +2142,7 @@ array_clip(PyArrayObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO&", kwlist, &min, &max, - PyArray_OutputConverter, &out)) { + PyArray_OutputAllowNAConverter, &out)) { return NULL; } if (max == NULL && min == NULL) { @@ -2159,7 +2159,7 @@ array_conjugate(PyArrayObject *self, PyObject *args) PyArrayObject *out = NULL; if (!PyArg_ParseTuple(args, "|O&", - PyArray_OutputConverter, + PyArray_OutputAllowNAConverter, &out)) { return NULL; } @@ -2223,7 +2223,7 @@ array_round(PyArrayObject *self, PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO&", kwlist, &decimals, - PyArray_OutputConverter, &out)) { + PyArray_OutputAllowNAConverter, &out)) { return NULL; } return PyArray_Return((PyArrayObject *)PyArray_Round(self, decimals, out)); diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index e269319a8..cfbc0a3af 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -1791,7 +1791,7 @@ array_copyto(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O&|O&Oi", kwlist, &PyArray_Type, &dst, - &PyArray_Converter, &src, + &PyArray_AllowNAConverter, &src, &PyArray_CastingConverter, &casting, &wheremask_in, &preservena)) { @@ -1896,9 +1896,9 @@ array_empty_like(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds) int subok = 1, maskna = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O&O&ii", kwlist, - PyArray_Converter, &prototype, - PyArray_DescrConverter2, &dtype, - PyArray_OrderConverter, &order, + &PyArray_AllowNAConverter, &prototype, + &PyArray_DescrConverter2, &dtype, + &PyArray_OrderConverter, &order, &subok, &maskna)) { goto fail; diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index ae8169166..273aea996 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -3695,7 +3695,7 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, return NULL; } } - else { + else if (operation == UFUNC_ACCUMULATE) { if(!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&ii", kwlist1, &op, &axes_in, @@ -3707,6 +3707,18 @@ PyUFunc_GenericReduction(PyUFuncObject *ufunc, PyObject *args, return NULL; } } + else { + if(!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO&O&ii", kwlist1, + &op, + &axes_in, + PyArray_DescrConverter2, &otype, + PyArray_OutputAllowNAConverter, &out, + &skipna, + &keepdims)) { + Py_XDECREF(otype); + return NULL; + } + } /* Ensure input is an array */ if (!PyArray_Check(op) && !PyArray_IsScalar(op, Generic)) { context = Py_BuildValue("O(O)i", ufunc, op, 0); |