In Speed up and clean up getting optional attributes in C code · Issue #76752 · python/cpython · GitHub I added private C API _PyObject_LookupAttr as a convenient function for getting optional attribute which combines PyObject_GetAttr(), PyErr_ExceptionMatches(PyExc_AttributeError) and PyErr_Clear().
int _PyObject_LookupAttr(PyObject *obj, PyObject *attr_name, PyObject **result);
It returns one of three values, 0, -1 or 1 and stores the result or NULL in *result.
- Return 1 and set
*result != NULLif an attribute is found. - Return 0 and set
*result == NULLif an attribute is not found;
an AttributeError is silenced. - Return -1 and set
*result == NULLif an error other than AttributeError
is raised.
It allows to replace the following code
PyObject *result = PyObject_GetAttr(obj, attr_name);
if (result == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
// handle "no such attribute" case
}
else {
// handle other errors
}
}
else {
// handle "has attribute" case
}
as
PyObject *result;
if (_PyObject_LookupAttr(obj, attr_name, &result) < 0) {
// handle other errors
}
else if (result == NULL) {
// handle "no such attribute" case
}
else {
// handle "has attribute" case
}
Advantages of using this function:
- It simplifies the code, especially if “no such attribute” and “has attribute” cases are handles the same.
- Due to this, it allowed to fix incorrect code which ignored other errors or treated them the same as AttributeError.
- It avoids raising and catching the AttributeError exception at first place, if the attribute is looked up in object’s
__dict__.
Currently this function is used around 350 times in the CPython code. I think that it can be useful for use in third-party code too, because it makes easier to write correct code. While the function can be re-implemented using other public C API (PyObject_GetAttr(), PyErr_ExceptionMatches(), PyExc_AttributeError and PyErr_Clear()), it will not provide the performance gain.
Now, I have a question about the name. I chose different verb to differentiate from PyObject_GetAttr, but I am not sure that it is the best option. I considered other variants with Find, Search, GetOpt, TryGet, etc. Lookup looked good to me, but it would conflict with _PyType_Lookup() and _PyObject_LookupSpecialId() if we made them public too. Whatever name we choose, we should perhaps use the same verb in names of other similar functions (getting optional item in mapping [2] and specifically dict [3]).
Do you support adding such function to the public C API? What is the best name fot it and for similar functions?
[1] Speed up and clean up getting optional attributes in C code · Issue #76752 · python/cpython · GitHub
[2] Add _PyMapping_LookupItem() · Issue #106307 · python/cpython · GitHub
[3] C API: Add PyDict_GetItemRef() function · Issue #106004 · python/cpython · GitHub