numpy  2.0.0
src/umath/ufunc_type_resolution.c File Reference
#include "Python.h"
#include "npy_config.h"
#include "npy_pycompat.h"
#include "numpy/ufuncobject.h"
#include "ufunc_type_resolution.h"

Data Structures

struct  _ufunc_masker_data

Defines

#define _UMATHMODULE
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION

Functions

static const char * npy_casting_to_string (NPY_CASTING casting)
NPY_NO_EXPORT int PyUFunc_ValidateCasting (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyArray_Descr **dtypes)
static PyArray_Descrensure_dtype_nbo (PyArray_Descr *type)
NPY_NO_EXPORT int PyUFunc_DefaultTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_SimpleBinaryComparisonTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_SimpleUnaryOperationTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_OnesLikeTypeResolver (PyUFuncObject *ufunc, NPY_CASTING NPY_UNUSED(casting), PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_SimpleBinaryOperationTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_AbsoluteTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
static PyArray_Descrtimedelta_dtype_with_copied_meta (PyArray_Descr *dtype)
NPY_NO_EXPORT int PyUFunc_AdditionTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_SubtractionTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_MultiplicationTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
NPY_NO_EXPORT int PyUFunc_DivisionTypeResolver (PyUFuncObject *ufunc, NPY_CASTING casting, PyArrayObject **operands, PyObject *type_tup, PyArray_Descr **out_dtypes)
static int find_userloop (PyUFuncObject *ufunc, PyArray_Descr **dtypes, PyUFuncGenericFunction *out_innerloop, void **out_innerloopdata)
NPY_NO_EXPORT int PyUFunc_DefaultLegacyInnerLoopSelector (PyUFuncObject *ufunc, PyArray_Descr **dtypes, PyUFuncGenericFunction *out_innerloop, void **out_innerloopdata, int *out_needs_api)
static NpyAuxDataufunc_masker_data_clone (NpyAuxData *data)
static void unmasked_ufunc_loop_as_masked (char **dataptrs, npy_intp *strides, char *mask, npy_intp mask_stride, npy_intp loopsize, NpyAuxData *innerloopdata)
NPY_NO_EXPORT int PyUFunc_DefaultMaskedInnerLoopSelector (PyUFuncObject *ufunc, PyArray_Descr **dtypes, PyArray_Descr *mask_dtype, npy_intp *NPY_UNUSED(fixed_strides), npy_intp NPY_UNUSED(fixed_mask_stride), PyUFunc_MaskedStridedInnerLoopFunc **out_innerloop, NpyAuxData **out_innerloopdata, int *out_needs_api)
static int ufunc_loop_matches (PyUFuncObject *self, PyArrayObject **op, NPY_CASTING input_casting, NPY_CASTING output_casting, int any_object, int use_min_scalar, int *types, int *out_no_castable_output, char *out_err_src_typecode, char *out_err_dst_typecode)
static int set_ufunc_loop_data_types (PyUFuncObject *self, PyArrayObject **op, PyArray_Descr **out_dtypes, int *type_nums)
static int linear_search_userloop_type_resolver (PyUFuncObject *self, PyArrayObject **op, NPY_CASTING input_casting, NPY_CASTING output_casting, int any_object, int use_min_scalar, PyArray_Descr **out_dtype, int *out_no_castable_output, char *out_err_src_typecode, char *out_err_dst_typecode)
static int type_tuple_userloop_type_resolver (PyUFuncObject *self, int n_specified, int *specified_types, PyArrayObject **op, NPY_CASTING casting, int any_object, int use_min_scalar, PyArray_Descr **out_dtype)
static int dtype_kind_to_simplified_ordering (char kind)
static int should_use_min_scalar (PyArrayObject **op, int nop)
NPY_NO_EXPORT int linear_search_type_resolver (PyUFuncObject *self, PyArrayObject **op, NPY_CASTING input_casting, NPY_CASTING output_casting, int any_object, PyArray_Descr **out_dtype)
NPY_NO_EXPORT int type_tuple_type_resolver (PyUFuncObject *self, PyObject *type_tup, PyArrayObject **op, NPY_CASTING casting, int any_object, PyArray_Descr **out_dtype)

Define Documentation

#define _UMATHMODULE
#define NPY_NO_DEPRECATED_API   NPY_API_VERSION

Function Documentation

static int dtype_kind_to_simplified_ordering ( char  kind) [static]
Provides an ordering for the dtype 'kind' character codes, to help determine when to use the min_scalar_type function. This groups 'kind' into boolean, integer, floating point, and everything else.
Boolean kind
Unsigned int kind
Signed int kind
Float kind
Complex kind
Anything else

References PyArray_DescrFromType(), and _PyArray_Descr::type_num.

static PyArray_Descr* ensure_dtype_nbo ( PyArray_Descr type) [static]
Returns a new reference to type if it is already NBO, otherwise returns a copy converted to NBO.

Referenced by PyUFunc_AdditionTypeResolver(), PyUFunc_MultiplicationTypeResolver(), and unmasked_ufunc_loop_as_masked().

static int find_userloop ( PyUFuncObject ufunc,
PyArray_Descr **  dtypes,
PyUFuncGenericFunction out_innerloop,
void **  out_innerloopdata 
) [static]
Use this to try to avoid repeating the same userdef loop search
It matched
Didn't find a match
NPY_NO_EXPORT int linear_search_type_resolver ( PyUFuncObject self,
PyArrayObject **  op,
NPY_CASTING  input_casting,
NPY_CASTING  output_casting,
int  any_object,
PyArray_Descr **  out_dtype 
)
Does a linear search for the best inner loop of the ufunc.
Note that if an error is returned, the caller must free the non-zero references in out_dtype. This function does not do its own clean-up.
For making a better error message on coercion error
If the ufunc has userloops, search for them.
Error
A loop was found
Determine the UFunc loop. This could in general be much faster, and a better way to implement it might be for the ufunc to provide a function which gives back the result type and inner loop function.
A default fast mechanism could be provided for functions which follow the most typical pattern, when all functions have signatures "xx...x -> x" for some built-in data type x, as follows.

System Message: ERROR/3 (<string>, line 9) Unexpected indentation.

<blockquote>

  • Use PyArray_ResultType to get the output type
  • Look up the inner loop in a table based on the output type_num

</blockquote>

The method for finding the loop in the previous code did not appear consistent (as noted by some asymmetry in the generated coercion tables for np.add).
Copy the types into an int array for matching
Error
Found a match
If no function was found, throw an error
TODO: We should try again if the casting rule is same_kind
or unsafe, and look for a function more liberally.

Referenced by PyUFunc_DefaultTypeResolver().

static int linear_search_userloop_type_resolver ( PyUFuncObject self,
PyArrayObject **  op,
NPY_CASTING  input_casting,
NPY_CASTING  output_casting,
int  any_object,
int  use_min_scalar,
PyArray_Descr **  out_dtype,
int *  out_no_castable_output,
char *  out_err_src_typecode,
char *  out_err_dst_typecode 
) [static]
Does a search through the arguments and the loops
Use this to try to avoid repeating the same userdef loop search
Error
Found a match
Didn't find a match

References NPY_MAXARGS, set_ufunc_loop_data_types(), should_use_min_scalar(), and ufunc_loop_matches().

NPY_NO_EXPORT int PyUFunc_AbsoluteTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies special type resolution rules for the absolute ufunc. This ufunc converts complex -> float, so isn't covered by the simple unary type resolution.
Returns 0 on success, -1 on error.
Use the default for complex types, to find the loop producing float
NPY_NO_EXPORT int PyUFunc_AdditionTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies the type resolution rules for addition. In particular, there are a number of special cases with datetime:

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)] m8[<A>] + int => m8[<A>] + m8[<A>] int + m8[<A>] => m8[<A>] + m8[<A>] M8[<A>] + int => M8[<A>] + m8[<A>] int + M8[<A>] => m8[<A>] + M8[<A>] M8[<A>] + m8[<B>] => M8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)] m8[<A>] + M8[<B>] => m8[gcd(<A>,<B>)] + M8[gcd(<A>,<B>)]</blockquote>

System Message: WARNING/2 (<string>, line 10) Block quote ends without a blank line; unexpected unindent.
TODO: Non-linear time unit cases require highly special-cased loops
M8[<A>] + m8[Y|M|B] m8[Y|M|B] + M8[<A>]
Use the default when datetime and timedelta are not involved
m8[<A>] + m8[<B>] => m8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)]
m8[<A>] + M8[<B>] => m8[gcd(<A>,<B>)] + M8[gcd(<A>,<B>)]
Make a new NPY_TIMEDELTA, and copy the datetime's metadata
m8[<A>] + int => m8[<A>] + m8[<A>]
M8[<A>] + m8[<B>] => M8[gcd(<A>,<B>)] + m8[gcd(<A>,<B>)]
Make a new NPY_TIMEDELTA, and copy the datetime's metadata
M8[<A>] + int => M8[<A>] + m8[<A>]
Make a new NPY_TIMEDELTA, and copy type1's metadata
int + m8[<A>] => m8[<A>] + m8[<A>]
Make a new NPY_TIMEDELTA, and copy type2's metadata
Check against the casting rules

References ensure_dtype_nbo(), NPY_TIMEDELTA, and PyArray_DESCR.

NPY_NO_EXPORT int PyUFunc_DefaultLegacyInnerLoopSelector ( PyUFuncObject ufunc,
PyArray_Descr **  dtypes,
PyUFuncGenericFunction out_innerloop,
void **  out_innerloopdata,
int *  out_needs_api 
)
If there are user-loops search them first. TODO: There needs to be a loop selection acceleration structure,

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> like a hash table.</blockquote>

Error
Found a loop
Copy the types into an int array for matching

References PyArray_free.

NPY_NO_EXPORT int PyUFunc_DefaultMaskedInnerLoopSelector ( PyUFuncObject ufunc,
PyArray_Descr **  dtypes,
PyArray_Descr mask_dtype,
npy_intp NPY_UNUSEDfixed_strides,
npy_intp   NPY_UNUSEDfixed_mask_stride,
PyUFunc_MaskedStridedInnerLoopFunc **  out_innerloop,
NpyAuxData **  out_innerloopdata,
int *  out_needs_api 
)
This function wraps a legacy inner loop so it becomes masked.
Returns 0 on success, -1 on error.
Create a new NpyAuxData object for the masker data
Get the unmasked ufunc inner loop
Return the loop function + aux data

References _loop1d_info::arg_types, _loop1d_info::next, NpyCapsule_AsVoidPtr(), PyArray_DESCR, PyTypeNum_ISUSERDEF, set_ufunc_loop_data_types(), and ufunc_loop_matches().

NPY_NO_EXPORT int PyUFunc_DefaultTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies the default type resolution rules

for the provided ufunc.

Returns 0 on success, -1 on error.

Decide the casting rules for inputs and outputs. We want NPY_SAFE_CASTING or stricter, so that the loop selection code doesn't choose an integer loop for float inputs, or a float32 loop for float64 inputs.
Find the best ufunc inner loop, and fill in the dtypes
Find the specified ufunc inner loop, and fill in the dtypes

References linear_search_type_resolver(), _tagPyUFuncObject::nin, _tagPyUFuncObject::nout, NPY_SAFE_CASTING, PyArray_DESCR, PyTypeNum_ISOBJECT, and type_tuple_type_resolver().

Referenced by PyUFunc_MultiplicationTypeResolver(), and PyUFunc_SimpleUnaryOperationTypeResolver().

NPY_NO_EXPORT int PyUFunc_DivisionTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies the type resolution rules for division. In particular, there are a number of special cases with datetime:

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> m8[<A>] / m8[<B>] to m8[gcd(<A>,<B>)] / m8[gcd(<A>,<B>)] -> float64 m8[<A>] / int## to m8[<A>] / int64 -> m8[<A>] m8[<A>] / float## to m8[<A>] / float64 -> m8[<A>]</blockquote>

Use the default when datetime and timedelta are not involved
m8[<A>] / m8[<B>] to m8[gcd(<A>,<B>)] / m8[gcd(<A>,<B>)] -> float64
m8[<A>] / int## => m8[<A>] / int64
m8[<A>] / float## => m8[<A>] / float64
Check against the casting rules
NPY_NO_EXPORT int PyUFunc_MultiplicationTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies the type resolution rules for multiplication. In particular, there are a number of special cases with datetime:

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> int## * m8[<A>] => int64 * m8[<A>] m8[<A>] * int## => m8[<A>] * int64 float## * m8[<A>] => float64 * m8[<A>] m8[<A>] * float## => m8[<A>] * float64</blockquote>

Use the default when datetime and timedelta are not involved
m8[<A>] * int## => m8[<A>] * int64
m8[<A>] * float## => m8[<A>] * float64
int## * m8[<A>] => int64 * m8[<A>]
float## * m8[<A>] => float64 * m8[<A>]
Check against the casting rules

References ensure_dtype_nbo(), _tagPyUFuncObject::name, NPY_DOUBLE, NPY_LONGLONG, NPY_TIMEDELTA, PyArray_DESCR, PyArray_DescrFromType(), PyArray_DescrNewFromType(), PyArray_PromoteTypes(), PyTypeNum_ISDATETIME, PyTypeNum_ISFLOAT, PyTypeNum_ISINTEGER, PyUFunc_DefaultTypeResolver(), PyUFunc_ValidateCasting(), PyUString_ConcatAndDel, PyUString_FromFormat, and PyUString_FromString.

NPY_NO_EXPORT int PyUFunc_OnesLikeTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING   NPY_UNUSEDcasting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
The ones_like function shouldn't really be a ufunc, but while it still is, this provides type resolution that always forces UNSAFE casting.
NPY_NO_EXPORT int PyUFunc_SimpleBinaryComparisonTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies special type resolution rules for the case where all the functions have the pattern XX->bool, using PyArray_ResultType instead of a linear search to get the best loop.
Returns 0 on success, -1 on error.
Use the default type resolution if there's a custom data type or object arrays.
Input types are the result type
If the type tuple isn't a single-element tuple, let the default type resolution handle this one.
Output type is always boolean
Check against the casting rules
NPY_NO_EXPORT int PyUFunc_SimpleBinaryOperationTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies special type resolution rules for the case where all the functions have the pattern XX->X, using PyArray_ResultType instead of a linear search to get the best loop.
Note that a simpler linear search through the functions loop is still done, but switching to a simple array lookup for built-in types would be better at some point.
Returns 0 on success, -1 on error.
Use the default type resolution if there's a custom data type or object arrays.
Input types are the result type
If the type tuple isn't a single-element tuple, let the default type resolution handle this one.
Check against the casting rules

References PyArray_ResultType().

Referenced by timedelta_dtype_with_copied_meta().

NPY_NO_EXPORT int PyUFunc_SimpleUnaryOperationTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies special type resolution rules for the case where all the functions have the pattern X->X, copying the input descr directly so that metadata is maintained.
Note that a simpler linear search through the functions loop is still done, but switching to a simple array lookup for built-in types would be better at some point.
Returns 0 on success, -1 on error.
Use the default type resolution if there's a custom data type or object arrays.
Input types are the result type
If the type tuple isn't a single-element tuple, let the default type resolution handle this one.
Check against the casting rules

References PyUFunc_DefaultTypeResolver().

NPY_NO_EXPORT int PyUFunc_SubtractionTypeResolver ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyObject *  type_tup,
PyArray_Descr **  out_dtypes 
)
This function applies the type resolution rules for subtraction. In particular, there are a number of special cases with datetime:

System Message: ERROR/3 (<string>, line 3) Unexpected indentation.

<blockquote> m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)] m8[<A>] - int => m8[<A>] - m8[<A>] int - m8[<A>] => m8[<A>] - m8[<A>] M8[<A>] - int => M8[<A>] - m8[<A>] M8[<A>] - m8[<B>] => M8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]</blockquote>

System Message: WARNING/2 (<string>, line 8) Block quote ends without a blank line; unexpected unindent.
TODO: Non-linear time unit cases require highly special-cased loops
M8[<A>] - m8[Y|M|B]
Use the default when datetime and timedelta are not involved
m8[<A>] - m8[<B>] => m8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]
m8[<A>] - int => m8[<A>] - m8[<A>]
M8[<A>] - m8[<B>] => M8[gcd(<A>,<B>)] - m8[gcd(<A>,<B>)]
Make a new NPY_TIMEDELTA, and copy the datetime's metadata
M8[<A>] - int => M8[<A>] - m8[<A>]
Make a new NPY_TIMEDELTA, and copy type1's metadata
M8[<A>] - M8[<B>] => M8[gcd(<A>,<B>)] - M8[gcd(<A>,<B>)]
Make a new NPY_TIMEDELTA, and copy type1's metadata
int - m8[<A>] => m8[<A>] - m8[<A>]
Check against the casting rules
NPY_NO_EXPORT int PyUFunc_ValidateCasting ( PyUFuncObject ufunc,
NPY_CASTING  casting,
PyArrayObject **  operands,
PyArray_Descr **  dtypes 
)
Validates that the input operands can be cast to

the input types, and the output types can be cast to the output operands where provided.

Returns 0 on success, -1 (with exception raised) on validation failure.

References _tagPyUFuncObject::name, _tagPyUFuncObject::nin, _tagPyUFuncObject::nout, npy_casting_to_string(), PyArray_CanCastArrayTo(), PyArray_CanCastTypeTo(), PyArray_DESCR, PyUString_ConcatAndDel, PyUString_FromFormat, and PyUString_FromString.

Referenced by PyUFunc_MultiplicationTypeResolver().

static int set_ufunc_loop_data_types ( PyUFuncObject self,
PyArrayObject **  op,
PyArray_Descr **  out_dtypes,
int *  type_nums 
) [static]
Fill the dtypes array. For outputs, also search the inputs for a matching type_num to copy instead of creating a new one, similarly to preserve metadata.
Copy the dtype from 'op' if the type_num matches, to preserve metadata.
For outputs, copy the dtype from op[0] if the type_num matches, similarly to preserve metdata.
Otherwise create a plain descr from the type number

Referenced by linear_search_userloop_type_resolver(), PyUFunc_DefaultMaskedInnerLoopSelector(), and ufunc_loop_matches().

static int should_use_min_scalar ( PyArrayObject **  op,
int  nop 
) [static]
Determine if there are any scalars, and if so, whether the maximum "kind" of the scalars surpasses the maximum "kind" of the arrays
Indicate whether to use the min_scalar_type function

Referenced by linear_search_userloop_type_resolver().

Creates a new NPY_TIMEDELTA dtype, copying the datetime metadata from the given dtype.

NOTE: This function is copied from datetime.c in multiarray,
because umath and multiarray are not linked together.

References PyUFunc_SimpleBinaryOperationTypeResolver().

NPY_NO_EXPORT int type_tuple_type_resolver ( PyUFuncObject self,
PyObject *  type_tup,
PyArrayObject **  op,
NPY_CASTING  casting,
int  any_object,
PyArray_Descr **  out_dtype 
)
Does a linear search for the inner loop of the ufunc specified by type_tup.
Note that if an error is returned, the caller must free the non-zero references in out_dtype. This function does not do its own clean-up.
For making a better error message on coercion error
Fill in specified_types from the tuple or string
If the ufunc has userloops, search for them.
Error
Found matching loop
Copy the types into an int array for matching
Error
It worked
Didn't work
If no function was found, throw an error

Referenced by PyUFunc_DefaultTypeResolver().

static int type_tuple_userloop_type_resolver ( PyUFuncObject self,
int  n_specified,
int *  specified_types,
PyArrayObject **  op,
NPY_CASTING  casting,
int  any_object,
int  use_min_scalar,
PyArray_Descr **  out_dtype 
) [static]
Does a search through the arguments and the loops
Use this to try to avoid repeating the same userdef loop search
It works
Didn't match
Error
Didn't find a match
static int ufunc_loop_matches ( PyUFuncObject self,
PyArrayObject **  op,
NPY_CASTING  input_casting,
NPY_CASTING  output_casting,
int  any_object,
int  use_min_scalar,
int *  types,
int *  out_no_castable_output,
char *  out_err_src_typecode,
char *  out_err_dst_typecode 
) [static]
First check if all the inputs can be safely cast to the types for this function
If no inputs are objects and there are more than one loop, don't allow conversion to object. The rationale behind this is mostly performance. Except for custom ufuncs built with just one object-parametered inner loop, only the types that are supported are implemented. Trying the object version of logical_or on float arguments doesn't seem right.
If all the inputs are scalars, use the regular promotion rules, not the special value-checking ones.
If all the inputs were ok, then check casting back to the outputs.

References _loop1d_info::arg_types, _loop1d_info::next, NPY_NOTYPE, NpyCapsule_AsVoidPtr(), PyArray_DESCR, PyTypeNum_ISUSERDEF, and set_ufunc_loop_data_types().

Referenced by linear_search_userloop_type_resolver(), and PyUFunc_DefaultMaskedInnerLoopSelector().

static NpyAuxData* ufunc_masker_data_clone ( NpyAuxData data) [static]
Allocate a new one
Copy the data (unmasked data doesn't have object semantics)

References PyArray_CanCastTypeTo(), PyArray_DESCR, PyArray_DescrFromType(), and _PyArray_Descr::type.

static void unmasked_ufunc_loop_as_masked ( char **  dataptrs,
npy_intp strides,
char *  mask,
npy_intp  mask_stride,
npy_intp  loopsize,
NpyAuxData innerloopdata 
) [static]
This function wraps a regular unmasked ufunc inner loop as a masked ufunc inner loop, only calling the function for elements where the mask is True.
Put the aux data into local variables
Process the data as runs of unmasked values
Skip masked values
Process unmasked values (assumes unmasked loop doesn't mess with the 'args' pointer values)

References ensure_dtype_nbo(), PyArray_DESCR, and PyArray_DescrFromType().