Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,10 @@ Retrieving source code
.. versionchanged:: 3.5
Documentation strings are now inherited if not overridden.

.. versionchanged:: next
Documentation strings on :class:`~functools.cached_property`
objects are now inherited if not overriden.


.. function:: getcomments(object)

Expand Down
6 changes: 6 additions & 0 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,12 @@ def _finddoc(obj):
cls = _findclass(obj.fget)
if cls is None or getattr(cls, name) is not obj:
return None
# Should be tested before ismethoddescriptor()
elif isinstance(obj, functools.cached_property):
name = obj.attrname
cls = _findclass(obj.func)
if cls is None or getattr(cls, name) is not obj:
return None
elif ismethoddescriptor(obj) or isdatadescriptor(obj):
name = obj.__name__
cls = obj.__objclass__
Expand Down
38 changes: 38 additions & 0 deletions Lib/test/test_inspect/inspect_fodder3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from functools import cached_property

# docstring in parent, inherited in child
class ParentInheritDoc:
@cached_property
def foo(self):
"""docstring for foo defined in parent"""

class ChildInheritDoc(ParentInheritDoc): pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class ChildInheritDoc(ParentInheritDoc): pass
class ChildInheritDoc(ParentInheritDoc):
pass


class ChildInheritDefineDoc(ParentInheritDoc):
@cached_property
def foo(self):
pass

# Redefine foo as something other than cached_property
class ChildPropertyFoo(ParentInheritDoc):
@property
def foo(self):
"""docstring for the property foo"""
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pass


class ChildMethodFoo(ParentInheritDoc):
def foo(self):
"""docstring for the method foo"""

# docstring in child but not parent
class ParentNoDoc:
@cached_property
def foo(self):
pass

class ChildNoDoc(ParentNoDoc): pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class ChildNoDoc(ParentNoDoc): pass
class ChildNoDoc(ParentNoDoc):
pass


class ChildDefineDoc(ParentNoDoc):
@cached_property
def foo(self):
"""docstring for foo defined in child"""
21 changes: 21 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

from test.test_inspect import inspect_fodder as mod
from test.test_inspect import inspect_fodder2 as mod2
from test.test_inspect import inspect_fodder3 as mod3
from test.test_inspect import inspect_stringized_annotations
from test.test_inspect import inspect_deferred_annotations

Expand Down Expand Up @@ -665,6 +666,26 @@ def test_getdoc_inherited(self):
self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction),
'The automatic gainsaying.')

def test_getdoc_inherited_cached_property(self):
self.assertEqual(inspect.getdoc(mod3.ChildInheritDoc.foo),
'docstring for foo defined in parent')
self.assertEqual(inspect.getdoc(mod3.ChildInheritDefineDoc.foo),
'docstring for foo defined in parent')
Comment on lines +670 to +673
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.assertEqual(inspect.getdoc(mod3.ChildInheritDoc.foo),
'docstring for foo defined in parent')
self.assertEqual(inspect.getdoc(mod3.ChildInheritDefineDoc.foo),
'docstring for foo defined in parent')
doc = inspect.getdoc(mod3.ParentInheritDoc.foo)
self.assertEqual(doc, 'docstring for foo defined in parent')
self.assertEqual(inspect.getdoc(mod3.ChildInheritDoc.foo), doc)
self.assertEqual(inspect.getdoc(mod3.ChildInheritDefineDoc.foo), doc)


def test_getdoc_redefine_cached_property_as_other(self):
self.assertEqual(inspect.getdoc(mod3.ChildPropertyFoo.foo),
'docstring for the property foo')
self.assertEqual(inspect.getdoc(mod3.ChildMethodFoo.foo),
'docstring for the method foo')

def test_getdoc_define_cached_property(self):
self.assertEqual(inspect.getdoc(mod3.ChildDefineDoc.foo),
'docstring for foo defined in child')

def test_getdoc_nodoc_inherited(self):
self.assertEqual(inspect.getdoc(mod3.ChildNoDoc.foo),
None)
Comment on lines +686 to +687
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.assertEqual(inspect.getdoc(mod3.ChildNoDoc.foo),
None)
self.assertIsNone(inspect.getdoc(mod3.ChildNoDoc.foo))


@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
def test_finddoc(self):
finddoc = inspect._finddoc
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`inspect.getdoc` now correctly returns an inherited docstring on :class:`~functools.cached_property` objects if none is given in a subclass.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
:func:`inspect.getdoc` now correctly returns an inherited docstring on :class:`~functools.cached_property` objects if none is given in a subclass.
:func:`inspect.getdoc` now correctly returns an inherited docstring on
:class:`~functools.cached_property` objects if none is given in a subclass.

Loading