8000 gh-96663: Add a better error message for __dict__-less classes setatt… · python/cpython@cdeb1a6 · GitHub
[go: up one dir, main page]

Skip to content

Commit cdeb1a6

Browse files
authored
gh-96663: Add a better error message for __dict__-less classes setattr (#103232)
1 parent 41ca164 commit cdeb1a6

File tree

4 files changed

+32
-5
lines changed

4 files changed

+32
-5
lines changed

Lib/test/test_class.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,14 @@ class A:
641641
class B:
642642
y = 0
643643
__slots__ = ('z',)
644+
class C:
645+
__slots__ = ("y",)
646+
647+
def __setattr__(self, name, value) -> None:
648+
if name == "z":
649+
super().__setattr__("y", 1)
650+
else:
651+
super().__setattr__(name, value)
644652

645653
error_msg = "'A' object has no attribute 'x'"
646654
with self.assertRaisesRegex(AttributeError, error_msg):
@@ -653,8 +661,16 @@ class B:
653661
B().x
654662
with self.assertRaisesRegex(AttributeError, error_msg):
655663
del B().x
656-
with self.assertRaisesRegex(AttributeError, error_msg):
664+
with self.assertRaisesRegex(
665+
AttributeError,
666+
"'B' object has no attribute 'x' and no __dict__ for setting new attributes"
667+
):
657668
B().x = 0
669+
with self.assertRaisesRegex(
670+
AttributeError,
671+
"'C' object has no attribute 'x'"
672+
):
673+
C().x = 0
658674

659675
error_msg = "'B' object attribute 'y' is read-only"
660676
with self.assertRaisesRegex(AttributeError, error_msg):

Lib/test/test_descrtut.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def merge(self, other):
139139
>>> a.x1 = 1
140140
Traceback (most recent call last):
141141
File "<stdin>", line 1, in ?
142-
AttributeError: 'defaultdict2' object has no attribute 'x1'
142+
AttributeError: 'defaultdict2' object has no attribute 'x1' and no __dict__ for setting new attributes
143143
>>>
144144
145145
"""
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a better, more introspect-able error message when setting attributes on classes without a ``__dict__`` and no slot member for the attribute.

Objects/object.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,9 +1576,18 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
15761576
}
15771577
if (dictptr == NULL) {
15781578
if (descr == NULL) {
1579-
PyErr_Format(PyExc_AttributeError,
1580-
"'%.100s' object has no attribute '%U'",
1581-
tp->tp_name, name);
1579+
if (tp->tp_setattro == PyObject_GenericSetAttr) {
1580+
PyErr_Format(PyExc_AttributeError,
1581+
"'%.100s' object has no attribute '%U' and no "
1582+
"__dict__ for setting new attributes",
1583+
tp->tp_name, name);
1584+
}
1585+
else {
1586+
PyErr_Format(PyExc_AttributeError,
1587+
"'%.100s' object has no attribute '%U'",
1588+
tp->tp_name, name);
1589+
}
1590+
set_attribute_error_context(obj, name);
15821591
}
15831592
else {
15841593
PyErr_Format(PyExc_AttributeError,
@@ -1611,6 +1620,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
16111620
"'%.100s' object has no attribute '%U'",
16121621
tp->tp_name, name);
16131622
}
1623+
set_attribute_error_context(obj, name);
16141624
}
16151625
done:
16161626
Py_XDECREF(descr);

0 commit comments

Comments
 (0)
0