summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/reference/arrays.maskna.rst2
-rw-r--r--doc/source/reference/c-api.array.rst24
-rw-r--r--doc/source/reference/c-api.maskna.rst6
-rw-r--r--numpy/core/code_generators/numpy_api.py2
-rw-r--r--numpy/core/src/multiarray/conversion_utils.c72
-rw-r--r--numpy/core/src/multiarray/conversion_utils.h6
-rw-r--r--numpy/core/src/multiarray/methods.c18
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c8
-rw-r--r--numpy/core/src/umath/ufunc_object.c14
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);