mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #4012 from avalentino/docstring-inheritance
Docstring inheritance
This commit is contained in:
commit
8b76d5b064
2
AUTHORS
2
AUTHORS
@ -57,7 +57,7 @@ Other contributors, listed alphabetically, are:
|
||||
* Stefan Seefeld -- toctree improvements
|
||||
* Gregory Szorc -- performance improvements
|
||||
* Taku Shimizu -- epub3 builder
|
||||
* Antonio Valentino -- qthelp builder
|
||||
* Antonio Valentino -- qthelp builder, docstring inheritance
|
||||
* Filip Vavera -- napoleon todo directive
|
||||
* Pauli Virtanen -- autodoc improvements, autosummary extension
|
||||
* Stefan van der Walt -- autosummary extension
|
||||
|
1
CHANGES
1
CHANGES
@ -28,6 +28,7 @@ Features added
|
||||
- ``ref.python`` (ref: #3866)
|
||||
* #3872: Add latex key to configure literal blocks caption position in PDF
|
||||
output (refs #3792, #1723)
|
||||
* In case of missing docstring try to retrieve doc from base classes (ref: #3140)
|
||||
|
||||
|
||||
Features removed
|
||||
|
@ -393,6 +393,16 @@ There are also new config values that you can set:
|
||||
If ``False`` is given, autodoc forcely suppresses the error if the imported
|
||||
module emits warnings. By default, ``True``.
|
||||
|
||||
.. confval:: autodoc_inherit_docstrings
|
||||
|
||||
This value controls the docstrings inheritance.
|
||||
If set to True the cocstring for classes or methods, if not explicitly set,
|
||||
is inherited form parents.
|
||||
|
||||
The default is ``True``.
|
||||
|
||||
.. versionadded:: 1.7
|
||||
|
||||
Docstring preprocessing
|
||||
-----------------------
|
||||
|
||||
|
@ -35,7 +35,7 @@ from sphinx.util import logging
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
|
||||
safe_getattr, object_description, is_builtin_class_method, \
|
||||
isenumclass, isenumattribute
|
||||
isenumclass, isenumattribute, getdoc
|
||||
from sphinx.util.docstrings import prepare_docstring
|
||||
|
||||
if False:
|
||||
@ -525,6 +525,8 @@ class Documenter(object):
|
||||
# type: (unicode, int) -> List[List[unicode]]
|
||||
"""Decode and return lines of the docstring(s) for the object."""
|
||||
docstring = self.get_attr(self.object, '__doc__', None)
|
||||
if docstring is None and self.env.config.autodoc_inherit_docstrings:
|
||||
docstring = getdoc(self.object)
|
||||
# make sure we have Unicode docstrings, then sanitize and split
|
||||
# into lines
|
||||
if isinstance(docstring, text_type):
|
||||
@ -682,6 +684,9 @@ class Documenter(object):
|
||||
isattr = False
|
||||
|
||||
doc = self.get_attr(member, '__doc__', None)
|
||||
if doc is None and self.env.config.autodoc_inherit_docstrings:
|
||||
doc = getdoc(member)
|
||||
|
||||
# if the member __doc__ is the same as self's __doc__, it's just
|
||||
# inherited and therefore not the member's doc
|
||||
cls = self.get_attr(member, '__class__', None)
|
||||
@ -1617,6 +1622,7 @@ def setup(app):
|
||||
app.add_config_value('autodoc_docstring_signature', True, True)
|
||||
app.add_config_value('autodoc_mock_imports', [], True)
|
||||
app.add_config_value('autodoc_warningiserror', True, True)
|
||||
app.add_config_value('autodoc_inherit_docstrings', True, True)
|
||||
app.add_event('autodoc-process-docstring')
|
||||
app.add_event('autodoc-process-signature')
|
||||
app.add_event('autodoc-skip-member')
|
||||
|
@ -11,6 +11,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import sys
|
||||
import typing
|
||||
import inspect
|
||||
from collections import OrderedDict
|
||||
@ -456,3 +457,102 @@ class Signature(object):
|
||||
', '.join(param_strings))
|
||||
|
||||
return qualified_name
|
||||
|
||||
|
||||
if sys.version_info >= (3, 5):
|
||||
getdoc = inspect.getdoc
|
||||
else:
|
||||
# code copied from the inspect.py module of the standard library
|
||||
# of Python 3.5
|
||||
|
||||
def _findclass(func):
|
||||
cls = sys.modules.get(func.__module__)
|
||||
if cls is None:
|
||||
return None
|
||||
if hasattr(func, 'im_class'):
|
||||
cls = func.im_class
|
||||
else:
|
||||
for name in func.__qualname__.split('.')[:-1]:
|
||||
cls = getattr(cls, name)
|
||||
if not inspect.isclass(cls):
|
||||
return None
|
||||
return cls
|
||||
|
||||
def _finddoc(obj):
|
||||
if inspect.isclass(obj):
|
||||
for base in obj.__mro__:
|
||||
if base is not object:
|
||||
try:
|
||||
doc = base.__doc__
|
||||
except AttributeError:
|
||||
continue
|
||||
if doc is not None:
|
||||
return doc
|
||||
return None
|
||||
|
||||
if inspect.ismethod(obj) and getattr(obj, '__self__', None):
|
||||
name = obj.__func__.__name__
|
||||
self = obj.__self__
|
||||
if (inspect.isclass(self) and
|
||||
getattr(getattr(self, name, None), '__func__')
|
||||
is obj.__func__):
|
||||
# classmethod
|
||||
cls = self
|
||||
else:
|
||||
cls = self.__class__
|
||||
elif inspect.isfunction(obj) or inspect.ismethod(obj):
|
||||
name = obj.__name__
|
||||
cls = _findclass(obj)
|
||||
if cls is None or getattr(cls, name) != obj:
|
||||
return None
|
||||
elif inspect.isbuiltin(obj):
|
||||
name = obj.__name__
|
||||
self = obj.__self__
|
||||
if (inspect.isclass(self) and
|
||||
self.__qualname__ + '.' + name == obj.__qualname__):
|
||||
# classmethod
|
||||
cls = self
|
||||
else:
|
||||
cls = self.__class__
|
||||
# Should be tested before isdatadescriptor().
|
||||
elif isinstance(obj, property):
|
||||
func = obj.fget
|
||||
name = func.__name__
|
||||
cls = _findclass(func)
|
||||
if cls is None or getattr(cls, name) is not obj:
|
||||
return None
|
||||
elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
|
||||
name = obj.__name__
|
||||
cls = obj.__objclass__
|
||||
if getattr(cls, name) is not obj:
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
for base in cls.__mro__:
|
||||
try:
|
||||
doc = getattr(base, name).__doc__
|
||||
except AttributeError:
|
||||
continue
|
||||
if doc is not None:
|
||||
return doc
|
||||
return None
|
||||
|
||||
def getdoc(object):
|
||||
"""Get the documentation string for an object.
|
||||
|
||||
All tabs are expanded to spaces. To clean up docstrings that are
|
||||
indented to line up with blocks of code, any whitespace than can be
|
||||
uniformly removed from the second line onwards is removed."""
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
if doc is None:
|
||||
try:
|
||||
doc = _finddoc(object)
|
||||
except (AttributeError, TypeError):
|
||||
return None
|
||||
if not isinstance(doc, str):
|
||||
return None
|
||||
return inspect.cleandoc(doc)
|
||||
|
@ -431,6 +431,13 @@ def test_get_doc():
|
||||
directive.env.config.autoclass_content = 'both'
|
||||
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
|
||||
|
||||
# NOTE: inspect.getdoc seems not to work with locally defined classes
|
||||
directive.env.config.autodoc_inherit_docstrings = False
|
||||
assert getdocl('method', Base.inheritedmeth) == ['Inherited function.']
|
||||
assert getdocl('method', Derived.inheritedmeth) == []
|
||||
directive.env.config.autodoc_inherit_docstrings = True
|
||||
assert getdocl('method', Derived.inheritedmeth) == ['Inherited function.']
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_test')
|
||||
def test_docstring_processing():
|
||||
@ -941,6 +948,12 @@ class Base(object):
|
||||
"""Inherited function."""
|
||||
|
||||
|
||||
class Derived(Base):
|
||||
def inheritedmeth(self):
|
||||
# no docstring here
|
||||
pass
|
||||
|
||||
|
||||
class Class(Base):
|
||||
"""Class to document."""
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user