Close #5923: autodoc: allow not to document inherited members of specific super class

This commit is contained in:
Takeshi KOMIYA 2019-11-30 17:21:21 +09:00
parent 9d39b187c1
commit 7c79a6b8d1
4 changed files with 73 additions and 12 deletions

View File

@ -14,6 +14,8 @@ Incompatible changes
* Drop features and APIs deprecated in 1.8.x * Drop features and APIs deprecated in 1.8.x
* #247: autosummary: stub files are overwritten automatically by default. see * #247: autosummary: stub files are overwritten automatically by default. see
:confval:`autosummary_generate_overwrite` to change the behavior :confval:`autosummary_generate_overwrite` to change the behavior
* #5923: autodoc: the members of ``object`` class are not documented by default
when ``:inherited-members:`` and ``:special-members:`` are given.
Deprecated Deprecated
---------- ----------
@ -23,6 +25,8 @@ Features added
* #247: autosummary: Add :confval:`autosummary_generate_overwrite` to overwrite * #247: autosummary: Add :confval:`autosummary_generate_overwrite` to overwrite
old stub file old stub file
* #5923: autodoc: ``:inherited-members:`` option takes a name of anchestor class
not to document inherited members of the class and uppers
Bugs fixed Bugs fixed
---------- ----------

View File

@ -157,7 +157,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
* For classes and exceptions, members inherited from base classes will be * For classes and exceptions, members inherited from base classes will be
left out when documenting all members, unless you give the left out when documenting all members, unless you give the
``inherited-members`` flag option, in addition to ``members``:: ``inherited-members`` option, in addition to ``members``::
.. autoclass:: Noodle .. autoclass:: Noodle
:members: :members:
@ -166,11 +166,19 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
This can be combined with ``undoc-members`` to document *all* available This can be combined with ``undoc-members`` to document *all* available
members of the class or module. members of the class or module.
It can take an anchestor class not to document inherited members from it.
By default, members of ``object`` class are not documented. To show them
all, give ``None`` to the option.
Note: this will lead to markup errors if the inherited members come from a Note: this will lead to markup errors if the inherited members come from a
module whose docstrings are not reST formatted. module whose docstrings are not reST formatted.
.. versionadded:: 0.3 .. versionadded:: 0.3
.. versionchanged:: 3.0
It takes an anchestor class name as an argument.
* It's possible to override the signature for explicitly documented callable * It's possible to override the signature for explicitly documented callable
objects (functions, methods, classes) with the regular syntax that will objects (functions, methods, classes) with the regular syntax that will
override the signature gained from introspection:: override the signature gained from introspection::

View File

@ -82,6 +82,14 @@ def members_set_option(arg: Any) -> Union[object, Set[str]]:
return {x.strip() for x in arg.split(',')} return {x.strip() for x in arg.split(',')}
def inherited_members_option(arg: Any) -> Union[object, Set[str]]:
"""Used to convert the :members: option to auto directives."""
if arg is None:
return 'object'
else:
return arg
SUPPRESS = object() SUPPRESS = object()
@ -515,6 +523,17 @@ class Documenter:
The user can override the skipping decision by connecting to the The user can override the skipping decision by connecting to the
``autodoc-skip-member`` event. ``autodoc-skip-member`` event.
""" """
def is_filtered_inherited_member(name: str) -> bool:
if inspect.isclass(self.object):
for cls in self.object.__mro__:
if cls.__name__ == self.options.inherited_members and cls != self.object:
# given member is a member of specified *super class*
return True
elif name in cls.__dict__:
return False
return False
ret = [] ret = []
# search for members in source code too # search for members in source code too
@ -545,12 +564,15 @@ class Documenter:
if want_all and membername.startswith('__') and \ if want_all and membername.startswith('__') and \
membername.endswith('__') and len(membername) > 4: membername.endswith('__') and len(membername) > 4:
# special __methods__ # special __methods__
if self.options.special_members is ALL and \ if self.options.special_members is ALL:
membername != '__doc__': if membername == '__doc__':
keep = False
elif is_filtered_inherited_member(membername):
keep = False
else:
keep = has_doc or self.options.undoc_members keep = has_doc or self.options.undoc_members
elif self.options.special_members and \ elif self.options.special_members:
self.options.special_members is not ALL and \ if membername in self.options.special_members:
membername in self.options.special_members:
keep = has_doc or self.options.undoc_members keep = has_doc or self.options.undoc_members
elif (namespace, membername) in attr_docs: elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'): if want_all and membername.startswith('_'):
@ -564,6 +586,9 @@ class Documenter:
# ignore members whose name starts with _ by default # ignore members whose name starts with _ by default
keep = self.options.private_members and \ keep = self.options.private_members and \
(has_doc or self.options.undoc_members) (has_doc or self.options.undoc_members)
else:
if self.options.members is ALL and is_filtered_inherited_member(membername):
keep = False
else: else:
# ignore undocumented members if :undoc-members: is not given # ignore undocumented members if :undoc-members: is not given
keep = has_doc or self.options.undoc_members keep = has_doc or self.options.undoc_members
@ -740,7 +765,7 @@ class ModuleDocumenter(Documenter):
option_spec = { option_spec = {
'members': members_option, 'undoc-members': bool_option, 'members': members_option, 'undoc-members': bool_option,
'noindex': bool_option, 'inherited-members': bool_option, 'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'synopsis': identity, 'show-inheritance': bool_option, 'synopsis': identity,
'platform': identity, 'deprecated': bool_option, 'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option, 'member-order': identity, 'exclude-members': members_set_option,
@ -1039,7 +1064,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
member_order = 20 member_order = 20
option_spec = { option_spec = {
'members': members_option, 'undoc-members': bool_option, 'members': members_option, 'undoc-members': bool_option,
'noindex': bool_option, 'inherited-members': bool_option, 'noindex': bool_option, 'inherited-members': inherited_members_option,
'show-inheritance': bool_option, 'member-order': identity, 'show-inheritance': bool_option, 'member-order': identity,
'exclude-members': members_set_option, 'exclude-members': members_set_option,
'private-members': bool_option, 'special-members': members_option, 'private-members': bool_option, 'special-members': members_option,

View File

@ -581,6 +581,30 @@ def test_autodoc_inherited_members(app):
] ]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members_Base(app):
options = {"members": None,
"inherited-members": "Base",
"special-members": None}
# check methods for object class are shown
actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options)
assert ' .. py:method:: Derived.inheritedmeth()' in actual
assert ' .. py:method:: Derived.inheritedclassmeth' not in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members_None(app):
options = {"members": None,
"inherited-members": "None",
"special-members": None}
# check methods for object class are shown
actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options)
assert ' .. py:method:: Derived.__init__' in actual
assert ' .. py:method:: Derived.__str__' in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc') @pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_imported_members(app): def test_autodoc_imported_members(app):
options = {"members": None, options = {"members": None,