Skip to content

Commit

Permalink
gh-96663: Add a better error message for __dict__-less classes setattr (
Browse files Browse the repository at this point in the history
  • Loading branch information
Gobot1234 authored Jul 22, 2023
1 parent 41ca164 commit cdeb1a6
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 5 deletions.
18 changes: 17 additions & 1 deletion Lib/test/test_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,14 @@ class A:
class B:
y = 0
__slots__ = ('z',)
class C:
__slots__ = ("y",)

def __setattr__(self, name, value) -> None:
if name == "z":
super().__setattr__("y", 1)
else:
super().__setattr__(name, value)

error_msg = "'A' object has no attribute 'x'"
with self.assertRaisesRegex(AttributeError, error_msg):
Expand All @@ -653,8 +661,16 @@ class B:
B().x
with self.assertRaisesRegex(AttributeError, error_msg):
del B().x
with self.assertRaisesRegex(AttributeError, error_msg):
with self.assertRaisesRegex(
AttributeError,
"'B' object has no attribute 'x' and no __dict__ for setting new attributes"
):
B().x = 0
with self.assertRaisesRegex(
AttributeError,
"'C' object has no attribute 'x'"
):
C().x = 0

error_msg = "'B' object attribute 'y' is read-only"
with self.assertRaisesRegex(AttributeError, error_msg):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_descrtut.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def merge(self, other):
>>> a.x1 = 1
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'defaultdict2' object has no attribute 'x1'
AttributeError: 'defaultdict2' object has no attribute 'x1' and no __dict__ for setting new attributes
>>>
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add a better, more introspect-able error message when setting attributes on classes without a ``__dict__`` and no slot member for the attribute.
16 changes: 13 additions & 3 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1576,9 +1576,18 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
}
if (dictptr == NULL) {
if (descr == NULL) {
PyErr_Format(PyExc_AttributeError,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
if (tp->tp_setattro == PyObject_GenericSetAttr) {
PyErr_Format(PyExc_AttributeError,
"'%.100s' object has no attribute '%U' and no "
"__dict__ for setting new attributes",
tp->tp_name, name);
}
else {
PyErr_Format(PyExc_AttributeError,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
}
set_attribute_error_context(obj, name);
}
else {
PyErr_Format(PyExc_AttributeError,
Expand Down Expand Up @@ -1611,6 +1620,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
}
set_attribute_error_context(obj, name);
}
done:
Py_XDECREF(descr);
Expand Down

0 comments on commit cdeb1a6

Please sign in to comment.