Skip to content

gh-98963: Add a note to the error for property subclasses without __doc__ #99058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Lib/test/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,18 @@ class PropertySubSlots(property):
class PropertySubclassTests(unittest.TestCase):

def test_slots_docstring_copy_exception(self):
try:
with self.assertRaises(AttributeError) as cm:
class Foo(object):
@PropertySubSlots
def spam(self):
"""Trying to copy this docstring will raise an exception"""
return 1
except AttributeError:
pass
else:
raise Exception("AttributeError not raised")
# gh-98963: A note is added to the exception when we don't have
# a writable __doc__.
notes = cm.exception.__notes__
wanted = "subclasses of 'property' need to provide a writable __doc__"
self.assertTrue(any(note.startswith(wanted) for note in notes), notes)


@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add a note to the ``AttributeError`` raised when instantiating a subclass of
:py:class:`property` that does not have a writable ``__doc__`` attribute.
(Creating such a subclass is possible via the C API.)
22 changes: 21 additions & 1 deletion Objects/descrobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1826,8 +1826,28 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
int err = PyObject_SetAttr(
(PyObject *)self, &_Py_ID(__doc__), prop_doc);
Py_XDECREF(prop_doc);
if (err < 0)
if (err < 0) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
// gh-98963: the subclass doesn't have a __dict__
// (probably it was subclassed using C API).
// Add a note to prevent surprises.
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
PyErr_NormalizeException(&type, &value, &traceback);
if (value) {
PyObject_CallMethod(
value, "add_note", "s",
"subclasses of 'property' need to provide a writable "
"__doc__ attribute (e.g. a __doc__ member or a "
"__dict__) to avoid per-property docstrings being "
"shadowed by the subclass docstring");
// ignore errors while setting the note
PyErr_Clear();
}
PyErr_Restore(type, value, traceback);
}
return -1;
}
}

return 0;
Expand Down