From 476b73b6ca6dd3e64a557495e3f8137e702f77dd Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 23 Feb 2020 01:51:43 +0900 Subject: [PATCH] autosummary: Add :recursive: option to autosummary directive --- doc/usage/extensions/autosummary.rst | 14 +++++++++++++ sphinx/ext/autosummary/__init__.py | 1 + sphinx/ext/autosummary/generate.py | 20 ++++++++++++++++--- .../templates/autosummary/module.rst | 1 + .../test-ext-autosummary-recursive/index.rst | 6 ++++++ .../package2/__init__.py | 0 .../package2/module.py | 13 ++++++++++++ tests/test_ext_autosummary.py | 7 ++++++- 8 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 tests/roots/test-ext-autosummary-recursive/package2/__init__.py create mode 100644 tests/roots/test-ext-autosummary-recursive/package2/module.py diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index 59620b4a8..3f4a52322 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -104,6 +104,20 @@ The :mod:`sphinx.ext.autosummary` extension does this in three parts: .. versionadded:: 1.0 + * You can specify the ``recursive`` option to generate documents for + modules and sub-packages recursively. It defaults to disabled. + For example, :: + + .. autosummary:: + :recursive: + + sphinx.environment.BuildEnvironment + + It is needed to enable :confval:`autosummary_recursive` also to + use this option. + + .. versionadded:: 3.1 + :program:`sphinx-autogen` -- generate autodoc stub pages -------------------------------------------------------- diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index d32cfa5af..72eb5afcf 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -224,6 +224,7 @@ class Autosummary(SphinxDirective): option_spec = { 'toctree': directives.unchanged, 'nosignatures': directives.flag, + 'recursive': directives.flag, 'template': directives.unchanged, } diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 74bd90bbe..2b4a19971 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -60,7 +60,8 @@ class DummyApplication: AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str), ('path', str), - ('template', str)]) + ('template', str), + ('recursive', bool)]) def setup_documenters(app: Any) -> None: @@ -266,8 +267,13 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, _warn('[autosummary] failed to import %r: %s' % (name, e)) continue + if entry.recursive and not recursive: + _warn('[autosummary] :resursive: option found. But ignored. ' + 'Please read document for autosummary_recursive option') + content = generate_autosummary_content(name, obj, parent, template, entry.template, - imported_members, app, recursive) + imported_members, app, + recursive and entry.recursive) filename = os.path.join(path, name + suffix) if os.path.isfile(filename): @@ -347,11 +353,13 @@ def find_autosummary_in_lines(lines: List[str], module: Any = None, filename: st module_re = re.compile( r'^\s*\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$') autosummary_item_re = re.compile(r'^\s+(~?[_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?') + recursive_arg_re = re.compile(r'^\s+:recursive:\s*$') toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$') template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$') documented = [] # type: List[AutosummaryEntry] + recursive = False toctree = None # type: str template = None current_module = module @@ -360,6 +368,11 @@ def find_autosummary_in_lines(lines: List[str], module: Any = None, filename: st for line in lines: if in_autosummary: + m = recursive_arg_re.match(line) + if m: + recursive = True + continue + m = toctree_arg_re.match(line) if m: toctree = m.group(1) @@ -384,7 +397,7 @@ def find_autosummary_in_lines(lines: List[str], module: Any = None, filename: st if current_module and \ not name.startswith(current_module + '.'): name = "%s.%s" % (current_module, name) - documented.append(AutosummaryEntry(name, toctree, template)) + documented.append(AutosummaryEntry(name, toctree, template, recursive)) continue if not line.strip() or line.startswith(base_indent + " "): @@ -396,6 +409,7 @@ def find_autosummary_in_lines(lines: List[str], module: Any = None, filename: st if m: in_autosummary = True base_indent = m.group(1) + recursive = False toctree = None template = None continue diff --git a/sphinx/ext/autosummary/templates/autosummary/module.rst b/sphinx/ext/autosummary/templates/autosummary/module.rst index 141ab779b..6b8b0392d 100644 --- a/sphinx/ext/autosummary/templates/autosummary/module.rst +++ b/sphinx/ext/autosummary/templates/autosummary/module.rst @@ -41,6 +41,7 @@ .. autosummary:: :toctree: modules + :recursive: {% for item in modules %} {{ item }} {%- endfor %} diff --git a/tests/roots/test-ext-autosummary-recursive/index.rst b/tests/roots/test-ext-autosummary-recursive/index.rst index d5292607c..5855bfa71 100644 --- a/tests/roots/test-ext-autosummary-recursive/index.rst +++ b/tests/roots/test-ext-autosummary-recursive/index.rst @@ -5,5 +5,11 @@ API Reference .. autosummary:: :toctree: generated + :recursive: package + +.. autosummary:: + :toctree: generated + + package2 diff --git a/tests/roots/test-ext-autosummary-recursive/package2/__init__.py b/tests/roots/test-ext-autosummary-recursive/package2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/roots/test-ext-autosummary-recursive/package2/module.py b/tests/roots/test-ext-autosummary-recursive/package2/module.py new file mode 100644 index 000000000..5506d0bc9 --- /dev/null +++ b/tests/roots/test-ext-autosummary-recursive/package2/module.py @@ -0,0 +1,13 @@ +from os import * # NOQA + + +class Foo: + def __init__(self): + pass + + def bar(self): + pass + + @property + def baz(self): + pass diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 7b59c8dd5..de439248e 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -292,6 +292,11 @@ def test_autosummary_recursive_enabled(app, status, warning): if toctree: assert not (generated / toctree).exists() + # autosummary without :recursive: option + generated = app.srcdir / 'generated' + assert (generated / 'package2.rst').exists() + assert not (generated / 'package2.module.rst').exists() + @pytest.mark.sphinx('dummy', testroot='ext-autosummary-recursive', srcdir='ext-autosummary-recursive-disabled', @@ -382,7 +387,7 @@ def test_autosummary_imported_members(app, status, warning): @pytest.mark.sphinx(testroot='ext-autodoc') def test_generate_autosummary_docs_property(app): with patch('sphinx.ext.autosummary.generate.find_autosummary_in_files') as mock: - mock.return_value = [AutosummaryEntry('target.methods.Base.prop', 'prop', None)] + mock.return_value = [AutosummaryEntry('target.methods.Base.prop', 'prop', None, False)] generate_autosummary_docs([], output_dir=app.srcdir, builder=app.builder, app=app) content = (app.srcdir / 'target.methods.Base.prop.rst').text()