1
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
2
#define _MULTIARRAYMODULE
3

4
#include "npy_pycompat.h"
5
#include "get_attr_string.h"
6
#include "npy_import.h"
7
#include "ufunc_override.h"
8

9
/*
10
 * Check whether an object has __array_ufunc__ defined on its class and it
11
 * is not the default, i.e., the object is not an ndarray, and its
12
 * __array_ufunc__ is not the same as that of ndarray.
13
 *
14
 * Returns a new reference, the value of type(obj).__array_ufunc__ if it
15
 * exists and is different from that of ndarray, and NULL otherwise.
16
 */
17
NPY_NO_EXPORT PyObject *
18 1
PyUFuncOverride_GetNonDefaultArrayUfunc(PyObject *obj)
19
{
20
    static PyObject *ndarray_array_ufunc = NULL;
21
    PyObject *cls_array_ufunc;
22

23
    /* On first entry, cache ndarray's __array_ufunc__ */
24 1
    if (ndarray_array_ufunc == NULL) {
25 1
        ndarray_array_ufunc = PyObject_GetAttrString((PyObject *)&PyArray_Type,
26
                                                     "__array_ufunc__");
27
    }
28

29
    /* Fast return for ndarray */
30 1
    if (PyArray_CheckExact(obj)) {
31
        return NULL;
32
    }
33
    /*
34
     * Does the class define __array_ufunc__? (Note that LookupSpecial has fast
35
     * return for basic python types, so no need to worry about those here)
36
     */
37 1
    cls_array_ufunc = PyArray_LookupSpecial(obj, "__array_ufunc__");
38 1
    if (cls_array_ufunc == NULL) {
39 1
        if (PyErr_Occurred()) {
40 0
            PyErr_Clear(); /* TODO[gh-14801]: propagate crashes during attribute access? */
41
        }
42
        return NULL;
43
    }
44
    /* Ignore if the same as ndarray.__array_ufunc__ */
45 1
    if (cls_array_ufunc == ndarray_array_ufunc) {
46 1
        Py_DECREF(cls_array_ufunc);
47
        return NULL;
48
    }
49
    return cls_array_ufunc;
50
}
51

52
/*
53
 * Check whether an object has __array_ufunc__ defined on its class and it
54
 * is not the default, i.e., the object is not an ndarray, and its
55
 * __array_ufunc__ is not the same as that of ndarray.
56
 *
57
 * Returns 1 if this is the case, 0 if not.
58
 */
59

60
NPY_NO_EXPORT int
61 1
PyUFunc_HasOverride(PyObject * obj)
62
{
63 1
    PyObject *method = PyUFuncOverride_GetNonDefaultArrayUfunc(obj);
64 1
    if (method) {
65 1
        Py_DECREF(method);
66
        return 1;
67
    }
68
    else {
69
        return 0;
70
    }
71
}
72

73
/*
74
 * Get possible out argument from kwds, and returns the number of outputs
75
 * contained within it: if a tuple, the number of elements in it, 1 otherwise.
76
 * The out argument itself is returned in out_kwd_obj, and the outputs
77
 * in the out_obj array (as borrowed references).
78
 *
79
 * Returns 0 if no outputs found, -1 if kwds is not a dict (with an error set).
80
 */
81
NPY_NO_EXPORT int
82 1
PyUFuncOverride_GetOutObjects(PyObject *kwds, PyObject **out_kwd_obj, PyObject ***out_objs)
83
{
84 1
    if (kwds == NULL) {
85 1
        Py_INCREF(Py_None);
86 1
        *out_kwd_obj = Py_None;
87 1
        return 0;
88
    }
89 1
    if (!PyDict_CheckExact(kwds)) {
90 0
        PyErr_SetString(PyExc_TypeError,
91
                        "Internal Numpy error: call to PyUFuncOverride_GetOutObjects "
92
                        "with non-dict kwds");
93 0
        *out_kwd_obj = NULL;
94 0
        return -1;
95
    }
96
    /* borrowed reference */
97 1
    *out_kwd_obj = _PyDict_GetItemStringWithError(kwds, "out");
98 1
    if (*out_kwd_obj == NULL) {
99 1
        if (PyErr_Occurred()) {
100
            return -1;
101
        }
102 1
        Py_INCREF(Py_None);
103 1
        *out_kwd_obj = Py_None;
104 1
        return 0;
105
    }
106 1
    if (PyTuple_CheckExact(*out_kwd_obj)) {
107
        /*
108
         * The C-API recommends calling PySequence_Fast before any of the other
109
         * PySequence_Fast* functions. This is required for PyPy
110
         */
111
        PyObject *seq;
112 1
        seq = PySequence_Fast(*out_kwd_obj,
113
                              "Could not convert object to sequence");
114 1
        if (seq == NULL) {
115 0
            *out_kwd_obj = NULL;
116 0
            return -1;
117
        }
118 1
        *out_objs = PySequence_Fast_ITEMS(seq);
119 1
        *out_kwd_obj = seq;
120 1
        return PySequence_Fast_GET_SIZE(seq);
121
    }
122
    else {
123 1
        Py_INCREF(*out_kwd_obj);
124 1
        *out_objs = out_kwd_obj;
125 1
        return 1;
126
    }
127
}

Read our documentation on viewing source code .

Loading