mirror of https://github.com/python/cpython.git
bpo-43857: Improve the AttributeError message when deleting a missing attribute (#25424)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
43b135f94e
commit
a95138b2c5
|
@ -611,6 +611,49 @@ class A:
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
type.__setattr__(A, b'x', None)
|
type.__setattr__(A, b'x', None)
|
||||||
|
|
||||||
|
def testTypeAttributeAccessErrorMessages(self):
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
error_msg = "type object 'A' has no attribute 'x'"
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
A.x
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
del A.x
|
||||||
|
|
||||||
|
def testObjectAttributeAccessErrorMessages(self):
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
class B:
|
||||||
|
y = 0
|
||||||
|
__slots__ = ('z',)
|
||||||
|
|
||||||
|
error_msg = "'A' object has no attribute 'x'"
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
A().x
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
del A().x
|
||||||
|
|
||||||
|
error_msg = "'B' object has no attribute 'x'"
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
B().x
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
del B().x
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
B().x = 0
|
||||||
|
|
||||||
|
error_msg = "'B' object attribute 'y' is read-only"
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
del B().y
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
B().y = 0
|
||||||
|
|
||||||
|
error_msg = 'z'
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
B().z
|
||||||
|
with self.assertRaisesRegex(AttributeError, error_msg):
|
||||||
|
del B().z
|
||||||
|
|
||||||
def testConstructorErrorMessages(self):
|
def testConstructorErrorMessages(self):
|
||||||
# bpo-31506: Improves the error message logic for object_new & object_init
|
# bpo-31506: Improves the error message logic for object_new & object_init
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Improve the :exc:`AttributeError` message when deleting a missing attribute.
|
||||||
|
Patch by Géry Ogam.
|
|
@ -5472,7 +5472,9 @@ _PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
|
||||||
values->values[ix] = value;
|
values->values[ix] = value;
|
||||||
if (old_value == NULL) {
|
if (old_value == NULL) {
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
PyErr_SetObject(PyExc_AttributeError, name);
|
PyErr_Format(PyExc_AttributeError,
|
||||||
|
"'%.100s' object has no attribute '%U'",
|
||||||
|
Py_TYPE(obj)->tp_name, name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
_PyDictValues_AddToInsertionOrder(values, ix);
|
_PyDictValues_AddToInsertionOrder(values, ix);
|
||||||
|
|
|
@ -1382,7 +1382,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Py_INCREF(name);
|
Py_INCREF(name);
|
||||||
|
Py_INCREF(tp);
|
||||||
descr = _PyType_Lookup(tp, name);
|
descr = _PyType_Lookup(tp, name);
|
||||||
|
|
||||||
if (descr != NULL) {
|
if (descr != NULL) {
|
||||||
|
@ -1426,11 +1426,21 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
||||||
res = PyDict_SetItem(dict, name, value);
|
res = PyDict_SetItem(dict, name, value);
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
}
|
}
|
||||||
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
|
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
|
||||||
PyErr_SetObject(PyExc_AttributeError, name);
|
if (PyType_IsSubtype(tp, &PyType_Type)) {
|
||||||
|
PyErr_Format(PyExc_AttributeError,
|
||||||
|
"type object '%.50s' has no attribute '%U'",
|
||||||
|
((PyTypeObject*)obj)->tp_name, name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyErr_Format(PyExc_AttributeError,
|
||||||
|
"'%.100s' object has no attribute '%U'",
|
||||||
|
tp->tp_name, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
done:
|
done:
|
||||||
Py_XDECREF(descr);
|
Py_XDECREF(descr);
|
||||||
|
Py_DECREF(tp);
|
||||||
Py_DECREF(name);
|
Py_DECREF(name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue