This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Fix the AttributeError message for deletion of a missing attribute
Type: enhancement Stage: patch review
Components: Interpreter Core Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: maggyero, pablogsal, rhettinger
Priority: normal Keywords: patch

Created on 2021-04-15 16:31 by maggyero, last changed 2022-04-11 14:59 by admin.

Pull Requests
URL Status Linked Edit
PR 25424 open maggyero, 2021-04-15 16:32
Messages (1)
msg391140 - (view) Author: Géry (maggyero) * Date: 2021-04-15 16:31
Compare the `AttributeError` messages in this interactive Python session:

```python
>>> class A:
...     y = 0
...     __slots__ = ('z',)
... 
>>> A().x
[…]
AttributeError: 'A' object has no attribute 'x'
>>> A().x = 1
[…]
AttributeError: 'A' object has no attribute 'x'
>>> del A().x
[…]
AttributeError: 'A' object has no attribute 'x'
>>> A().y
0
>>> A().y = 2
[…]
AttributeError: 'A' object attribute 'y' is read-only
>>> del A().y
[…]
AttributeError: 'A' object attribute 'y' is read-only
>>> A().z
[…]
AttributeError: z
>>> A().z = 3
>>> del A().z
[…]
AttributeError: z
```

with the `AttributeError` messages in that one:

```python
>>> class B: pass
... 
>>> B().x
[…]
AttributeError: 'B' object has no attribute 'x'
>>> B().x = 1
>>> del B().x
[…]
AttributeError: x
```

The message `AttributeError: x` from `del B().x` does not feel right. I expect this message to be the same as the message `AttributeError: 'B' object has no attribute 'x'` from `B().x`, since in both cases the object `B()` has no attribute `'x'`.

I have checked on PyPy 7.3.3 (Python 3.7.9) and it uses the expected message `AttributeError: 'B' object has no attribute 'x'` from `B().x` for `del B().x`. So this confirms my initial suspicion.

----

In CPython, the `AttributeError` message for attribute retrieval is implemented [here](https://github.com/python/cpython/blob/v3.9.4/Objects/object.c#L1266-L1270) (except for [slot retrieval](https://github.com/python/cpython/blob/v3.9.4/Python/structmember.c#L70-L75)):

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

And the `AttributeError` messages for attribute assignment and deletion are implemented [here](https://github.com/python/cpython/blob/v3.9.4/Objects/object.c#L1324-L1350) (except for [slot deletion](https://github.com/python/cpython/blob/v3.9.4/Python/structmember.c#L112-L118)):

```c
    if (dict == NULL) {
        dictptr = _PyObject_GetDictPtr(obj);
        if (dictptr == NULL) {
            if (descr == NULL) {
                PyErr_Format(PyExc_AttributeError,
                             "'%.100s' object has no attribute '%U'",
                             tp->tp_name, name);
            }
            else {
                PyErr_Format(PyExc_AttributeError,
                             "'%.50s' object attribute '%U' is read-only",
                             tp->tp_name, name);
            }
            goto done;
        }
        res = _PyObjectDict_SetItem(tp, dictptr, name, value);
    }
    else {
        Py_INCREF(dict);
        if (value == NULL)
            res = PyDict_DelItem(dict, name);
        else
            res = PyDict_SetItem(dict, name, value);
        Py_DECREF(dict);
    }
    if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
        PyErr_SetObject(PyExc_AttributeError, name);
```

So it is the last line `PyErr_SetObject(PyExc_AttributeError, name);` that would be updated. Note that `_PyObjectDict_SetItem` delegates to `PyDict_DelItem` (if `value` is `NULL`) or `PyDict_SetItem` (if `value` is not `NULL`), and that only `PyDict_DelItem` can [set an exception](https://github.com/python/cpython/blob/v3.9.4/Objects/dictobject.c#L1655-L1657) `PyExc_KeyError`, which is then translated to an exception `PyExc_AttributeError` in the last line.
History
Date User Action Args
2022-04-11 14:59:44adminsetgithub: 88023
2021-04-18 05:53:26rhettingersetnosy: + rhettinger
2021-04-16 21:21:07terry.reedysetnosy: + pablogsal

versions: - Python 3.6, Python 3.7
2021-04-15 16:32:00maggyerosetkeywords: + patch
stage: patch review
pull_requests: + pull_request24158
2021-04-15 16:31:31maggyerocreate