@@ -92,6 +92,68 @@ PyArray_LookupSpecial(PyObject *obj, char const *name)
92
92
return maybe_get_attr ((PyObject * )tp , name );
93
93
}
94
94
95
+ /*
96
+ * Stripped down version of PyObject_GetAttrString(obj, name) that does not
97
+ * raise PyExc_AttributeError.
98
+ *
99
+ * This allows it to avoid creating then discarding exception objects when
100
+ * performing lookups on objects without any attributes.
101
+ *
102
+ * Returns attribute value on success, NULL without an exception set if
103
+ * there is no such attribute, and NULL with an exception on failure.
104
+ */
105
+ static NPY_INLINE PyObject *
106
+ _maybe_get_attr_unicode (PyObject * obj , char const * name , PyObject * name_unicode )
107
+ {
108
+ PyTypeObject * tp = Py_TYPE (obj );
109
+ PyObject * res = (PyObject * )NULL ;
110
+
111
+ /* Attribute referenced by (char *)name */
112
+ if (tp -> tp_getattr != NULL ) {
113
+ res = (* tp -> tp_getattr )(obj , (char * )name );
114
+ if (res == NULL && PyErr_ExceptionMatches (PyExc_AttributeError )) {
115
+ PyErr_Clear ();
116
+ }
117
+ }
118
+ /* Attribute referenced by (PyObject *)name */
119
+ else if (tp -> tp_getattro != NULL ) {
120
+ res = (* tp -> tp_getattro )(obj , name_unicode );
121
+ if (res == NULL && PyErr_ExceptionMatches (PyExc_AttributeError )) {
122
+ PyErr_Clear ();
123
+ }
124
+ }
125
+ return res ;
126
+ }
127
+
128
+ /*
129
+ * Lookup a special method "__array_ufunc__", following the python approach of looking up
130
+ * on the type object, rather than on the instance itself.
131
+ *
132
+ * Assumes that the special method is a numpy-specific one, so does not look
133
+ * at builtin types, nor does it look at a base ndarray.
134
+ *
135
+ * In future, could be made more like _Py_LookupSpecial
136
+ */
137
+ static NPY_INLINE PyObject *
138
+ PyArray_Lookup_Array_UFunc (PyObject * obj )
139
+ {
140
+ static const char * name = "__array_ufunc__" ;
141
+ static PyObject * name_unicode = NULL ;
142
+
143
+ /* On first entry, cache unicode version of __array_ufunc__ */
144
+ if (name_unicode == NULL ) {
145
+ name_unicode = PyUnicode_InternFromString (name );
146
+ }
147
+
148
+ PyTypeObject * tp = Py_TYPE (obj );
149
+
150
+ /* We do not need to check for special attributes on trivial types */
151
+ if (_is_basic_python_type (tp )) {
152
+ return NULL ;
153
+ }
154
+ return _maybe_get_attr_unicode ((PyObject * )tp , name , name_unicode );
155
+ }
156
+
95
157
/*
96
158
* PyArray_LookupSpecial_OnInstance:
97
159
*
0 commit comments