autodoc: allow specifying values to global arguments

Previously, users could specify a *list* of flags in their config files.
The flags were directive names that would otherwise be present in the
.rst files. However, as a list, it was not possible to specify values
to those flags, which *is* possible in .rst files.

For example, in .rst you could say

  :special-members: __init__, __iter__

And this would cause autodoc to generate documents for these methods that
it would otherwise ignore.

This commit changes the config option to instead accept a dictionary.
This is a dictionary whose keys can contain the same flag-names as before,
but whose values can contain the arguments as seen in .rst files.

The old list is still supported, for backwards compatibility, but the data
is transformed into a dictionary when the user's config is loaded.
This commit is contained in:
Lewis Haley 2018-08-08 16:26:11 +01:00
parent 87029392fd
commit 6e1e35c98a
5 changed files with 155 additions and 9 deletions

View File

@ -353,8 +353,25 @@ There are also new config values that you can set:
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,
}
Setting ``None`` is equivalent to giving the option name in the list format
(i.e. it means "yes/true/on").
.. versionadded:: 1.0
.. versionchanged:: 1.8
Specifying in dictionary format added.
.. confval:: autodoc_docstring_signature
Functions imported from C modules cannot be introspected, and therefore the

View File

@ -15,6 +15,7 @@ import inspect
import re
import sys
import warnings
from typing import Any
from docutils.statemachine import ViewList
from six import iteritems, itervalues, text_type, class_types, string_types
@ -41,6 +42,7 @@ if False:
from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
@ -679,8 +681,13 @@ class Documenter(object):
# remove members given by exclude-members
if self.options.exclude_members:
members = [(membername, member) for (membername, member) in members
if membername not in self.options.exclude_members]
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
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
@ -1528,6 +1535,34 @@ def autodoc_attrgetter(app, 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):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)
@ -1541,7 +1576,7 @@ def setup(app):
app.add_config_value('autoclass_content', 'class', 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_mock_imports', [], 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-skip-member')
app.connect('config-inited', convert_autodoc_default_flags)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@ -68,7 +68,7 @@ def process_documenter_options(documenter, config, options):
else:
negated = options.pop('no-' + name, True) is None
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))

View File

@ -234,3 +234,18 @@ class EnumCls(enum.Enum):
val3 = 34
"""doc for val3"""
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 platform
import sys
from warnings import catch_warnings
@ -19,7 +20,8 @@ from docutils.statemachine import ViewList
from six import PY3
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.testing.util import SphinxTestApp, Struct # NOQA
@ -33,6 +35,8 @@ if PY3:
else:
ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
IS_PYPY = platform.python_implementation() == 'PyPy'
def do_autodoc(app, objtype, name, options=None):
if options is None:
@ -1414,21 +1418,94 @@ def test_partialmethod(app):
@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
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' 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:
app.config.autodoc_default_flags = ['members']
app.config.autodoc_default_flags = {'members': None}
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' in actual
assert ' .. py:attribute:: EnumCls.val4' not in actual
# with :members: and :undoc-members:
app.config.autodoc_default_flags = ['members',
'undoc-members']
app.config.autodoc_default_flags = {
'members': None,
'undoc-members': None,
}
actual = do_autodoc(app, 'class', 'target.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' 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
@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