Skip to content

Commit 15d834e

Browse files
authored
Merge pull request #9831 from Yoshanuikabundi/autosummary___all__
Allow autosummary to respect __all__
2 parents 259de30 + 73b7cd5 commit 15d834e

File tree

7 files changed

+105
-14
lines changed

7 files changed

+105
-14
lines changed

CHANGES

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Features added
1515

1616
* #9815: html theme: Wrap sidebar components in div to allow customizing their
1717
layout via CSS
18+
* #9831: Autosummary now documents only the members specified in a module's
19+
``__all__`` attribute if :confval:`autosummary_ignore_module_all` is set to
20+
``False``. The default behaviour is unchanged. Autogen also now supports
21+
this behavior with the ``--respect-module-all`` switch.
1822

1923
Bugs fixed
2024
----------

doc/man/sphinx-autogen.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ Options
3939

4040
Document imported members.
4141

42+
.. option:: -a, --respect-module-all
43+
44+
Document exactly the members in a module's ``__all__`` attribute.
45+
4246
Example
4347
-------
4448

doc/usage/extensions/autosummary.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,25 @@ also use these config values:
201201

202202
.. versionadded:: 2.1
203203

204+
.. versionchanged:: 4.4
205+
206+
If ``autosummary_ignore_module_all`` is ``False``, this configuration
207+
value is ignored for members listed in ``__all__``.
208+
209+
.. confval:: autosummary_ignore_module_all
210+
211+
If ``False`` and a module has the ``__all__`` attribute set, autosummary
212+
documents every member listed in ``__all__`` and no others. Default is
213+
``True``
214+
215+
Note that if an imported member is listed in ``__all__``, it will be
216+
documented regardless of the value of ``autosummary_imported_members``. To
217+
match the behaviour of ``from module import *``, set
218+
``autosummary_ignore_module_all`` to False and
219+
``autosummary_imported_members`` to True.
220+
221+
.. versionadded:: 4.4
222+
204223
.. confval:: autosummary_filename_map
205224

206225
A dict mapping object names to filenames. This is necessary to avoid

sphinx/ext/autosummary/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,5 +826,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
826826
app.add_config_value('autosummary_mock_imports',
827827
lambda config: config.autodoc_mock_imports, 'env')
828828
app.add_config_value('autosummary_imported_members', [], False, [bool])
829+
app.add_config_value('autosummary_ignore_module_all', True, 'env', bool)
829830

830831
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

sphinx/ext/autosummary/generate.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import warnings
2929
from gettext import NullTranslations
3030
from os import path
31-
from typing import Any, Dict, List, NamedTuple, Set, Tuple, Type, Union
31+
from typing import Any, Dict, List, NamedTuple, Sequence, Set, Tuple, Type, Union
3232

3333
from jinja2 import TemplateNotFound
3434
from jinja2.sandbox import SandboxedEnvironment
@@ -46,7 +46,7 @@
4646
from sphinx.pycode import ModuleAnalyzer, PycodeError
4747
from sphinx.registry import SphinxComponentRegistry
4848
from sphinx.util import logging, rst, split_full_qualified_name
49-
from sphinx.util.inspect import safe_getattr
49+
from sphinx.util.inspect import getall, safe_getattr
5050
from sphinx.util.osutil import ensuredir
5151
from sphinx.util.template import SphinxTemplateLoader
5252

@@ -68,6 +68,7 @@ def __init__(self, translator: NullTranslations) -> None:
6868

6969
self.config.add('autosummary_context', {}, True, None)
7070
self.config.add('autosummary_filename_map', {}, True, None)
71+
self.config.add('autosummary_ignore_module_all', True, 'env', bool)
7172
self.config.init_values()
7273

7374
def emit_firstresult(self, *args: Any) -> None:
@@ -192,7 +193,7 @@ def is_skipped(self, name: str, value: Any, objtype: str) -> bool:
192193

193194
def scan(self, imported_members: bool) -> List[str]:
194195
members = []
195-
for name in dir(self.object):
196+
for name in members_of(self.object, self.app.config):
196197
try:
197198
value = safe_getattr(self.object, name)
198199
except AttributeError:
@@ -212,16 +213,31 @@ def scan(self, imported_members: bool) -> List[str]:
212213
except AttributeError:
213214
imported = False
214215

216+
respect_module_all = not self.app.config.autosummary_ignore_module_all
215217
if imported_members:
216218
# list all members up
217219
members.append(name)
218220
elif imported is False:
219-
# list not-imported members up
221+
# list not-imported members
222+
members.append(name)
223+
elif '__all__' in dir(self.object) and respect_module_all:
224+
# list members that have __all__ set
220225
members.append(name)
221226

222227
return members
223228

224229

230+
def members_of(obj: Any, conf: Config) -> Sequence[str]:
231+
"""Get the members of ``obj``, possibly ignoring the ``__all__`` module attribute
232+
233+
Follows the ``conf.autosummary_ignore_module_all`` setting."""
234+
235+
if conf.autosummary_ignore_module_all:
236+
return dir(obj)
237+
else:
238+
return getall(obj) or dir(obj)
239+
240+
225241
def generate_autosummary_content(name: str, obj: Any, parent: Any,
226242
template: AutosummaryRenderer, template_name: str,
227243
imported_members: bool, app: Any,
@@ -245,7 +261,7 @@ def get_class_members(obj: Any) -> Dict[str, Any]:
245261

246262
def get_module_members(obj: Any) -> Dict[str, Any]:
247263
members = {}
248-
for name in dir(obj):
264+
for name in members_of(obj, app.config):
249265
try:
250266
members[name] = safe_getattr(obj, name)
251267
except AttributeError:
@@ -630,6 +646,10 @@ def get_parser() -> argparse.ArgumentParser:
630646
dest='imported_members', default=False,
631647
help=__('document imported members (default: '
632648
'%(default)s)'))
649+
parser.add_argument('-a', '--respect-module-all', action='store_true',
650+
dest='respect_module_all', default=False,
651+
help=__('document exactly the members in module __all__ attribute. '
652+
'(default: %(default)s)'))
633653

634654
return parser
635655

@@ -646,6 +666,7 @@ def main(argv: List[str] = sys.argv[1:]) -> None:
646666

647667
if args.templates:
648668
app.config.templates_path.append(path.abspath(args.templates))
669+
app.config.autosummary_ignore_module_all = not args.respect_module_all # type: ignore
649670

650671
generate_autosummary_docs(args.source_file, args.output_dir,
651672
'.' + args.suffix,

tests/roots/test-ext-autosummary/autosummary_dummy_module.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
from os import path # NOQA
22
from typing import Union
33

4+
__all__ = [
5+
"CONSTANT1",
6+
"Exc",
7+
"Foo",
8+
"_Baz",
9+
"bar",
10+
"qux",
11+
"path",
12+
]
13+
414
#: module variable
515
CONSTANT1 = None
616
CONSTANT2 = None
@@ -48,3 +58,5 @@ class _Exc(Exception):
4858

4959
#: a module-level attribute
5060
qux = 2
61+
#: a module-level attribute that has been excluded from __all__
62+
quuz = 2

tests/test_ext_autosummary.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,42 @@ def test_autosummary_generate_content_for_module(app):
216216

217217
context = template.render.call_args[0][1]
218218
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', '_Baz', '_Exc',
219-
'__builtins__', '__cached__', '__doc__', '__file__',
220-
'__name__', '__package__', '_quux', 'bar', 'qux']
219+
'__all__', '__builtins__', '__cached__', '__doc__',
220+
'__file__', '__name__', '__package__', '_quux', 'bar',
221+
'quuz', 'qux']
221222
assert context['functions'] == ['bar']
222223
assert context['all_functions'] == ['_quux', 'bar']
223224
assert context['classes'] == ['Foo']
224225
assert context['all_classes'] == ['Foo', '_Baz']
225226
assert context['exceptions'] == ['Exc']
226227
assert context['all_exceptions'] == ['Exc', '_Exc']
228+
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
229+
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
230+
assert context['fullname'] == 'autosummary_dummy_module'
231+
assert context['module'] == 'autosummary_dummy_module'
232+
assert context['objname'] == ''
233+
assert context['name'] == ''
234+
assert context['objtype'] == 'module'
235+
236+
237+
@pytest.mark.sphinx(testroot='ext-autosummary')
238+
def test_autosummary_generate_content_for_module___all__(app):
239+
import autosummary_dummy_module
240+
template = Mock()
241+
app.config.autosummary_ignore_module_all = False
242+
243+
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
244+
template, None, False, app, False, {})
245+
assert template.render.call_args[0][0] == 'module'
246+
247+
context = template.render.call_args[0][1]
248+
assert context['members'] == ['CONSTANT1', 'Exc', 'Foo', '_Baz', 'bar', 'qux', 'path']
249+
assert context['functions'] == ['bar']
250+
assert context['all_functions'] == ['bar']
251+
assert context['classes'] == ['Foo']
252+
assert context['all_classes'] == ['Foo', '_Baz']
253+
assert context['exceptions'] == ['Exc']
254+
assert context['all_exceptions'] == ['Exc']
227255
assert context['attributes'] == ['CONSTANT1', 'qux']
228256
assert context['all_attributes'] == ['CONSTANT1', 'qux']
229257
assert context['fullname'] == 'autosummary_dummy_module'
@@ -246,9 +274,9 @@ def skip_member(app, what, name, obj, skip, options):
246274
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
247275
template, None, False, app, False, {})
248276
context = template.render.call_args[0][1]
249-
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__builtins__',
250-
'__cached__', '__doc__', '__file__', '__name__',
251-
'__package__', '_quux', 'qux']
277+
assert context['members'] == ['CONSTANT1', 'CONSTANT2', '_Baz', '_Exc', '__all__',
278+
'__builtins__', '__cached__', '__doc__', '__file__',
279+
'__name__', '__package__', '_quux', 'quuz', 'qux']
252280
assert context['functions'] == []
253281
assert context['classes'] == []
254282
assert context['exceptions'] == []
@@ -265,17 +293,17 @@ def test_autosummary_generate_content_for_module_imported_members(app):
265293

266294
context = template.render.call_args[0][1]
267295
assert context['members'] == ['CONSTANT1', 'CONSTANT2', 'Exc', 'Foo', 'Union', '_Baz',
268-
'_Exc', '__builtins__', '__cached__', '__doc__',
296+
'_Exc', '__all__', '__builtins__', '__cached__', '__doc__',
269297
'__file__', '__loader__', '__name__', '__package__',
270-
'__spec__', '_quux', 'bar', 'path', 'qux']
298+
'__spec__', '_quux', 'bar', 'path', 'quuz', 'qux']
271299
assert context['functions'] == ['bar']
272300
assert context['all_functions'] == ['_quux', 'bar']
273301
assert context['classes'] == ['Foo']
274302
assert context['all_classes'] == ['Foo', '_Baz']
275303
assert context['exceptions'] == ['Exc']
276304
assert context['all_exceptions'] == ['Exc', '_Exc']
277-
assert context['attributes'] == ['CONSTANT1', 'qux']
278-
assert context['all_attributes'] == ['CONSTANT1', 'qux']
305+
assert context['attributes'] == ['CONSTANT1', 'qux', 'quuz']
306+
assert context['all_attributes'] == ['CONSTANT1', 'qux', 'quuz']
279307
assert context['fullname'] == 'autosummary_dummy_module'
280308
assert context['module'] == 'autosummary_dummy_module'
281309
assert context['objname'] == ''
@@ -313,6 +341,7 @@ def test_autosummary_generate(app, status, warning):
313341
assert doctree[3][0][0][2][5].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
314342

315343
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
344+
316345
assert (' .. autosummary::\n'
317346
' \n'
318347
' Foo\n'
@@ -321,6 +350,7 @@ def test_autosummary_generate(app, status, warning):
321350
' \n'
322351
' CONSTANT1\n'
323352
' qux\n'
353+
' quuz\n'
324354
' \n' in module)
325355

326356
Foo = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.rst').read_text()

0 commit comments

Comments
 (0)