Merge pull request #6873 from tk0miya/5923_autodoc_inherited_members_can_ignore_specific_superclass

Close #5923: autodoc: allow not to document inherited members of specific super class
This commit is contained in:
Takeshi KOMIYA 2019-12-22 16:30:56 +09:00 committed by GitHub
commit e83bb29789
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 12 deletions

View File

@ -14,6 +14,8 @@ Incompatible changes
* Drop features and APIs deprecated in 1.8.x
* #247: autosummary: stub files are overwritten automatically by default. see
: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
----------
@ -23,6 +25,8 @@ Features added
* #247: autosummary: Add :confval:`autosummary_generate_overwrite` to overwrite
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
----------

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
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
:members:
@ -166,11 +166,29 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
This can be combined with ``undoc-members`` to document *all* available
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.
For example; If your class ``Foo`` is derived from ``list`` class and
you don't want to document ``list.__len__()``, you should specify a
option ``:inherited-members: list`` to avoid special members of list
class.
Another example; If your class Foo has ``__str__`` special method and
autodoc directive has both ``inherited-members`` and ``special-members``,
``__str__`` will be documented as in the past, but other special method
that are not implemented in your class ``Foo``.
Note: this will lead to markup errors if the inherited members come from a
module whose docstrings are not reST formatted.
.. 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
objects (functions, methods, classes) with the regular syntax that will
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(',')}
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()
@ -515,6 +523,17 @@ class Documenter:
The user can override the skipping decision by connecting to the
``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 = []
# search for members in source code too
@ -545,13 +564,16 @@ class Documenter:
if want_all and membername.startswith('__') and \
membername.endswith('__') and len(membername) > 4:
# special __methods__
if self.options.special_members is ALL and \
membername != '__doc__':
keep = has_doc or self.options.undoc_members
elif self.options.special_members and \
self.options.special_members is not ALL and \
membername in self.options.special_members:
keep = has_doc or self.options.undoc_members
if self.options.special_members is ALL:
if membername == '__doc__':
keep = False
elif is_filtered_inherited_member(membername):
keep = False
else:
keep = has_doc or self.options.undoc_members
elif self.options.special_members:
if membername in self.options.special_members:
keep = has_doc or self.options.undoc_members
elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default
@ -565,8 +587,11 @@ class Documenter:
keep = self.options.private_members and \
(has_doc or self.options.undoc_members)
else:
# ignore undocumented members if :undoc-members: is not given
keep = has_doc or self.options.undoc_members
if self.options.members is ALL and is_filtered_inherited_member(membername):
keep = False
else:
# ignore undocumented members if :undoc-members: is not given
keep = has_doc or self.options.undoc_members
# give the user a chance to decide whether this member
# should be skipped
@ -740,7 +765,7 @@ class ModuleDocumenter(Documenter):
option_spec = {
'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,
'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option,
@ -1039,7 +1064,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
member_order = 20
option_spec = {
'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,
'exclude-members': members_set_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')
def test_autodoc_imported_members(app):
options = {"members": None,