Skip to content

Commit b19523a

Browse files
committed
Handle doc argument of property.__init__ in subclasses
1 parent 3faa9f7 commit b19523a

File tree

2 files changed

+41
-17
lines changed

2 files changed

+41
-17
lines changed

Lib/test/test_pydoc.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,25 @@ def test_issue8225(self):
449449
result, doc_loc = get_pydoc_text(xml.etree)
450450
self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link")
451451

452+
def test_issue41287(self):
453+
# Test issue41287 to ensure property subclass handles `doc` argument correctly
454+
455+
class Property(property):
456+
"""A subclass of builtin property"""
457+
458+
self.assertEqual(Property.__doc__, "A subclass of builtin property", "Docstring of `property` subclass is ignored")
459+
460+
doc = Property(None, None, None, "issue 41287 is fixed").__doc__
461+
self.assertEqual(doc, "issue 41287 is fixed", "Subclasses of `property` ignores `doc` constructor argument")
462+
463+
def getter(x):
464+
"""Getter docstring"""
465+
doc = Property(getter, None, None, "issue 41287 is fixed").__doc__
466+
self.assertEqual(doc, "issue 41287 is fixed", "Getter overrides explicit property docstring docstring")
467+
468+
doc = Property(getter, None, None, None).__doc__
469+
self.assertEqual(doc, "Getter docstring", "Getter docstring is not picked-up")
470+
452471
def test_getpager_with_stdin_none(self):
453472
previous_stdin = sys.stdin
454473
try:

Objects/descrobject.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,39 +1781,44 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
17811781
Py_XINCREF(fget);
17821782
Py_XINCREF(fset);
17831783
Py_XINCREF(fdel);
1784-
Py_XINCREF(doc);
17851784

17861785
Py_XSETREF(self->prop_get, fget);
17871786
Py_XSETREF(self->prop_set, fset);
17881787
Py_XSETREF(self->prop_del, fdel);
1789-
Py_XSETREF(self->prop_doc, doc);
1788+
Py_XSETREF(self->prop_doc, NULL);
17901789
Py_XSETREF(self->prop_name, NULL);
17911790

17921791
self->getter_doc = 0;
1792+
PyObject *get_doc = NULL;
17931793

17941794
/* if no docstring given and the getter has one, use that one */
17951795
if ((doc == NULL || doc == Py_None) && fget != NULL) {
1796-
PyObject *get_doc;
17971796
int rc = _PyObject_LookupAttr(fget, &_Py_ID(__doc__), &get_doc);
17981797
if (rc <= 0) {
17991798
return rc;
18001799
}
1801-
if (Py_IS_TYPE(self, &PyProperty_Type)) {
1802-
Py_XSETREF(self->prop_doc, get_doc);
1803-
}
1804-
else {
1805-
/* If this is a property subclass, put __doc__
1806-
in dict of the subclass instance instead,
1807-
otherwise it gets shadowed by __doc__ in the
1808-
class's dict. */
1809-
int err = PyObject_SetAttr(
1810-
(PyObject *)self, &_Py_ID(__doc__), get_doc);
1811-
Py_DECREF(get_doc);
1812-
if (err < 0)
1813-
return -1;
1814-
}
18151800
self->getter_doc = 1;
18161801
}
1802+
else {
1803+
get_doc = doc;
1804+
Py_XINCREF(get_doc);
1805+
}
1806+
1807+
1808+
if (Py_IS_TYPE(self, &PyProperty_Type)) {
1809+
Py_XSETREF(self->prop_doc, get_doc);
1810+
}
1811+
else {
1812+
/* If this is a property subclass, put __doc__
1813+
in dict of the subclass instance instead,
1814+
otherwise it gets shadowed by __doc__ in the
1815+
class's dict. */
1816+
int err = PyObject_SetAttr(
1817+
(PyObject *)self, &_Py_ID(__doc__), get_doc);
1818+
Py_XDECREF(get_doc);
1819+
if (err < 0)
1820+
return -1;
1821+
}
18171822

18181823
return 0;
18191824
}

0 commit comments

Comments
 (0)