summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_property.py34
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-10-09-15-14-53.bpo-24766.c_C1Wc.rst1
-rw-r--r--Objects/descrobject.c19
3 files changed, 39 insertions, 15 deletions
diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py
index 408e64f..b7a2219 100644
--- a/Lib/test/test_property.py
+++ b/Lib/test/test_property.py
@@ -465,6 +465,40 @@ class PropertySubclassTests(unittest.TestCase):
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
+ def test_prefer_explicit_doc(self):
+ # Issue 25757: subclasses of property lose docstring
+ self.assertEqual(property(doc="explicit doc").__doc__, "explicit doc")
+ self.assertEqual(PropertySub(doc="explicit doc").__doc__, "explicit doc")
+
+ class Foo:
+ spam = PropertySub(doc="spam explicit doc")
+
+ @spam.getter
+ def spam(self):
+ """ignored as doc already set"""
+ return 1
+
+ def _stuff_getter(self):
+ """ignored as doc set directly"""
+ stuff = PropertySub(doc="stuff doc argument", fget=_stuff_getter)
+
+ #self.assertEqual(Foo.spam.__doc__, "spam explicit doc")
+ self.assertEqual(Foo.stuff.__doc__, "stuff doc argument")
+
+ def test_property_no_doc_on_getter(self):
+ # If a property's getter has no __doc__ then the property's doc should
+ # be None; test that this is consistent with subclasses as well; see
+ # GH-2487
+ class NoDoc:
+ @property
+ def __doc__(self):
+ raise AttributeError
+
+ self.assertEqual(property(NoDoc()).__doc__, None)
+ self.assertEqual(PropertySub(NoDoc()).__doc__, None)
+
+ @unittest.skipIf(sys.flags.optimize >= 2,
+ "Docstrings are omitted with -O2 and above")
def test_property_setter_copies_getter_docstring(self):
class Foo(object):
def __init__(self): self._spam = 1
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-10-09-15-14-53.bpo-24766.c_C1Wc.rst b/Misc/NEWS.d/next/Core and Builtins/2018-10-09-15-14-53.bpo-24766.c_C1Wc.rst
new file mode 100644
index 0000000..93a8562
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-10-09-15-14-53.bpo-24766.c_C1Wc.rst
@@ -0,0 +1 @@
+Fix handling of ``doc`` argument to subclasses of ``property``.
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 1b7e2fd..4eccd17 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -1859,22 +1859,9 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
/* if no docstring given and the getter has one, use that one */
else if (fget != NULL) {
int rc = PyObject_GetOptionalAttr(fget, &_Py_ID(__doc__), &prop_doc);
- if (rc <= 0) {
+ if (rc < 0) {
return rc;
}
- if (!Py_IS_TYPE(self, &PyProperty_Type) &&
- prop_doc != NULL && prop_doc != Py_None) {
- // This oddity preserves the long existing behavior of surfacing
- // an AttributeError when using a dict-less (__slots__) property
- // subclass as a decorator on a getter method with a docstring.
- // See PropertySubclassTest.test_slots_docstring_copy_exception.
- int err = PyObject_SetAttr(
- (PyObject *)self, &_Py_ID(__doc__), prop_doc);
- if (err < 0) {
- Py_DECREF(prop_doc); // release our new reference.
- return -1;
- }
- }
if (prop_doc == Py_None) {
prop_doc = NULL;
Py_DECREF(Py_None);
@@ -1902,7 +1889,9 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
Py_DECREF(prop_doc);
if (err < 0) {
assert(PyErr_Occurred());
- if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ if (!self->getter_doc &&
+ PyErr_ExceptionMatches(PyExc_AttributeError))
+ {
PyErr_Clear();
// https://github.com/python/cpython/issues/98963#issuecomment-1574413319
// Python silently dropped this doc assignment through 3.11.