* Main ufunc call implementation. * * This implementation makes use of the "fastcall" way of passing keyword * arguments and is called directly from `ufunc_generic_vectorcall` when * Python has `tp_vectorcall` (Python 3.8+). * If `tp_vectorcall` is not available, the dictionary `kwargs` are unpacked in * `ufunc_generic_call` with fairly little overhead. */
| 4704 | * `ufunc_generic_call` with fairly little overhead. |
| 4705 | */ |
| 4706 | static PyObject * |
| 4707 | ufunc_generic_fastcall(PyUFuncObject *ufunc, |
| 4708 | PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames, |
| 4709 | npy_bool outer) |
| 4710 | { |
| 4711 | int errval; |
| 4712 | int nin = ufunc->nin, nout = ufunc->nout, nop = ufunc->nargs; |
| 4713 | |
| 4714 | /* All following variables are cleared in the `fail` error path */ |
| 4715 | ufunc_full_args full_args; |
| 4716 | PyArrayObject *wheremask = NULL; |
| 4717 | |
| 4718 | PyArray_DTypeMeta *signature[NPY_MAXARGS]; |
| 4719 | PyArrayObject *operands[NPY_MAXARGS]; |
| 4720 | PyArray_DTypeMeta *operand_DTypes[NPY_MAXARGS]; |
| 4721 | PyArray_Descr *operation_descrs[NPY_MAXARGS]; |
| 4722 | PyObject *output_array_prepare[NPY_MAXARGS]; |
| 4723 | /* Initialize all arrays (we usually only need a small part) */ |
| 4724 | memset(signature, 0, nop * sizeof(*signature)); |
| 4725 | memset(operands, 0, nop * sizeof(*operands)); |
| 4726 | memset(operand_DTypes, 0, nop * sizeof(*operation_descrs)); |
| 4727 | memset(operation_descrs, 0, nop * sizeof(*operation_descrs)); |
| 4728 | memset(output_array_prepare, 0, nout * sizeof(*output_array_prepare)); |
| 4729 | |
| 4730 | /* |
| 4731 | * Note that the input (and possibly output) arguments are passed in as |
| 4732 | * positional arguments. We extract these first and check for `out` |
| 4733 | * passed by keyword later. |
| 4734 | * Outputs and inputs are stored in `full_args.in` and `full_args.out` |
| 4735 | * as tuples (or NULL when no outputs are passed). |
| 4736 | */ |
| 4737 | |
| 4738 | /* Check number of arguments */ |
| 4739 | if (NPY_UNLIKELY((len_args < nin) || (len_args > nop))) { |
| 4740 | PyErr_Format(PyExc_TypeError, |
| 4741 | "%s() takes from %d to %d positional arguments but " |
| 4742 | "%zd were given", |
| 4743 | ufunc_get_name_cstr(ufunc) , nin, nop, len_args); |
| 4744 | return NULL; |
| 4745 | } |
| 4746 | |
| 4747 | /* Fetch input arguments. */ |
| 4748 | full_args.in = PyArray_TupleFromItems(ufunc->nin, args, 0); |
| 4749 | if (full_args.in == NULL) { |
| 4750 | return NULL; |
| 4751 | } |
| 4752 | |
| 4753 | /* |
| 4754 | * If there are more arguments, they define the out args. Otherwise |
| 4755 | * full_args.out is NULL for now, and the `out` kwarg may still be passed. |
| 4756 | */ |
| 4757 | npy_bool out_is_passed_by_position = len_args > nin; |
| 4758 | if (out_is_passed_by_position) { |
| 4759 | npy_bool all_none = NPY_TRUE; |
| 4760 | |
| 4761 | full_args.out = PyTuple_New(nout); |
| 4762 | if (full_args.out == NULL) { |
| 4763 | goto fail; |
no test coverage detected