Search code examples
pythonattributeerrorcpythonpython-internals

Where is this AttributeError message implemented in CPython?


I think that the AttributeError message in this Python session

>>> class A: pass
... 
>>> A().x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'x'

is implemented in the function _PyObject_GenericGetAttrWithDict at these lines in CPython:

    if (!suppress) {
        PyErr_Format(PyExc_AttributeError,
                     "'%.50s' object has no attribute '%U'",
                     tp->tp_name, name);
    }

However I cannot find where the AttributeError message in this Python session

>>> class A: __slots__ = ('x',)
... 
>>> A().x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: x

is implemented in CPython. Could you provide a link to the exact lines in the GitHub repository?


Solution

  • It's here:

    case T_OBJECT_EX:
        v = *(PyObject **)addr;
        if (v == NULL)
            PyErr_SetString(PyExc_AttributeError, l->name);
        Py_XINCREF(v);
        break;
    

    That's an excerpt from PyMember_GetOne, which is where most of the logic for the __get__ method of the slot descriptor is implemented.

    You might also be interested in how that descriptor gets there. type.__new__ internally translates __slots__ to an array of PyMemberDef structs, one of the mechanisms classes written in C use to define access to their internal data. All PyMemberDef structs generated this way are marked T_OBJECT_EX, meaning they correspond to a PyObject * in the instance memory layout, and if the pointer is null, access should raise an AttributeError. PyType_Ready then generates the slot descriptors based on the PyMemberDef array.