Skip to content

Commit 179e242

Browse files
Add the sphinx.ext.apidoc extension (#13333)
A common use-case is that users simply want to point Sphinx towards a Python module, and have it generate documentation automatically. This is not possible currently, without a "pre-build" step of running the ``sphinx-autogen`` CLI. This PR adds ``sphinx.ext.apidoc`` as a first-party extension, to incorporate the source file generation into the Sphinx build. Co-authored-by: Adam Turner <[email protected]>
1 parent c85c63f commit 179e242

File tree

11 files changed

+583
-7
lines changed

11 files changed

+583
-7
lines changed

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ Features added
114114
* #13354: Insert abbreviation nodes (hover text) for positional- and keyword-only
115115
separators in Python signatures.
116116
Patch by Adam Turner.
117+
* #13333: Add the :mod:`sphinx.ext.autodoc` extension,
118+
to automate API documentation generation from Python modules.
119+
Patch by Chris Sewell and Adam Turner.
117120

118121
Bugs fixed
119122
----------

doc/usage/extensions/apidoc.rst

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
.. _ext-apidoc:
2+
3+
:mod:`sphinx.ext.apidoc` -- Generate API documentation from Python packages
4+
===========================================================================
5+
6+
.. py:module:: sphinx.ext.apidoc
7+
:synopsis: Generate API documentation from Python modules
8+
9+
.. index:: pair: automatic; documentation
10+
.. index:: pair: generation; documentation
11+
.. index:: pair: generate; documentation
12+
13+
.. versionadded:: 8.2
14+
15+
.. role:: code-py(code)
16+
:language: Python
17+
18+
:mod:`sphinx.ext.apidoc` is a tool for automatic generation
19+
of Sphinx sources from Python packages.
20+
It provides the :program:`sphinx-apidoc` command-line tool as an extension,
21+
allowing it to be run during the Sphinx build process.
22+
23+
The extension writes generated source files to a provided directory,
24+
which are then read by Sphinx using the :mod:`sphinx.ext.autodoc` extension.
25+
26+
.. warning::
27+
28+
:mod:`sphinx.ext.apidoc` generates source files that
29+
use :mod:`sphinx.ext.autodoc` to document all found modules.
30+
If any modules have side effects on import,
31+
these will be executed by ``autodoc`` when :program:`sphinx-build` is run.
32+
33+
If you document scripts (as opposed to library modules),
34+
make sure their main routine is protected by
35+
an ``if __name__ == '__main__'`` condition.
36+
37+
38+
Configuration
39+
-------------
40+
41+
The apidoc extension uses the following configuration values:
42+
43+
.. confval:: apidoc_modules
44+
:type: :code-py:`Sequence[dict[str, str | int | bool | Sequence[str] | Set[str]]`
45+
:default: :code-py:`()`
46+
47+
A list or sequence of dictionaries describing modules to document.
48+
If a value is left unspecified in any dictionary,
49+
the general configuration value is used as the default.
50+
51+
For example:
52+
53+
.. code-block:: python
54+
55+
apidoc_modules = [
56+
{'path': 'path/to/module', 'destination': 'source/'},
57+
{
58+
'path': 'path/to/another_module',
59+
'destination': 'source/',
60+
'exclude_patterns': ['**/test*'],
61+
'max_depth': 4,
62+
'follow_links': False,
63+
'separate_modules': False,
64+
'include_private': False,
65+
'no_headings': False,
66+
'module_first': False,
67+
'implicit_namespaces': False,
68+
'automodule_options': {
69+
'members', 'show-inheritance', 'undoc-members'
70+
},
71+
},
72+
]
73+
74+
75+
Valid keys are:
76+
77+
:code-py:`'path'`
78+
The path to the module to document (**required**).
79+
This must be absolute or relative to the configuration directory.
80+
81+
:code-py:`'destination'`
82+
The output directory for generated files (**required**).
83+
This must be relative to the source directory,
84+
and will be created if it does not exist.
85+
86+
:code-py:`'exclude_patterns'`
87+
See :confval:`apidoc_exclude_patterns`.
88+
89+
:code-py:`'max_depth'`
90+
See :confval:`apidoc_max_depth`.
91+
92+
:code-py:`'follow_links'`
93+
See :confval:`apidoc_follow_links`.
94+
95+
:code-py:`'separate_modules'`
96+
See :confval:`apidoc_separate_modules`.
97+
98+
:code-py:`'include_private'`
99+
See :confval:`apidoc_include_private`.
100+
101+
:code-py:`'no_headings'`
102+
See :confval:`apidoc_no_headings`.
103+
104+
:code-py:`'module_first'`
105+
See :confval:`apidoc_module_first`.
106+
107+
:code-py:`'implicit_namespaces'`
108+
See :confval:`apidoc_implicit_namespaces`.
109+
110+
:code-py:`'automodule_options'`
111+
See :confval:`apidoc_automodule_options`.
112+
113+
.. confval:: apidoc_exclude_patterns
114+
:type: :code-py:`Sequence[str]`
115+
:default: :code-py:`()`
116+
117+
A sequence of patterns to exclude from generation.
118+
These may be literal paths or :py:mod:`fnmatch`-style patterns.
119+
120+
.. confval:: apidoc_max_depth
121+
:type: :code-py:`int`
122+
:default: :code-py:`4`
123+
124+
The maximum depth of submodules to show in the generated table of contents.
125+
126+
.. confval:: apidoc_follow_links
127+
:type: :code-py:`bool`
128+
:default: :code-py:`False`
129+
130+
Follow symbolic links.
131+
132+
.. confval:: apidoc_separate_modules
133+
:type: :code-py:`bool`
134+
:default: :code-py:`False`
135+
136+
Put documentation for each module on an individual page.
137+
138+
.. confval:: apidoc_include_private
139+
:type: :code-py:`bool`
140+
:default: :code-py:`False`
141+
142+
Generate documentation for '_private' modules with leading underscores.
143+
144+
.. confval:: apidoc_no_headings
145+
:type: :code-py:`bool`
146+
:default: :code-py:`False`
147+
148+
Do not create headings for the modules/packages.
149+
Useful when source docstrings already contain headings.
150+
151+
.. confval:: apidoc_module_first
152+
:type: :code-py:`bool`
153+
:default: :code-py:`False`
154+
155+
Place module documentation before submodule documentation.
156+
157+
.. confval:: apidoc_implicit_namespaces
158+
:type: :code-py:`bool`
159+
:default: :code-py:`False`
160+
161+
By default sphinx-apidoc processes sys.path searching for modules only.
162+
Python 3.3 introduced :pep:`420` implicit namespaces that allow module path
163+
structures such as ``foo/bar/module.py`` or ``foo/bar/baz/__init__.py``
164+
(notice that ``bar`` and ``foo`` are namespaces, not modules).
165+
166+
Interpret module paths using :pep:`420` implicit namespaces.
167+
168+
.. confval:: apidoc_automodule_options
169+
:type: :code-py:`Set[str]`
170+
:default: :code-py:`{'members', 'show-inheritance', 'undoc-members'}`
171+
172+
Options to pass to generated :rst:dir:`automodule` directives.

doc/usage/extensions/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ These extensions are built in and can be activated by respective entries in the
2121
.. toctree::
2222
:maxdepth: 1
2323

24+
apidoc
2425
autodoc
2526
autosectionlabel
2627
autosummary

sphinx/ext/apidoc/__init__.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,54 @@
1313

1414
from typing import TYPE_CHECKING
1515

16+
import sphinx
1617
from sphinx.ext.apidoc._cli import main
1718

1819
if TYPE_CHECKING:
1920
from collections.abc import Sequence
2021

21-
__all__: Sequence[str] = ('main',)
22+
from sphinx.application import Sphinx
23+
from sphinx.util.typing import ExtensionMetadata
24+
25+
__all__: Sequence[str] = 'main', 'setup'
26+
27+
28+
def setup(app: Sphinx) -> ExtensionMetadata:
29+
from sphinx.ext.apidoc._extension import run_apidoc
30+
31+
# Require autodoc
32+
app.setup_extension('sphinx.ext.autodoc')
33+
34+
# Configuration values
35+
app.add_config_value(
36+
'apidoc_exclude_patterns', (), 'env', types=frozenset({list, tuple})
37+
)
38+
app.add_config_value('apidoc_max_depth', 4, 'env', types=frozenset({int}))
39+
app.add_config_value('apidoc_follow_links', False, 'env', types=frozenset({bool}))
40+
app.add_config_value(
41+
'apidoc_separate_modules', False, 'env', types=frozenset({bool})
42+
)
43+
app.add_config_value(
44+
'apidoc_include_private', False, 'env', types=frozenset({bool})
45+
)
46+
app.add_config_value('apidoc_no_headings', False, 'env', types=frozenset({bool}))
47+
app.add_config_value('apidoc_module_first', False, 'env', types=frozenset({bool}))
48+
app.add_config_value(
49+
'apidoc_implicit_namespaces', False, 'env', types=frozenset({bool})
50+
)
51+
app.add_config_value(
52+
'apidoc_automodule_options',
53+
frozenset(('members', 'undoc-members', 'show-inheritance')),
54+
'env',
55+
types=frozenset({frozenset, list, set, tuple}),
56+
)
57+
app.add_config_value('apidoc_modules', (), 'env', types=frozenset({list, tuple}))
58+
59+
# Entry point to run apidoc
60+
app.connect('builder-inited', run_apidoc)
61+
62+
return {
63+
'version': sphinx.__display_version__,
64+
'parallel_read_safe': True,
65+
'parallel_write_safe': True,
66+
}

0 commit comments

Comments
 (0)