Merge pull request #5215 from LewisHaley/lewis/allow-setting-audodoc-default-options-in-conf

Allow setting audodoc default options in conf
This commit is contained in:
Takeshi KOMIYA 2018-08-18 15:13:02 +09:00 committed by GitHub
commit 00072ec0e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 214 additions and 12 deletions

View File

@ -341,7 +341,8 @@ There are also new config values that you can set:
This value is a list of autodoc directive flags that should be automatically This value is a list of autodoc directive flags that should be automatically
applied to all autodoc directives. The supported flags are ``'members'``, applied to all autodoc directives. The supported flags are ``'members'``,
``'undoc-members'``, ``'private-members'``, ``'special-members'``, ``'undoc-members'``, ``'private-members'``, ``'special-members'``,
``'inherited-members'``, ``'show-inheritance'`` and ``'ignore-module-all'``. ``'inherited-members'``, ``'show-inheritance'``, ``'ignore-module-all'``
and ``'exclude-members'``.
If you set one of these flags in this config value, you can use a negated If you set one of these flags in this config value, you can use a negated
form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once. form, :samp:`'no-{flag}'`, in an autodoc directive, to disable it once.
@ -353,8 +354,26 @@ There are also new config values that you can set:
the directive will be interpreted as if only ``:members:`` was given. the directive will be interpreted as if only ``:members:`` was given.
You can also set `autodoc_default_flags` to a dictionary, mapping option
names to the values which can used in .rst files. For example::
autodoc_default_flags = {
'members': 'var1, var2',
'member-order': 'bysource',
'special-members': '__init__',
'undoc-members': None,
'exclude-members': '__weakref__'
}
Setting ``None`` is equivalent to giving the option name in the list format
(i.e. it means "yes/true/on").
.. versionadded:: 1.0 .. versionadded:: 1.0
.. versionchanged:: 1.8
Specifying in dictionary format added.
.. confval:: autodoc_docstring_signature .. confval:: autodoc_docstring_signature
Functions imported from C modules cannot be introspected, and therefore the Functions imported from C modules cannot be introspected, and therefore the

View File

@ -15,6 +15,7 @@ import inspect
import re import re
import sys import sys
import warnings import warnings
from typing import Any
from docutils.statemachine import ViewList from docutils.statemachine import ViewList
from six import iteritems, itervalues, text_type, class_types, string_types from six import iteritems, itervalues, text_type, class_types, string_types
@ -41,6 +42,7 @@ if False:
from docutils import nodes # NOQA from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
@ -679,8 +681,13 @@ class Documenter(object):
# remove members given by exclude-members # remove members given by exclude-members
if self.options.exclude_members: if self.options.exclude_members:
members = [(membername, member) for (membername, member) in members members = [
if membername not in self.options.exclude_members] (membername, member) for (membername, member) in members
if (
self.options.exclude_members is ALL or
membername not in self.options.exclude_members
)
]
# document non-skipped members # document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]] memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
@ -1528,6 +1535,34 @@ def autodoc_attrgetter(app, obj, name, *defargs):
return safe_getattr(obj, name, *defargs) return safe_getattr(obj, name, *defargs)
def convert_autodoc_default_flags(app, config):
# type: (Sphinx, Config) -> None
"""This converts the old list-of-flags (strings) to a dict of Nones."""
if isinstance(config.autodoc_default_flags, dict):
# Already new-style
return
elif not isinstance(config.autodoc_default_flags, list):
# Not old-style list
logger.error(
__("autodoc_default_flags is invalid type %r"),
config.autodoc_default_flags.__class__.__name__
)
return
autodoc_default_flags = {} # type: Dict[unicode, Any]
for option in config.autodoc_default_flags:
if isinstance(option, string_types):
autodoc_default_flags[option] = None
else:
logger.warning(
__("Ignoring invalid option in autodoc_default_flags: %r"),
option
)
config.autodoc_default_flags = autodoc_default_flags # type: ignore
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ModuleDocumenter)
@ -1541,7 +1576,7 @@ def setup(app):
app.add_config_value('autoclass_content', 'class', True) app.add_config_value('autoclass_content', 'class', True)
app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_config_value('autodoc_member_order', 'alphabetic', True)
app.add_config_value('autodoc_default_flags', [], True) app.add_config_value('autodoc_default_flags', {}, True, Any)
app.add_config_value('autodoc_docstring_signature', True, True) app.add_config_value('autodoc_docstring_signature', True, True)
app.add_config_value('autodoc_mock_imports', [], True) app.add_config_value('autodoc_mock_imports', [], True)
app.add_config_value('autodoc_warningiserror', True, True) app.add_config_value('autodoc_warningiserror', True, True)
@ -1550,4 +1585,6 @@ def setup(app):
app.add_event('autodoc-process-signature') app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member') app.add_event('autodoc-skip-member')
app.connect('config-inited', convert_autodoc_default_flags)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True} return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@ -31,7 +31,7 @@ logger = logging.getLogger(__name__)
# common option names for autodoc directives # common option names for autodoc directives
AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
'show-inheritance', 'private-members', 'special-members', 'show-inheritance', 'private-members', 'special-members',
'ignore-module-all'] 'ignore-module-all', 'exclude-members']
class DummyOptionSpec(object): class DummyOptionSpec(object):
@ -68,7 +68,7 @@ def process_documenter_options(documenter, config, options):
else: else:
negated = options.pop('no-' + name, True) is None negated = options.pop('no-' + name, True) is None
if name in config.autodoc_default_flags and not negated: if name in config.autodoc_default_flags and not negated:
options[name] = None options[name] = config.autodoc_default_flags[name]
return Options(assemble_option_dict(options.items(), documenter.option_spec)) return Options(assemble_option_dict(options.items(), documenter.option_spec))

View File

@ -234,3 +234,18 @@ class EnumCls(enum.Enum):
val3 = 34 val3 = 34
"""doc for val3""" """doc for val3"""
val4 = 34 val4 = 34
class CustomIter(object):
def __init__(self):
"""Create a new `CustomIter`."""
self.values = range(10)
def __iter__(self):
"""Iterate squares of each value."""
for i in self.values:
yield i ** 2
def snafucate(self):
"""Makes this snafucated."""
print("snafucated")

View File

@ -11,6 +11,7 @@
""" """
import re import re
import platform
import sys import sys
from warnings import catch_warnings from warnings import catch_warnings
@ -19,7 +20,8 @@ from docutils.statemachine import ViewList
from six import PY3 from six import PY3
from sphinx.ext.autodoc import ( from sphinx.ext.autodoc import (
AutoDirective, ModuleLevelDocumenter, cut_lines, between, ALL AutoDirective, ModuleLevelDocumenter, cut_lines, between, ALL,
convert_autodoc_default_flags
) )
from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
from sphinx.testing.util import SphinxTestApp, Struct # NOQA from sphinx.testing.util import SphinxTestApp, Struct # NOQA
@ -33,8 +35,12 @@ if PY3:
else: else:
ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)' ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
IS_PYPY = platform.python_implementation() == 'PyPy'
def do_autodoc(app, objtype, name, options={}):
def do_autodoc(app, objtype, name, options=None):
if options is None:
options = {}
doccls = app.registry.documenters[objtype] doccls = app.registry.documenters[objtype]
docoptions = process_documenter_options(doccls, app.config, options) docoptions = process_documenter_options(doccls, app.config, options)
bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1) bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1)
@ -1412,21 +1418,146 @@ def test_partialmethod(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc') @pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_flags(app): def test_autodoc_default_flags__as_list__converted(app):
orig = [
'members',
'undoc-members',
('skipped', 1, 2),
{'also': 'skipped'},
]
expected = {
'members': None,
'undoc-members': None,
}
app.config.autodoc_default_flags = orig
convert_autodoc_default_flags(app, app.config)
assert app.config.autodoc_default_flags == expected
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_flags__as_dict__no_conversion(app):
orig = {
'members': 'this,that,other',
'undoc-members': None,
}
app.config.autodoc_default_flags = orig
convert_autodoc_default_flags(app, app.config)
assert app.config.autodoc_default_flags == orig
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_flags__with_flags(app):
# no settings # no settings
actual = do_autodoc(app, 'class', 'target.EnumCls') actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' not in actual assert ' .. py:attribute:: EnumCls.val1' not in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual assert ' .. py:attribute:: EnumCls.val4' not in actual
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: target.CustomIter' not in actual
# with :members: # with :members:
app.config.autodoc_default_flags = ['members'] app.config.autodoc_default_flags = {'members': None}
actual = do_autodoc(app, 'class', 'target.EnumCls') actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val1' in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual assert ' .. py:attribute:: EnumCls.val4' not in actual
# with :members: and :undoc-members: # with :members: and :undoc-members:
app.config.autodoc_default_flags = ['members', app.config.autodoc_default_flags = {
'undoc-members'] 'members': None,
'undoc-members': None,
}
actual = do_autodoc(app, 'class', 'target.EnumCls') actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val1' in actual
assert ' .. py:attribute:: EnumCls.val4' in actual assert ' .. py:attribute:: EnumCls.val4' in actual
# with :special-members:
# Note that :members: must be *on* for :special-members: to work.
app.config.autodoc_default_flags = {
'members': None,
'special-members': None
}
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: CustomIter.__init__()' in actual
assert ' Create a new `CustomIter`.' in actual
assert ' .. py:method:: CustomIter.__iter__()' in actual
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' in actual
assert ' list of weak references to the object (if defined)' in actual
# :exclude-members: None - has no effect. Unlike :members:,
# :special-members:, etc. where None == "include all", here None means
# "no/false/off".
app.config.autodoc_default_flags = {
'members': None,
'exclude-members': None,
}
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual
app.config.autodoc_default_flags = {
'members': None,
'special-members': None,
'exclude-members': None,
}
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: CustomIter.__init__()' in actual
assert ' Create a new `CustomIter`.' in actual
assert ' .. py:method:: CustomIter.__iter__()' in actual
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' in actual
assert ' list of weak references to the object (if defined)' in actual
assert ' .. py:method:: CustomIter.snafucate()' in actual
assert ' Makes this snafucated.' in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_flags__with_values(app):
# with :members:
app.config.autodoc_default_flags = {'members': 'val1,val2'}
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' in actual
assert ' .. py:attribute:: EnumCls.val2' in actual
assert ' .. py:attribute:: EnumCls.val3' not in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual
# with :special-members:
# Note that :members: must be *on* for :special-members: to work.
app.config.autodoc_default_flags = {
'members': None,
'special-members': '__init__,__iter__',
}
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: CustomIter.__init__()' in actual
assert ' Create a new `CustomIter`.' in actual
assert ' .. py:method:: CustomIter.__iter__()' in actual
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' not in actual
assert ' list of weak references to the object (if defined)' not in actual
# with :exclude-members:
app.config.autodoc_default_flags = {
'members': None,
'exclude-members': 'val1'
}
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' not in actual
assert ' .. py:attribute:: EnumCls.val2' in actual
assert ' .. py:attribute:: EnumCls.val3' in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual
app.config.autodoc_default_flags = {
'members': None,
'special-members': None,
'exclude-members': '__weakref__,snafucate',
}
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: CustomIter.__init__()' in actual
assert ' Create a new `CustomIter`.' in actual
assert ' .. py:method:: CustomIter.__iter__()' in actual
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' not in actual
assert ' list of weak references to the object (if defined)' not in actual
assert ' .. py:method:: CustomIter.snafucate()' not in actual
assert ' Makes this snafucated.' not in actual