Fix #6588: autodoc: Decorated inherited method has no documentation

This commit is contained in:
Takeshi KOMIYA 2020-04-26 11:24:15 +09:00
parent 1f5dab6446
commit 13113fc0b6
4 changed files with 36 additions and 5 deletions

View File

@ -68,6 +68,7 @@ Bugs fixed
* #6703: autodoc: incremental build does not work for imported objects * #6703: autodoc: incremental build does not work for imported objects
* #7564: autodoc: annotations not to be shown for descriptors * #7564: autodoc: annotations not to be shown for descriptors
* #6588: autodoc: Decorated inherited method has no documentation
* #7535: sphinx-autogen: crashes when custom template uses inheritance * #7535: sphinx-autogen: crashes when custom template uses inheritance
* #7536: sphinx-autogen: crashes when template uses i18n feature * #7536: sphinx-autogen: crashes when template uses i18n feature

View File

@ -436,7 +436,8 @@ class Documenter:
% self.__class__.__name__, % self.__class__.__name__,
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
docstring = getdoc(self.object, self.get_attr, docstring = getdoc(self.object, self.get_attr,
self.env.config.autodoc_inherit_docstrings) self.env.config.autodoc_inherit_docstrings,
self.parent, self.object_name)
if docstring: if docstring:
tab_width = self.directive.state.document.settings.tab_width tab_width = self.directive.state.document.settings.tab_width
return [prepare_docstring(docstring, ignore, tab_width)] return [prepare_docstring(docstring, ignore, tab_width)]
@ -557,7 +558,8 @@ class Documenter:
else: else:
isattr = False isattr = False
doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings) doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings,
self.parent, self.object_name)
if not isinstance(doc, str): if not isinstance(doc, str):
# Ignore non-string __doc__ # Ignore non-string __doc__
doc = None doc = None
@ -1250,7 +1252,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
if content in ('both', 'init'): if content in ('both', 'init'):
__init__ = self.get_attr(self.object, '__init__', None) __init__ = self.get_attr(self.object, '__init__', None)
initdocstring = getdoc(__init__, self.get_attr, initdocstring = getdoc(__init__, self.get_attr,
self.env.config.autodoc_inherit_docstrings) self.env.config.autodoc_inherit_docstrings,
self.parent, self.object_name)
# for new-style classes, no __init__ means default __init__ # for new-style classes, no __init__ means default __init__
if (initdocstring is not None and if (initdocstring is not None and
(initdocstring == object.__init__.__doc__ or # for pypy (initdocstring == object.__init__.__doc__ or # for pypy
@ -1260,7 +1263,8 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
# try __new__ # try __new__
__new__ = self.get_attr(self.object, '__new__', None) __new__ = self.get_attr(self.object, '__new__', None)
initdocstring = getdoc(__new__, self.get_attr, initdocstring = getdoc(__new__, self.get_attr,
self.env.config.autodoc_inherit_docstrings) self.env.config.autodoc_inherit_docstrings,
self.parent, self.object_name)
# for new-style classes, no __new__ means default __new__ # for new-style classes, no __new__ means default __new__
if (initdocstring is not None and if (initdocstring is not None and
(initdocstring == object.__new__.__doc__ or # for pypy (initdocstring == object.__new__.__doc__ or # for pypy

View File

@ -691,13 +691,14 @@ class Signature:
def getdoc(obj: Any, attrgetter: Callable = safe_getattr, def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
allow_inherited: bool = False) -> str: allow_inherited: bool = False, cls: Any = None, name: str = None) -> str:
"""Get the docstring for the object. """Get the docstring for the object.
This tries to obtain the docstring for some kind of objects additionally: This tries to obtain the docstring for some kind of objects additionally:
* partial functions * partial functions
* inherited docstring * inherited docstring
* inherited decorated methods
""" """
doc = attrgetter(obj, '__doc__', None) doc = attrgetter(obj, '__doc__', None)
if ispartial(obj) and doc == obj.__class__.__doc__: if ispartial(obj) and doc == obj.__class__.__doc__:
@ -705,4 +706,14 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
elif doc is None and allow_inherited: elif doc is None and allow_inherited:
doc = inspect.getdoc(obj) doc = inspect.getdoc(obj)
if doc is None and cls:
# inspect.getdoc() does not support some kind of inherited and decorated methods.
# This tries to obtain the docstring from super classes.
for basecls in getattr(cls, '__mro__', []):
meth = safe_getattr(basecls, name, None)
if meth:
doc = inspect.getdoc(meth)
if doc:
break
return doc return doc

View File

@ -564,3 +564,18 @@ def test_unpartial():
assert inspect.unpartial(func2) is func1 assert inspect.unpartial(func2) is func1
assert inspect.unpartial(func3) is func1 assert inspect.unpartial(func3) is func1
def test_getdoc_inherited_decorated_method():
class Foo:
def meth(self):
"""docstring."""
class Bar(Foo):
@functools.lru_cache()
def meth(self):
# inherited and decorated method
pass
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == "docstring."