8000 [3.12] bpo-24766: doc= argument to subclasses of property not handled… · python/cpython@7545b7c · GitHub
[go: up one dir, main page]

Skip to content

Commit 7545b7c

Browse files
[3.12] bpo-24766: doc= argument to subclasses of property not handled correctly (GH-2487) (GH-120312)
(cherry picked from commit 4829522) Co-authored-by: E. M. Bray <erik.bray@lri.fr>
1 parent ff980e3 commit 7545b7c

File tree

3 files changed

+39
-15
lines changed

3 files changed

+39
-15
lines changed

Lib/test/test_property.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,40 @@ def getter3(self):
431431
self.assertEqual(p.__doc__, "user")
432432
self.assertEqual(p2.__doc__, "user")
433433

434+
@unittest.skipIf(sys.flags.optimize >= 2,
435+
"Docstrings are omitted with -O2 and above")
436+
def test_prefer_explicit_doc(self):
437+
# Issue 25757: subclasses of property lose docstring
438+
self.assertEqual(property(doc="explicit doc").__doc__, "explicit doc")
439+
self.assertEqual(PropertySub(doc="explicit doc").__doc__, "explicit doc")
440+
441+
class Foo:
442+
spam = PropertySub(doc="spam explicit doc")
443+
444+
@spam.getter
445+
def spam(self):
446+
"""ignored as doc already set"""
447+
return 1
448+
449+
def _stuff_getter(self):
450+
"""ignored as doc set directly"""
451+
stuff = PropertySub(doc="stuff doc argument", fget=_stuff_getter)
452+
453+
#self.assertEqual(Foo.spam.__doc__, "spam explicit doc")
454+
self.assertEqual(Foo.stuff.__doc__, "stuff doc argument")
455+
456+
def test_property_no_doc_on_getter(self):
457+
# If a property's getter has no __doc__ then the property's doc should
458+
# be None; test that this is consistent with subclasses as well; see
459+
# GH-2487
460+
class NoDoc:
461+
@property
462+
def __doc__(self):
463+
raise AttributeError
464+
465+
self.assertEqual(property(NoDoc()).__doc__, None)
466+
self.assertEqual(PropertySub(NoDoc()).__doc__, None)
467+
434468
@unittest.skipIf(sys.flags.optimize >= 2,
435469
"Docstrings are omitted with -O2 and above")
436470
def test_property_setter_copies_getter_docstring(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix handling of ``doc`` argument to subclasses of ``property``.

Objects/descrobject.c

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,22 +1788,9 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
17881788
/* if no docstring given and the getter has one, use that one */
17891789
else if (fget != NULL) {
17901790
int rc = _PyObject_LookupAttr(fget, &_Py_ID(__doc__), &prop_doc);
1791-
if (rc <= 0) {
1791+
if (rc < 0) {
17921792
return rc;
17931793
}
1794-
if (!Py_IS_TYPE(self, &PyProperty_Type) &&
1795-
prop_doc != NULL && prop_doc != Py_None) {
1796-
// This oddity preserves the long existing behavior of surfacing
1797-
// an AttributeError when using a dict-less (__slots__) property
1798-
// subclass as a decorator on a getter method with a docstring.
1799-
// See PropertySubclassTest.test_slots_docstring_copy_exception.
1800-
int err = PyObject_SetAttr(
1801-
(PyObject *)self, &_Py_ID(__doc__), prop_doc);
1802-
if (err < 0) {
1803-
Py_DECREF(prop_doc); // release our new reference.
1804-
return -1;
1805-
}
1806-
}
18071794
if (prop_doc == Py_None) {
18081795
prop_doc = NULL;
18091796
Py_DECREF(Py_None);
@@ -1831,7 +1818,9 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
18311818
Py_DECREF(prop_doc);
18321819
if (err < 0) {
18331820
assert(PyErr_Occurred());
1834-
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
1821+
if (!self->getter_doc &&
1822+
PyErr_ExceptionMatches(PyExc_AttributeError))
1823+
{
18351824
PyErr_Clear();
18361825
// https://github.com/python/cpython/issues/98963#issuecomment-1574413319
18371826
// Python silently dropped this doc assignment through 3.11.

0 commit comments

Comments
 (0)
0