* The default `get_reduction_initial` attempts to look up the identity * from the calling ufunc. This might fail, so we only call it when necessary. * * For internal number dtypes, we can easily cache it, so do so after the * first call by overriding the function with `copy_cache_initial`. * This path is not publicly available. That could be added, and for a * custom initial getter it shou
| 263 | * custom initial getter it should be static/compile time data anyway. |
| 264 | */ |
| 265 | static int |
| 266 | get_initial_from_ufunc( |
| 267 | PyArrayMethod_Context *context, npy_bool reduction_is_empty, |
| 268 | char *initial) |
| 269 | { |
| 270 | if (context->caller == NULL |
| 271 | || !PyObject_TypeCheck(context->caller, &PyUFunc_Type)) { |
| 272 | /* Impossible in NumPy 1.24; guard in case it becomes possible. */ |
| 273 | PyErr_SetString(PyExc_ValueError, |
| 274 | "getting initial failed because it can only done for legacy " |
| 275 | "ufunc loops when the ufunc is provided."); |
| 276 | return -1; |
| 277 | } |
| 278 | npy_bool reorderable; |
| 279 | PyObject *identity_obj = PyUFunc_GetDefaultIdentity( |
| 280 | (PyUFuncObject *)context->caller, &reorderable); |
| 281 | if (identity_obj == NULL) { |
| 282 | return -1; |
| 283 | } |
| 284 | if (identity_obj == Py_None) { |
| 285 | /* UFunc has no idenity (should not happen) */ |
| 286 | Py_DECREF(identity_obj); |
| 287 | return 0; |
| 288 | } |
| 289 | if (PyTypeNum_ISUNSIGNED(context->descriptors[1]->type_num) |
| 290 | && PyLong_CheckExact(identity_obj)) { |
| 291 | /* |
| 292 | * This is a bit of a hack until we have truly loop specific |
| 293 | * identities. Python -1 cannot be cast to unsigned so convert |
| 294 | * it to a NumPy scalar, but we use -1 for bitwise functions to |
| 295 | * signal all 1s. |
| 296 | * (A builtin identity would not overflow here, although we may |
| 297 | * unnecessary convert 0 and 1.) |
| 298 | */ |
| 299 | Py_SETREF(identity_obj, PyObject_CallFunctionObjArgs( |
| 300 | (PyObject *)&PyLongArrType_Type, identity_obj, NULL)); |
| 301 | if (identity_obj == NULL) { |
| 302 | return -1; |
| 303 | } |
| 304 | } |
| 305 | else if (context->descriptors[0]->type_num == NPY_OBJECT |
| 306 | && !reduction_is_empty) { |
| 307 | /* Allows `sum([object()])` to work, but use 0 when empty. */ |
| 308 | Py_DECREF(identity_obj); |
| 309 | return 0; |
| 310 | } |
| 311 | |
| 312 | int res = PyArray_Pack(context->descriptors[0], initial, identity_obj); |
| 313 | Py_DECREF(identity_obj); |
| 314 | if (res < 0) { |
| 315 | return -1; |
| 316 | } |
| 317 | |
| 318 | if (PyTypeNum_ISNUMBER(context->descriptors[0]->type_num)) { |
| 319 | /* For numbers we can cache to avoid going via Python ints */ |
| 320 | memcpy(context->method->legacy_initial, initial, |
| 321 | context->descriptors[0]->elsize); |
| 322 | context->method->get_reduction_initial = ©_cached_initial; |
nothing calls this directly
no test coverage detected