mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #9831 from Yoshanuikabundi/autosummary___all__
Allow autosummary to respect __all__
This commit is contained in:
commit
15d834e4d8
4
CHANGES
4
CHANGES
@ -15,6 +15,10 @@ Features added
|
||||
|
||||
* #9815: html theme: Wrap sidebar components in div to allow customizing their
|
||||
layout via CSS
|
||||
* #9831: Autosummary now documents only the members specified in a module's
|
||||
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
|
||||
``False``. The default behaviour is unchanged. Autogen also now supports
|
||||
this behavior with the ``--respect-module-all`` switch.
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
@ -39,6 +39,10 @@ Options
|
||||
|
||||
Document imported members.
|
||||
|
||||
.. option:: -a, --respect-module-all
|
||||
|
||||
Document exactly the members in a module's ``__all__`` attribute.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
|
@ -201,6 +201,25 @@ also use these config values:
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. versionchanged:: 4.4
|
||||
|
||||
If ``autosummary_ignore_module_all`` is ``False``, this configuration
|
||||
value is ignored for members listed in ``__all__``.
|
||||
|
||||
.. confval:: autosummary_ignore_module_all
|
||||
|
||||
If ``False`` and a module has the ``__all__`` attribute set, autosummary
|
||||
documents every member listed in ``__all__`` and no others. Default is
|
||||
``True``
|
||||
|
||||
Note that if an imported member is listed in ``__all__``, it will be
|
||||
documented regardless of the value of ``autosummary_imported_members``. To
|
||||
match the behaviour of ``from module import *``, set
|
||||
``autosummary_ignore_module_all`` to False and
|
||||
``autosummary_imported_members`` to True.
|
||||
|
||||
.. versionadded:: 4.4
|
||||
|
||||
.. confval:: autosummary_filename_map
|
||||
|
||||
A dict mapping object names to filenames. This is necessary to avoid
|
||||
|
@ -826,5 +826,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_config_value('autosummary_mock_imports',
|
||||
lambda config: config.autodoc_mock_imports, 'env')
|
||||
app.add_config_value('autosummary_imported_members', [], False, [bool])
|
||||
app.add_config_value('autosummary_ignore_module_all', True, 'env', bool)
|
||||
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
@ -28,7 +28,7 @@ import sys
|
||||
import warnings
|
||||
from gettext import NullTranslations
|
||||
from os import path
|
||||
from typing import Any, Dict, List, NamedTuple, Set, Tuple, Type, Union
|
||||
from typing import Any, Dict, List, NamedTuple, Sequence, Set, Tuple, Type, Union
|
||||
|
||||
from jinja2 import TemplateNotFound
|
||||
from jinja2.sandbox import SandboxedEnvironment
|
||||
@ -46,7 +46,7 @@ from sphinx.locale import __
|
||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||
from sphinx.registry import SphinxComponentRegistry
|
||||
from sphinx.util import logging, rst, split_full_qualified_name
|
||||
from sphinx.util.inspect import safe_getattr
|
||||
from sphinx.util.inspect import getall, safe_getattr
|
||||
from sphinx.util.osutil import ensuredir
|
||||
from sphinx.util.template import SphinxTemplateLoader
|
||||
|
||||
@ -68,6 +68,7 @@ class DummyApplication:
|
||||
|
||||
self.config.add('autosummary_context', {}, True, None)
|
||||
self.config.add('autosummary_filename_map', {}, True, None)
|
||||
self.config.add('autosummary_ignore_module_all', True, 'env', bool)
|
||||
self.config.init_values()
|
||||
|
||||
def emit_firstresult(self, *args: Any) -> None:
|
||||
@ -192,7 +193,7 @@ class ModuleScanner:
|
||||
|
||||
def scan(self, imported_members: bool) -> List[str]:
|
||||
members = []
|
||||
for name in dir(self.object):
|
||||
for name in members_of(self.object, self.app.config):
|
||||
try:
|
||||
value = safe_getattr(self.object, name)
|
||||
except AttributeError:
|
||||
@ -212,16 +213,31 @@ class ModuleScanner:
|
||||
except AttributeError:
|
||||
imported = False
|
||||
|
||||
respect_module_all = not self.app.config.autosummary_ignore_module_all
|
||||
if imported_members:
|
||||
# list all members up
|
||||
members.append(name)
|
||||
elif imported is False:
|
||||
# list not-imported members up
|
||||
# list not-imported members
|
||||
members.append(name)
|
||||
elif '__all__' in dir(self.object) and respect_module_all:
|
||||
# list members that have __all__ set
|
||||
members.append(name)
|
||||
|
||||
return members
|
||||
|
||||
|
||||
def members_of(obj: Any, conf: Config) -> Sequence[str]:
|
||||
"""Get the members of ``obj``, possibly ignoring the ``__all__`` module attribute
|
||||
|
||||
Follows the ``conf.autosummary_ignore_module_all`` setting."""
|
||||
|
||||
if conf.autosummary_ignore_module_all:
|
||||
return dir(obj)
|
||||
else:
|
||||
return getall(obj) or dir(obj)
|
||||
|
||||
|
||||
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||
template: AutosummaryRenderer, template_name: str,
|
||||
imported_members: bool, app: Any,
|
||||
@ -245,7 +261,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||
|
||||
def get_module_members(obj: Any) -> Dict[str, Any]:
|
||||
members = {}
|
||||
for name in dir(obj):
|
||||
for name in members_of(obj, app.config):
|
||||
try:
|
||||
members[name] = safe_getattr(obj, name)
|
||||
except AttributeError:
|
||||
@ -630,6 +646,10 @@ The format of the autosummary directive is documented in the
|
||||
dest='imported_members', default=False,
|
||||
help=__('document imported members (default: '
|
||||
'%(default)s)'))
|
||||
parser.add_argument('-a', '--respect-module-all', action='store_true',
|
||||
dest='respect_module_all', default=False,
|
||||
help=__('document exactly the members in module __all__ attribute. '
|
||||
'(default: %(default)s)'))
|
||||
|
||||
return parser
|
||||
|
||||
@ -646,6 +666,7 @@ def main(argv: List[str] = sys.argv[1:]) -> None:
|
||||
|
||||
if args.templates:
|
||||
app.config.templates_path.append(path.abspath(args.templates))
|
||||
app.config.autosummary_ignore_module_all = not args.respect_module_all # type: ignore
|
||||
|
||||
generate_autosummary_docs(args.source_file, args.output_dir,
|
||||
'.' + args.suffix,
|
||||
|
@ -1,6 +1,16 @@
|
||||
from os import path # NOQA
|
||||
from typing import Union
|
||||
|
||||
__all__ = [
|
||||
"CONSTANT1",
|
||||
"Exc",
|
||||
"Foo",
|
||||
"_Baz",
|
||||
"bar",
|
||||
"qux",
|
||||
"path",
|
||||
]
|
||||
|
||||
#: module variable
|
||||
CONSTANT1 = None
|
||||
CONSTANT2 = None
|
||||
@ -48,3 +58,5 @@ class _Exc(Exception):
|
||||
|
||||
#: a module-level attribute
|
||||
qux = 2
|
||||
#: a module-level attribute that has been excluded from __all__
|
||||
quuz = 2
|
||||
|
@ -216,14 +216,42 @@ def test_autosummary_generate_content_for_module(app):
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
|
||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||
'__name__', '__package__', '_quux', 'bar', 'qux']
|
||||
'__all__', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__name__', '__package__', '_quux', 'bar',
|
||||
'quuz', 'qux']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['_quux', 'bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
assert context['name'] == ''
|
||||
assert context['objtype'] == 'module'
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||
def test_autosummary_generate_content_for_module___all__(app):
|
||||
import autosummary_dummy_module
|
||||
template = Mock()
|
||||
app.config.autosummary_ignore_module_all = False
|
||||
|
||||
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||
template, None, False, app, False, {})
|
||||
assert template.render.call_args[0][0] == 'module'
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'Exc', 'Foo', '_Baz', 'bar', 'qux', 'path']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
@ -246,9 +274,9 @@ def test_autosummary_generate_content_for_module_skipped(app):
|
||||
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||
template, None, False, app, False, {})
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
|
||||
'__cached__', '__doc__', '__file__', '__name__',
|
||||
'__package__', '_quux', 'qux']
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__all__',
|
||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||
'__name__', '__package__', '_quux', 'quuz', 'qux']
|
||||
assert context['functions'] == []
|
||||
assert context['classes'] == []
|
||||
assert context['exceptions'] == []
|
||||
@ -265,17 +293,17 @@ def test_autosummary_generate_content_for_module_imported_members(app):
|
||||
|
||||
context = template.render.call_args[0][1]
|
||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
|
||||
'_Exc', '__builtins__', '__cached__', '__doc__',
|
||||
'_Exc', '__all__', '__builtins__', '__cached__', '__doc__',
|
||||
'__file__', '__loader__', '__name__', '__package__',
|
||||
'__spec__', '_quux', 'bar', 'path', 'qux']
|
||||
'__spec__', '_quux', 'bar', 'path', 'quuz', 'qux']
|
||||
assert context['functions'] == ['bar']
|
||||
assert context['all_functions'] == ['_quux', 'bar']
|
||||
assert context['classes'] == ['Foo']
|
||||
assert context['all_classes'] == ['Foo', '_Baz']
|
||||
assert context['exceptions'] == ['Exc']
|
||||
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||
assert context['fullname'] == 'autosummary_dummy_module'
|
||||
assert context['module'] == 'autosummary_dummy_module'
|
||||
assert context['objname'] == ''
|
||||
@ -313,6 +341,7 @@ def test_autosummary_generate(app, status, warning):
|
||||
assert doctree[3][0][0][2][5].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
|
||||
|
||||
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
|
||||
|
||||
assert (' .. autosummary::\n'
|
||||
' \n'
|
||||
' Foo\n'
|
||||
@ -321,6 +350,7 @@ def test_autosummary_generate(app, status, warning):
|
||||
' \n'
|
||||
' CONSTANT1\n'
|
||||
' qux\n'
|
||||
' quuz\n'
|
||||
' \n' in module)
|
||||
|
||||
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()
|
||||
|
Loading…
Reference in New Issue
Block a user