summaryrefslogtreecommitdiff
path: root/numpy/core
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2021-06-15 14:39:42 -0500
committerSebastian Berg <sebastian@sipsolutions.net>2021-06-15 15:08:55 -0500
commitce6afb6a99c89300c5fb132a2b1e508d903dc1aa (patch)
tree40042a1154c6068127509a9490a696464eb555d4 /numpy/core
parentb5cc1f80c6747e6c19adeaf241d2d471d2b71cb7 (diff)
downloadnumpy-ce6afb6a99c89300c5fb132a2b1e508d903dc1aa.tar.gz
MAINT: Refactor output ufunc wrapping logic
This replaces the output wrapping logic to work towards (and simplify) the big ufunc refactor for NEP 43.
Diffstat (limited to 'numpy/core')
-rw-r--r--numpy/core/src/umath/ufunc_object.c136
1 files changed, 80 insertions, 56 deletions
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 0644a28c0..8fdd14e61 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -4667,6 +4667,81 @@ _get_normalized_typetup(PyUFuncObject *ufunc,
}
+/**
+ * Wraps all outputs and returns the result (which may be NULL on error).
+ *
+ * Use __array_wrap__ on all outputs
+ * if present on one of the input arguments.
+ * If present for multiple inputs:
+ * use __array_wrap__ of input object with largest
+ * __array_priority__ (default = 0.0)
+ *
+ * Exception: we should not wrap outputs for items already
+ * passed in as output-arguments. These items should either
+ * be left unwrapped or wrapped by calling their own __array_wrap__
+ * routine.
+ *
+ * For each output argument, wrap will be either
+ * NULL --- call PyArray_Return() -- default if no output arguments given
+ * None --- array-object passed in don't call PyArray_Return
+ * method --- the __array_wrap__ method to call.
+ *
+ * @param ufunc
+ * @param full_args Original inputs and outputs
+ * @param subok Whether subclasses are allowed
+ * @param result_arrays The ufunc result(s). REFERENCES ARE STOLEN!
+ */
+static PyObject *
+replace_with_wrapped_result_and_return(PyUFuncObject *ufunc,
+ ufunc_full_args full_args, npy_bool subok,
+ PyArrayObject *result_arrays[])
+{
+ PyObject *retobj[NPY_MAXARGS];
+ PyObject *wraparr[NPY_MAXARGS];
+ _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout);
+
+ /* wrap outputs */
+ for (int i = 0; i < ufunc->nout; i++) {
+ _ufunc_context context;
+
+ context.ufunc = ufunc;
+ context.args = full_args;
+ context.out_i = i;
+
+ retobj[i] = _apply_array_wrap(wraparr[i], result_arrays[i], &context);
+ result_arrays[i] = NULL; /* Was DECREF'ed and (probably) wrapped */
+ if (retobj[i] == NULL) {
+ goto fail;
+ }
+ }
+
+ if (ufunc->nout == 1) {
+ return retobj[0];
+ }
+ else {
+ PyObject *result = PyTuple_New(ufunc->nout);
+ if (result == NULL) {
+ return NULL;
+ }
+ for (int i = 0; i < ufunc->nout; i++) {
+ PyTuple_SET_ITEM(result, i, retobj[i]);
+ }
+ return result;
+ }
+
+ fail:
+ for (int i = 0; i < ufunc->nout; i++) {
+ if (result_arrays[i] != NULL) {
+ Py_DECREF(result_arrays[i]);
+ }
+ else {
+ Py_XDECREF(retobj[i]);
+ }
+ }
+ return NULL;
+}
+
+
/*
* Main ufunc call implementation.
*
@@ -4682,8 +4757,6 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc,
npy_bool outer)
{
PyArrayObject *operands[NPY_MAXARGS] = {NULL};
- PyObject *retobj[NPY_MAXARGS];
- PyObject *wraparr[NPY_MAXARGS];
PyObject *override = NULL;
ufunc_full_args full_args = {NULL, NULL};
PyObject *typetup = NULL;
@@ -4901,63 +4974,14 @@ ufunc_generic_fastcall(PyUFuncObject *ufunc,
for (int i = 0; i < ufunc->nin; i++) {
Py_XSETREF(operands[i], NULL);
}
-
- /*
- * Use __array_wrap__ on all outputs
- * if present on one of the input arguments.
- * If present for multiple inputs:
- * use __array_wrap__ of input object with largest
- * __array_priority__ (default = 0.0)
- *
- * Exception: we should not wrap outputs for items already
- * passed in as output-arguments. These items should either
- * be left unwrapped or wrapped by calling their own __array_wrap__
- * routine.
- *
- * For each output argument, wrap will be either
- * NULL --- call PyArray_Return() -- default if no output arguments given
- * None --- array-object passed in don't call PyArray_Return
- * method --- the __array_wrap__ method to call.
- */
- _find_array_wrap(full_args, subok, wraparr, ufunc->nin, ufunc->nout);
-
- /* wrap outputs */
- for (int i = 0; i < ufunc->nout; i++) {
- int j = ufunc->nin+i;
- _ufunc_context context;
- PyObject *wrapped;
-
- context.ufunc = ufunc;
- context.args = full_args;
- context.out_i = i;
-
- wrapped = _apply_array_wrap(wraparr[i], operands[j], &context);
- operands[j] = NULL; /* Prevent fail double-freeing this */
- if (wrapped == NULL) {
- for (int j = 0; j < i; j++) {
- Py_DECREF(retobj[j]);
- }
- goto fail;
- }
-
- retobj[i] = wrapped;
- }
-
Py_XDECREF(typetup);
+
+ /* The following steals the references to the outputs: */
+ PyObject *result = replace_with_wrapped_result_and_return(ufunc,
+ full_args, subok, operands+nin);
Py_XDECREF(full_args.in);
Py_XDECREF(full_args.out);
- if (ufunc->nout == 1) {
- return retobj[0];
- }
- else {
- PyTupleObject *ret;
-
- ret = (PyTupleObject *)PyTuple_New(ufunc->nout);
- for (int i = 0; i < ufunc->nout; i++) {
- PyTuple_SET_ITEM(ret, i, retobj[i]);
- }
- return (PyObject *)ret;
- }
+ return result;
fail:
Py_XDECREF(typetup);