Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
35 changes: 35 additions & 0 deletions Lib/test/test_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ def test_property_set_name_incorrect_args(self):
class PropertySub(property):
"""This is a subclass of property"""

class PropertySubWoDoc(property):
pass

class PropertySubSlots(property):
"""This is a subclass of property that defines __slots__"""
__slots__ = ()
Expand All @@ -237,6 +240,38 @@ def spam(self):
else:
raise Exception("AttributeError not raised")

@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def test_issue41287(self):

self.assertEqual(PropertySub.__doc__, "This is a subclass of property",
"Docstring of `property` subclass is ignored")

doc = PropertySub(None, None, None, "issue 41287 is fixed").__doc__
self.assertEqual(doc, "issue 41287 is fixed",
"Subclasses of `property` ignores `doc` constructor argument")

def getter(x):
"""Getter docstring"""

def getter_wo_doc(x):
pass

for ps in property, PropertySub, PropertySubWoDoc:
doc = ps(getter, None, None, "issue 41287 is fixed").__doc__
self.assertEqual(doc, "issue 41287 is fixed",
"Getter overrides explicit property docstring (%s)" % ps.__name__)

doc = ps(getter, None, None, None).__doc__
self.assertEqual(doc, "Getter docstring", "Getter docstring is not picked-up (%s)" % ps.__name__)

doc = ps(getter_wo_doc, None, None, "issue 41287 is fixed").__doc__
self.assertEqual(doc, "issue 41287 is fixed",
"Getter overrides explicit property docstring (%s)" % ps.__name__)

doc = ps(getter_wo_doc, None, None, None).__doc__
self.assertIsNone(doc, "Property class doc appears in instance __doc__ (%s)" % ps.__name__)

@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def test_docstring_copy(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix handling of the ``doc`` argument in subclasses of :func:`property`.
50 changes: 32 additions & 18 deletions Objects/descrobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1781,40 +1781,54 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
Py_XINCREF(fget);
Py_XINCREF(fset);
Py_XINCREF(fdel);
Py_XINCREF(doc);

Py_XSETREF(self->prop_get, fget);
Py_XSETREF(self->prop_set, fset);
Py_XSETREF(self->prop_del, fdel);
Py_XSETREF(self->prop_doc, doc);
Py_XSETREF(self->prop_doc, NULL);
Py_XSETREF(self->prop_name, NULL);

self->getter_doc = 0;
PyObject *prop_doc = NULL;

if (doc != NULL && doc != Py_None) {
prop_doc = doc;
Py_XINCREF(prop_doc);
}
/* if no docstring given and the getter has one, use that one */
if ((doc == NULL || doc == Py_None) && fget != NULL) {
PyObject *get_doc;
int rc = _PyObject_LookupAttr(fget, &_Py_ID(__doc__), &get_doc);
else if (fget != NULL) {
int rc = _PyObject_LookupAttr(fget, &_Py_ID(__doc__), &prop_doc);
if (rc <= 0) {
return rc;
}
if (Py_IS_TYPE(self, &PyProperty_Type)) {
Py_XSETREF(self->prop_doc, get_doc);
}
else {
/* If this is a property subclass, put __doc__
in dict of the subclass instance instead,
otherwise it gets shadowed by __doc__ in the
class's dict. */
int err = PyObject_SetAttr(
(PyObject *)self, &_Py_ID(__doc__), get_doc);
Py_DECREF(get_doc);
if (err < 0)
return -1;
if (prop_doc == Py_None) {
prop_doc = NULL;
Py_DECREF(Py_None);
}
self->getter_doc = 1;
}

if (Py_IS_TYPE(self, &PyProperty_Type)) {
if (prop_doc != NULL && prop_doc != Py_None) {
Py_XSETREF(self->prop_doc, prop_doc);
}
} else {
/* If this is a property subclass, put __doc__
in dict of the subclass instance instead,
otherwise it gets shadowed by __doc__ in the
class's dict. */

if (prop_doc == NULL) {
prop_doc = Py_None;
Py_INCREF(prop_doc);
}
int err = PyObject_SetAttr(
(PyObject *)self, &_Py_ID(__doc__), prop_doc);
Py_XDECREF(prop_doc);
if (err < 0)
return -1;
}

return 0;
}

Expand Down