1 #ifndef __GET_ATTR_STRING_H
2 #define __GET_ATTR_STRING_H
3
4 static NPY_INLINE npy_bool
_is_basic_python_type(PyTypeObject * tp)5 _is_basic_python_type(PyTypeObject *tp)
6 {
7 return (
8 /* Basic number types */
9 tp == &PyBool_Type ||
10 tp == &PyLong_Type ||
11 tp == &PyFloat_Type ||
12 tp == &PyComplex_Type ||
13
14 /* Basic sequence types */
15 tp == &PyList_Type ||
16 tp == &PyTuple_Type ||
17 tp == &PyDict_Type ||
18 tp == &PySet_Type ||
19 tp == &PyFrozenSet_Type ||
20 tp == &PyUnicode_Type ||
21 tp == &PyBytes_Type ||
22
23 /* other builtins */
24 tp == &PySlice_Type ||
25 tp == Py_TYPE(Py_None) ||
26 tp == Py_TYPE(Py_Ellipsis) ||
27 tp == Py_TYPE(Py_NotImplemented) ||
28
29 /* TODO: ndarray, but we can't see PyArray_Type here */
30
31 /* sentinel to swallow trailing || */
32 NPY_FALSE
33 );
34 }
35
36 /*
37 * Stripped down version of PyObject_GetAttrString(obj, name) that does not
38 * raise PyExc_AttributeError.
39 *
40 * This allows it to avoid creating then discarding exception objects when
41 * performing lookups on objects without any attributes.
42 *
43 * Returns attribute value on success, NULL without an exception set if
44 * there is no such attribute, and NULL with an exception on failure.
45 */
46 static NPY_INLINE PyObject *
maybe_get_attr(PyObject * obj,char const * name)47 maybe_get_attr(PyObject *obj, char const *name)
48 {
49 PyTypeObject *tp = Py_TYPE(obj);
50 PyObject *res = (PyObject *)NULL;
51
52 /* Attribute referenced by (char *)name */
53 if (tp->tp_getattr != NULL) {
54 res = (*tp->tp_getattr)(obj, (char *)name);
55 if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
56 PyErr_Clear();
57 }
58 }
59 /* Attribute referenced by (PyObject *)name */
60 else if (tp->tp_getattro != NULL) {
61 PyObject *w = PyUnicode_InternFromString(name);
62 if (w == NULL) {
63 return (PyObject *)NULL;
64 }
65 res = (*tp->tp_getattro)(obj, w);
66 Py_DECREF(w);
67 if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
68 PyErr_Clear();
69 }
70 }
71 return res;
72 }
73
74 /*
75 * Lookup a special method, following the python approach of looking up
76 * on the type object, rather than on the instance itself.
77 *
78 * Assumes that the special method is a numpy-specific one, so does not look
79 * at builtin types, nor does it look at a base ndarray.
80 *
81 * In future, could be made more like _Py_LookupSpecial
82 */
83 static NPY_INLINE PyObject *
PyArray_LookupSpecial(PyObject * obj,char const * name)84 PyArray_LookupSpecial(PyObject *obj, char const *name)
85 {
86 PyTypeObject *tp = Py_TYPE(obj);
87
88 /* We do not need to check for special attributes on trivial types */
89 if (_is_basic_python_type(tp)) {
90 return NULL;
91 }
92 return maybe_get_attr((PyObject *)tp, name);
93 }
94
95 /*
96 * PyArray_LookupSpecial_OnInstance:
97 *
98 * Implements incorrect special method lookup rules, that break the python
99 * convention, and looks on the instance, not the type.
100 *
101 * Kept for backwards compatibility. In future, we should deprecate this.
102 */
103 static NPY_INLINE PyObject *
PyArray_LookupSpecial_OnInstance(PyObject * obj,char const * name)104 PyArray_LookupSpecial_OnInstance(PyObject *obj, char const *name)
105 {
106 PyTypeObject *tp = Py_TYPE(obj);
107
108 /* We do not need to check for special attributes on trivial types */
109 if (_is_basic_python_type(tp)) {
110 return NULL;
111 }
112
113 return maybe_get_attr(obj, name);
114 }
115
116 #endif
117