mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
#2021: Allow autosummary to respect __all__
This commit is contained in:
parent
cee86909b9
commit
ad0071ddb7
@ -201,6 +201,25 @@ also use these config values:
|
|||||||
|
|
||||||
.. versionadded:: 2.1
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
.. versionchanged:: 4.3
|
||||||
|
|
||||||
|
If ``autosummary_ignore___all__`` is ``False``, this configuration value
|
||||||
|
is ignored for members listed in ``__all__``.
|
||||||
|
|
||||||
|
.. confval:: autosummary_ignore___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___all__`` to False and ``autosummary_imported_members``
|
||||||
|
to True.
|
||||||
|
|
||||||
|
.. versionadded:: 4.3
|
||||||
|
|
||||||
.. confval:: autosummary_filename_map
|
.. confval:: autosummary_filename_map
|
||||||
|
|
||||||
A dict mapping object names to filenames. This is necessary to avoid
|
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',
|
app.add_config_value('autosummary_mock_imports',
|
||||||
lambda config: config.autodoc_mock_imports, 'env')
|
lambda config: config.autodoc_mock_imports, 'env')
|
||||||
app.add_config_value('autosummary_imported_members', [], False, [bool])
|
app.add_config_value('autosummary_imported_members', [], False, [bool])
|
||||||
|
app.add_config_value('autosummary_ignore___all__', True, 'env', bool)
|
||||||
|
|
||||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||||
|
@ -28,7 +28,7 @@ import sys
|
|||||||
import warnings
|
import warnings
|
||||||
from gettext import NullTranslations
|
from gettext import NullTranslations
|
||||||
from os import path
|
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 import TemplateNotFound
|
||||||
from jinja2.sandbox import SandboxedEnvironment
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
@ -46,7 +46,7 @@ from sphinx.locale import __
|
|||||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
from sphinx.registry import SphinxComponentRegistry
|
from sphinx.registry import SphinxComponentRegistry
|
||||||
from sphinx.util import logging, rst, split_full_qualified_name
|
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.osutil import ensuredir
|
||||||
from sphinx.util.template import SphinxTemplateLoader
|
from sphinx.util.template import SphinxTemplateLoader
|
||||||
|
|
||||||
@ -68,6 +68,7 @@ class DummyApplication:
|
|||||||
|
|
||||||
self.config.add('autosummary_context', {}, True, None)
|
self.config.add('autosummary_context', {}, True, None)
|
||||||
self.config.add('autosummary_filename_map', {}, True, None)
|
self.config.add('autosummary_filename_map', {}, True, None)
|
||||||
|
self.config.add('autosummary_ignore___all__', True, 'env', bool)
|
||||||
self.config.init_values()
|
self.config.init_values()
|
||||||
|
|
||||||
def emit_firstresult(self, *args: Any) -> None:
|
def emit_firstresult(self, *args: Any) -> None:
|
||||||
@ -192,7 +193,7 @@ class ModuleScanner:
|
|||||||
|
|
||||||
def scan(self, imported_members: bool) -> List[str]:
|
def scan(self, imported_members: bool) -> List[str]:
|
||||||
members = []
|
members = []
|
||||||
for name in dir(self.object):
|
for name in members_of(self.app.config, self.object):
|
||||||
try:
|
try:
|
||||||
value = safe_getattr(self.object, name)
|
value = safe_getattr(self.object, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -216,11 +217,25 @@ class ModuleScanner:
|
|||||||
# list all members up
|
# list all members up
|
||||||
members.append(name)
|
members.append(name)
|
||||||
elif imported is False:
|
elif imported is False:
|
||||||
# list not-imported members up
|
# list not-imported members
|
||||||
|
members.append(name)
|
||||||
|
elif '__all__' in dir(self.object) and not self.app.config.autosummary_ignore___all__:
|
||||||
|
# list members that have __all__ set
|
||||||
members.append(name)
|
members.append(name)
|
||||||
|
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
def members_of(conf: Config, obj: Any) -> Sequence[str]:
|
||||||
|
"""Get the members of ``obj``, possibly ignoring the ``__all__`` module attribute
|
||||||
|
|
||||||
|
Follows the ``conf.autosummary_ignore___all__`` setting."""
|
||||||
|
|
||||||
|
if conf.autosummary_ignore___all__:
|
||||||
|
return dir(obj)
|
||||||
|
else:
|
||||||
|
return getall(obj) or dir(obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||||
template: AutosummaryRenderer, template_name: str,
|
template: AutosummaryRenderer, template_name: str,
|
||||||
@ -245,7 +260,7 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
|
|
||||||
def get_module_members(obj: Any) -> Dict[str, Any]:
|
def get_module_members(obj: Any) -> Dict[str, Any]:
|
||||||
members = {}
|
members = {}
|
||||||
for name in dir(obj):
|
for name in members_of(app.config, obj):
|
||||||
try:
|
try:
|
||||||
members[name] = safe_getattr(obj, name)
|
members[name] = safe_getattr(obj, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -630,6 +645,10 @@ The format of the autosummary directive is documented in the
|
|||||||
dest='imported_members', default=False,
|
dest='imported_members', default=False,
|
||||||
help=__('document imported members (default: '
|
help=__('document imported members (default: '
|
||||||
'%(default)s)'))
|
'%(default)s)'))
|
||||||
|
parser.add_argument('-a', '--respect-__all__', action='store_false',
|
||||||
|
dest='ignore___all__', default=True,
|
||||||
|
help=__('document exactly the members in module __all__ attribute. '
|
||||||
|
'(default: %(default)s)'))
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
@ -646,6 +665,7 @@ def main(argv: List[str] = sys.argv[1:]) -> None:
|
|||||||
|
|
||||||
if args.templates:
|
if args.templates:
|
||||||
app.config.templates_path.append(path.abspath(args.templates))
|
app.config.templates_path.append(path.abspath(args.templates))
|
||||||
|
app.config.autosummary_ignore___all__ = args.ignore___all__
|
||||||
|
|
||||||
generate_autosummary_docs(args.source_file, args.output_dir,
|
generate_autosummary_docs(args.source_file, args.output_dir,
|
||||||
'.' + args.suffix,
|
'.' + args.suffix,
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
from os import path # NOQA
|
from os import path # NOQA
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"CONSTANT1",
|
||||||
|
"Exc",
|
||||||
|
"Foo",
|
||||||
|
"_Baz",
|
||||||
|
"bar",
|
||||||
|
"qux",
|
||||||
|
"path",
|
||||||
|
]
|
||||||
|
|
||||||
#: module variable
|
#: module variable
|
||||||
CONSTANT1 = None
|
CONSTANT1 = None
|
||||||
CONSTANT2 = None
|
CONSTANT2 = None
|
||||||
@ -48,3 +58,5 @@ class _Exc(Exception):
|
|||||||
|
|
||||||
#: a module-level attribute
|
#: a module-level attribute
|
||||||
qux = 2
|
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]
|
context = template.render.call_args[0][1]
|
||||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
|
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
|
||||||
'__builtins__', '__cached__', '__doc__', '__file__',
|
'__all__', '__builtins__', '__cached__', '__doc__',
|
||||||
'__name__', '__package__', '_quux', 'bar', 'qux']
|
'__file__', '__name__', '__package__', '_quux', 'bar',
|
||||||
|
'quuz', 'qux']
|
||||||
assert context['functions'] == ['bar']
|
assert context['functions'] == ['bar']
|
||||||
assert context['all_functions'] == ['_quux', 'bar']
|
assert context['all_functions'] == ['_quux', 'bar']
|
||||||
assert context['classes'] == ['Foo']
|
assert context['classes'] == ['Foo']
|
||||||
assert context['all_classes'] == ['Foo', '_Baz']
|
assert context['all_classes'] == ['Foo', '_Baz']
|
||||||
assert context['exceptions'] == ['Exc']
|
assert context['exceptions'] == ['Exc']
|
||||||
assert context['all_exceptions'] == ['Exc', '_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___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['attributes'] == ['CONSTANT1', 'qux']
|
||||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
||||||
assert context['fullname'] == 'autosummary_dummy_module'
|
assert context['fullname'] == 'autosummary_dummy_module'
|
||||||
@ -232,7 +260,6 @@ def test_autosummary_generate_content_for_module(app):
|
|||||||
assert context['name'] == ''
|
assert context['name'] == ''
|
||||||
assert context['objtype'] == 'module'
|
assert context['objtype'] == 'module'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx(testroot='ext-autosummary')
|
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||||
def test_autosummary_generate_content_for_module_skipped(app):
|
def test_autosummary_generate_content_for_module_skipped(app):
|
||||||
import autosummary_dummy_module
|
import autosummary_dummy_module
|
||||||
@ -246,9 +273,9 @@ def test_autosummary_generate_content_for_module_skipped(app):
|
|||||||
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||||
template, None, False, app, False, {})
|
template, None, False, app, False, {})
|
||||||
context = template.render.call_args[0][1]
|
context = template.render.call_args[0][1]
|
||||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
|
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__all__',
|
||||||
'__cached__', '__doc__', '__file__', '__name__',
|
'__builtins__', '__cached__', '__doc__', '__file__',
|
||||||
'__package__', '_quux', 'qux']
|
'__name__', '__package__', '_quux', 'quuz', 'qux']
|
||||||
assert context['functions'] == []
|
assert context['functions'] == []
|
||||||
assert context['classes'] == []
|
assert context['classes'] == []
|
||||||
assert context['exceptions'] == []
|
assert context['exceptions'] == []
|
||||||
@ -265,17 +292,17 @@ def test_autosummary_generate_content_for_module_imported_members(app):
|
|||||||
|
|
||||||
context = template.render.call_args[0][1]
|
context = template.render.call_args[0][1]
|
||||||
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
|
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
|
||||||
'_Exc', '__builtins__', '__cached__', '__doc__',
|
'_Exc', '__all__', '__builtins__', '__cached__', '__doc__',
|
||||||
'__file__', '__loader__', '__name__', '__package__',
|
'__file__', '__loader__', '__name__', '__package__',
|
||||||
'__spec__', '_quux', 'bar', 'path', 'qux']
|
'__spec__', '_quux', 'bar', 'path', 'quuz', 'qux']
|
||||||
assert context['functions'] == ['bar']
|
assert context['functions'] == ['bar']
|
||||||
assert context['all_functions'] == ['_quux', 'bar']
|
assert context['all_functions'] == ['_quux', 'bar']
|
||||||
assert context['classes'] == ['Foo']
|
assert context['classes'] == ['Foo']
|
||||||
assert context['all_classes'] == ['Foo', '_Baz']
|
assert context['all_classes'] == ['Foo', '_Baz']
|
||||||
assert context['exceptions'] == ['Exc']
|
assert context['exceptions'] == ['Exc']
|
||||||
assert context['all_exceptions'] == ['Exc', '_Exc']
|
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||||
assert context['attributes'] == ['CONSTANT1', 'qux']
|
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||||
assert context['all_attributes'] == ['CONSTANT1', 'qux']
|
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
|
||||||
assert context['fullname'] == 'autosummary_dummy_module'
|
assert context['fullname'] == 'autosummary_dummy_module'
|
||||||
assert context['module'] == 'autosummary_dummy_module'
|
assert context['module'] == 'autosummary_dummy_module'
|
||||||
assert context['objname'] == ''
|
assert context['objname'] == ''
|
||||||
@ -313,6 +340,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'
|
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()
|
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
|
||||||
|
print(module)
|
||||||
assert (' .. autosummary::\n'
|
assert (' .. autosummary::\n'
|
||||||
' \n'
|
' \n'
|
||||||
' Foo\n'
|
' Foo\n'
|
||||||
@ -321,6 +349,7 @@ def test_autosummary_generate(app, status, warning):
|
|||||||
' \n'
|
' \n'
|
||||||
' CONSTANT1\n'
|
' CONSTANT1\n'
|
||||||
' qux\n'
|
' qux\n'
|
||||||
|
' quuz\n'
|
||||||
' \n' in module)
|
' \n' in module)
|
||||||
|
|
||||||
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()
|
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()
|
||||||
|
Loading…
Reference in New Issue
Block a user