Skip to content

Commit 70748bd

Browse files
authored
pythongh-131116: Fix inspect.getdoc() to work with cached_property objects (pythonGH-131165)
1 parent 23d85a2 commit 70748bd

File tree

5 files changed

+70
-0
lines changed

5 files changed

+70
-0
lines changed

Doc/library/inspect.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,9 @@ Retrieving source code
639639
.. versionchanged:: next
640640
Added parameters *inherit_class_doc* and *fallback_to_class_doc*.
641641

642+
Documentation strings on :class:`~functools.cached_property`
643+
objects are now inherited if not overriden.
644+
642645

643646
.. function:: getcomments(object)
644647

Lib/inspect.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,12 @@ def _finddoc(obj, *, search_in_class=True):
747747
cls = _findclass(obj.fget)
748748
if cls is None or getattr(cls, name) is not obj:
749749
return None
750+
# Should be tested before ismethoddescriptor()
751+
elif isinstance(obj, functools.cached_property):
752+
name = obj.attrname
753+
cls = _findclass(obj.func)
754+
if cls is None or getattr(cls, name) is not obj:
755+
return None
750756
elif ismethoddescriptor(obj) or isdatadescriptor(obj):
751757
name = obj.__name__
752758
cls = obj.__objclass__
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from functools import cached_property
2+
3+
# docstring in parent, inherited in child
4+
class ParentInheritDoc:
5+
@cached_property
6+
def foo(self):
7+
"""docstring for foo defined in parent"""
8+
9+
class ChildInheritDoc(ParentInheritDoc):
10+
pass
11+
12+
class ChildInheritDefineDoc(ParentInheritDoc):
13+
@cached_property
14+
def foo(self):
15+
pass
16+
17+
# Redefine foo as something other than cached_property
18+
class ChildPropertyFoo(ParentInheritDoc):
19+
@property
20+
def foo(self):
21+
"""docstring for the property foo"""
22+
23+
class ChildMethodFoo(ParentInheritDoc):
24+
def foo(self):
25+
"""docstring for the method foo"""
26+
27+
# docstring in child but not parent
28+
class ParentNoDoc:
29+
@cached_property
30+
def foo(self):
31+
pass
32+
33+
class ChildNoDoc(ParentNoDoc):
34+
pass
35+
36+
class ChildDefineDoc(ParentNoDoc):
37+
@cached_property
38+
def foo(self):
39+
"""docstring for foo defined in child"""

Lib/test/test_inspect/test_inspect.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
from test.test_inspect import inspect_fodder as mod
4848
from test.test_inspect import inspect_fodder2 as mod2
49+
from test.test_inspect import inspect_fodder3 as mod3
4950
from test.test_inspect import inspect_stringized_annotations
5051
from test.test_inspect import inspect_deferred_annotations
5152

@@ -714,6 +715,25 @@ class B(A):
714715
b.__doc__ = 'Instance'
715716
self.assertEqual(inspect.getdoc(b, fallback_to_class_doc=False), 'Instance')
716717

718+
def test_getdoc_inherited_cached_property(self):
719+
doc = inspect.getdoc(mod3.ParentInheritDoc.foo)
720+
self.assertEqual(doc, 'docstring for foo defined in parent')
721+
self.assertEqual(inspect.getdoc(mod3.ChildInheritDoc.foo), doc)
722+
self.assertEqual(inspect.getdoc(mod3.ChildInheritDefineDoc.foo), doc)
723+
724+
def test_getdoc_redefine_cached_property_as_other(self):
725+
self.assertEqual(inspect.getdoc(mod3.ChildPropertyFoo.foo),
726+
'docstring for the property foo')
727+
self.assertEqual(inspect.getdoc(mod3.ChildMethodFoo.foo),
728+
'docstring for the method foo')
729+
730+
def test_getdoc_define_cached_property(self):
731+
self.assertEqual(inspect.getdoc(mod3.ChildDefineDoc.foo),
732+
'docstring for foo defined in child')
733+
734+
def test_getdoc_nodoc_inherited(self):
735+
self.assertIsNone(inspect.getdoc(mod3.ChildNoDoc.foo))
736+
717737
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
718738
def test_finddoc(self):
719739
finddoc = inspect._finddoc
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`inspect.getdoc` now correctly returns an inherited docstring on
2+
:class:`~functools.cached_property` objects if none is given in a subclass.

0 commit comments

Comments
 (0)