mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #7527 from tk0miya/6040_autosummary_recursive
recursive autosummary feature
This commit is contained in:
commit
acf9c0c3c6
@ -32,7 +32,8 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
|||||||
|
|
||||||
The :rst:dir:`autosummary` directive can also optionally serve as a
|
The :rst:dir:`autosummary` directive can also optionally serve as a
|
||||||
:rst:dir:`toctree` entry for the included items. Optionally, stub
|
:rst:dir:`toctree` entry for the included items. Optionally, stub
|
||||||
``.rst`` files for these items can also be automatically generated.
|
``.rst`` files for these items can also be automatically generated
|
||||||
|
when :confval:`autosummary_generate` is `True`.
|
||||||
|
|
||||||
For example, ::
|
For example, ::
|
||||||
|
|
||||||
@ -105,6 +106,17 @@ The :mod:`sphinx.ext.autosummary` extension does this in two parts:
|
|||||||
|
|
||||||
.. versionadded:: 1.0
|
.. 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
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
|
||||||
:program:`sphinx-autogen` -- generate autodoc stub pages
|
:program:`sphinx-autogen` -- generate autodoc stub pages
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
@ -142,7 +154,7 @@ also use these config values:
|
|||||||
.. confval:: autosummary_generate
|
.. confval:: autosummary_generate
|
||||||
|
|
||||||
Boolean indicating whether to scan all found documents for autosummary
|
Boolean indicating whether to scan all found documents for autosummary
|
||||||
directives, and to generate stub pages for each.
|
directives, and to generate stub pages for each. It is disabled by default.
|
||||||
|
|
||||||
Can also be a list of documents for which stub pages should be generated.
|
Can also be a list of documents for which stub pages should be generated.
|
||||||
|
|
||||||
@ -269,6 +281,12 @@ The following variables available in the templates:
|
|||||||
List containing names of "public" attributes in the class. Only available
|
List containing names of "public" attributes in the class. Only available
|
||||||
for classes.
|
for classes.
|
||||||
|
|
||||||
|
.. data:: modules
|
||||||
|
|
||||||
|
List containing names of "public" modules in the package. Only available for
|
||||||
|
modules that are packages.
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
Additionally, the following filters are available
|
Additionally, the following filters are available
|
||||||
|
|
||||||
|
@ -231,6 +231,7 @@ class Autosummary(SphinxDirective):
|
|||||||
'caption': directives.unchanged_required,
|
'caption': directives.unchanged_required,
|
||||||
'toctree': directives.unchanged,
|
'toctree': directives.unchanged,
|
||||||
'nosignatures': directives.flag,
|
'nosignatures': directives.flag,
|
||||||
|
'recursive': directives.flag,
|
||||||
'template': directives.unchanged,
|
'template': directives.unchanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
|
import pkgutil
|
||||||
import pydoc
|
import pydoc
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@ -68,7 +69,8 @@ class DummyApplication:
|
|||||||
|
|
||||||
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
|
AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
|
||||||
('path', str),
|
('path', str),
|
||||||
('template', str)])
|
('template', str),
|
||||||
|
('recursive', bool)])
|
||||||
|
|
||||||
|
|
||||||
def setup_documenters(app: Any) -> None:
|
def setup_documenters(app: Any) -> None:
|
||||||
@ -147,7 +149,8 @@ class AutosummaryRenderer:
|
|||||||
|
|
||||||
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,
|
||||||
imported_members: bool, app: Any) -> str:
|
imported_members: bool, app: Any,
|
||||||
|
recursive: bool) -> str:
|
||||||
doc = get_documenter(app, obj, parent)
|
doc = get_documenter(app, obj, parent)
|
||||||
|
|
||||||
if template_name is None:
|
if template_name is None:
|
||||||
@ -192,6 +195,14 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
public.append(name)
|
public.append(name)
|
||||||
return public, items
|
return public, items
|
||||||
|
|
||||||
|
def get_modules(obj: Any) -> Tuple[List[str], List[str]]:
|
||||||
|
items = [] # type: List[str]
|
||||||
|
for _, modname, ispkg in pkgutil.iter_modules(obj.__path__):
|
||||||
|
fullname = name + '.' + modname
|
||||||
|
items.append(fullname)
|
||||||
|
public = [x for x in items if not x.split('.')[-1].startswith('_')]
|
||||||
|
return public, items
|
||||||
|
|
||||||
ns = {} # type: Dict[str, Any]
|
ns = {} # type: Dict[str, Any]
|
||||||
|
|
||||||
if doc.objtype == 'module':
|
if doc.objtype == 'module':
|
||||||
@ -202,6 +213,9 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
get_members(obj, {'class'}, imported=imported_members)
|
get_members(obj, {'class'}, imported=imported_members)
|
||||||
ns['exceptions'], ns['all_exceptions'] = \
|
ns['exceptions'], ns['all_exceptions'] = \
|
||||||
get_members(obj, {'exception'}, imported=imported_members)
|
get_members(obj, {'exception'}, imported=imported_members)
|
||||||
|
ispackage = hasattr(obj, '__path__')
|
||||||
|
if ispackage and recursive:
|
||||||
|
ns['modules'], ns['all_modules'] = get_modules(obj)
|
||||||
elif doc.objtype == 'class':
|
elif doc.objtype == 'class':
|
||||||
ns['members'] = dir(obj)
|
ns['members'] = dir(obj)
|
||||||
ns['inherited_members'] = \
|
ns['inherited_members'] = \
|
||||||
@ -288,7 +302,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
content = generate_autosummary_content(name, obj, parent, template, entry.template,
|
content = generate_autosummary_content(name, obj, parent, template, entry.template,
|
||||||
imported_members, app)
|
imported_members, app, entry.recursive)
|
||||||
|
|
||||||
filename = os.path.join(path, name + suffix)
|
filename = os.path.join(path, name + suffix)
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
@ -373,11 +387,13 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
module_re = re.compile(
|
module_re = re.compile(
|
||||||
r'^\s*\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$')
|
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*.*?')
|
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*$')
|
toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$')
|
||||||
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
|
template_arg_re = re.compile(r'^\s+:template:\s*(.*?)\s*$')
|
||||||
|
|
||||||
documented = [] # type: List[AutosummaryEntry]
|
documented = [] # type: List[AutosummaryEntry]
|
||||||
|
|
||||||
|
recursive = False
|
||||||
toctree = None # type: str
|
toctree = None # type: str
|
||||||
template = None
|
template = None
|
||||||
current_module = module
|
current_module = module
|
||||||
@ -386,6 +402,11 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if in_autosummary:
|
if in_autosummary:
|
||||||
|
m = recursive_arg_re.match(line)
|
||||||
|
if m:
|
||||||
|
recursive = True
|
||||||
|
continue
|
||||||
|
|
||||||
m = toctree_arg_re.match(line)
|
m = toctree_arg_re.match(line)
|
||||||
if m:
|
if m:
|
||||||
toctree = m.group(1)
|
toctree = m.group(1)
|
||||||
@ -410,7 +431,7 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
if current_module and \
|
if current_module and \
|
||||||
not name.startswith(current_module + '.'):
|
not name.startswith(current_module + '.'):
|
||||||
name = "%s.%s" % (current_module, name)
|
name = "%s.%s" % (current_module, name)
|
||||||
documented.append(AutosummaryEntry(name, toctree, template))
|
documented.append(AutosummaryEntry(name, toctree, template, recursive))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not line.strip() or line.startswith(base_indent + " "):
|
if not line.strip() or line.startswith(base_indent + " "):
|
||||||
@ -422,6 +443,7 @@ def find_autosummary_in_lines(lines: List[str], module: str = None, filename: st
|
|||||||
if m:
|
if m:
|
||||||
in_autosummary = True
|
in_autosummary = True
|
||||||
base_indent = m.group(1)
|
base_indent = m.group(1)
|
||||||
|
recursive = False
|
||||||
toctree = None
|
toctree = None
|
||||||
template = None
|
template = None
|
||||||
continue
|
continue
|
||||||
|
@ -34,3 +34,16 @@
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modules %}
|
||||||
|
{% if modules %}
|
||||||
|
.. rubric:: Modules
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:toctree:
|
||||||
|
:recursive:
|
||||||
|
{% for item in modules %}
|
||||||
|
{{ item }}
|
||||||
|
{%- endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
7
tests/roots/test-ext-autosummary-recursive/conf.py
Normal file
7
tests/roots/test-ext-autosummary-recursive/conf.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
extensions = ['sphinx.ext.autosummary']
|
||||||
|
autosummary_generate = True
|
15
tests/roots/test-ext-autosummary-recursive/index.rst
Normal file
15
tests/roots/test-ext-autosummary-recursive/index.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
API Reference
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. rubric:: Packages
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:toctree: generated
|
||||||
|
:recursive:
|
||||||
|
|
||||||
|
package
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:toctree: generated
|
||||||
|
|
||||||
|
package2
|
13
tests/roots/test-ext-autosummary-recursive/package/module.py
Normal file
13
tests/roots/test-ext-autosummary-recursive/package/module.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from os import * # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bar(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def baz(self):
|
||||||
|
pass
|
@ -0,0 +1,4 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
# Fail module import in a catastrophic way
|
||||||
|
sys.exit(1)
|
@ -0,0 +1,13 @@
|
|||||||
|
from os import * # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bar(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def baz(self):
|
||||||
|
pass
|
@ -0,0 +1,13 @@
|
|||||||
|
from os import * # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def bar(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def baz(self):
|
||||||
|
pass
|
@ -261,6 +261,31 @@ def test_autosummary_generate_overwrite2(app_params, make_app):
|
|||||||
assert 'autosummary_dummy_module.rst' not in app._warning.getvalue()
|
assert 'autosummary_dummy_module.rst' not in app._warning.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-recursive')
|
||||||
|
def test_autosummary_recursive(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
|
||||||
|
# autosummary having :recursive: option
|
||||||
|
assert (app.srcdir / 'generated' / 'package.rst').exists()
|
||||||
|
assert (app.srcdir / 'generated' / 'package.module.rst').exists()
|
||||||
|
assert (app.srcdir / 'generated' / 'package.module_importfail.rst').exists() is False
|
||||||
|
assert (app.srcdir / 'generated' / 'package.package.rst').exists()
|
||||||
|
assert (app.srcdir / 'generated' / 'package.package.module.rst').exists()
|
||||||
|
|
||||||
|
# autosummary not having :recursive: option
|
||||||
|
assert (app.srcdir / 'generated' / 'package2.rst').exists()
|
||||||
|
assert (app.srcdir / 'generated' / 'package2.module.rst').exists() is False
|
||||||
|
|
||||||
|
# Check content of recursively generated stub-files
|
||||||
|
content = (app.srcdir / 'generated' / 'package.rst').read_text()
|
||||||
|
assert 'package.module' in content
|
||||||
|
assert 'package.package' in content
|
||||||
|
assert 'package.module_importfail' in content
|
||||||
|
|
||||||
|
content = (app.srcdir / 'generated' / 'package.package.rst').read_text()
|
||||||
|
assert 'package.package.module' in content
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('latex', **default_kw)
|
@pytest.mark.sphinx('latex', **default_kw)
|
||||||
def test_autosummary_latex_table_colspec(app, status, warning):
|
def test_autosummary_latex_table_colspec(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
@ -330,7 +355,7 @@ def test_autosummary_imported_members(app, status, warning):
|
|||||||
@pytest.mark.sphinx(testroot='ext-autodoc')
|
@pytest.mark.sphinx(testroot='ext-autodoc')
|
||||||
def test_generate_autosummary_docs_property(app):
|
def test_generate_autosummary_docs_property(app):
|
||||||
with patch('sphinx.ext.autosummary.generate.find_autosummary_in_files') as mock:
|
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)
|
generate_autosummary_docs([], output_dir=app.srcdir, builder=app.builder, app=app)
|
||||||
|
|
||||||
content = (app.srcdir / 'target.methods.Base.prop.rst').read_text()
|
content = (app.srcdir / 'target.methods.Base.prop.rst').read_text()
|
||||||
|
Loading…
Reference in New Issue
Block a user