Sphinx 8.2.0rc1__py3-none-any.whl → 8.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Sphinx might be problematic. Click here for more details.
- sphinx/__init__.py +2 -2
- sphinx/application.py +3 -3
- sphinx/domains/math.py +2 -0
- sphinx/domains/python/_annotations.py +26 -10
- sphinx/domains/python/_object.py +4 -1
- sphinx/ext/apidoc/__init__.py +46 -1
- sphinx/ext/apidoc/_cli.py +25 -25
- sphinx/ext/apidoc/_extension.py +262 -0
- sphinx/ext/apidoc/_generate.py +14 -14
- sphinx/ext/apidoc/_shared.py +51 -18
- sphinx/ext/autodoc/__init__.py +3 -3
- sphinx/ext/autodoc/directive.py +1 -1
- sphinx/ext/autodoc/mock.py +1 -1
- sphinx/ext/autosummary/__init__.py +11 -0
- sphinx/ext/napoleon/__init__.py +23 -24
- sphinx/ext/viewcode.py +12 -8
- sphinx/highlighting.py +1 -1
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/sphinx.pot +2069 -1964
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/testing/path.py +8 -0
- sphinx/themes/basic/static/basic.css.jinja +0 -8
- sphinx/util/_files.py +2 -2
- sphinx/util/fileutil.py +2 -1
- sphinx/util/osutil.py +6 -1
- sphinx/writers/html5.py +2 -2
- sphinx/writers/latex.py +3 -2
- sphinx/writers/texinfo.py +3 -2
- sphinx/writers/text.py +2 -2
- {sphinx-8.2.0rc1.dist-info → sphinx-8.2.1.dist-info}/METADATA +8 -5
- {sphinx-8.2.0rc1.dist-info → sphinx-8.2.1.dist-info}/RECORD +164 -160
- {sphinx-8.2.0rc1.dist-info → sphinx-8.2.1.dist-info}/WHEEL +1 -1
- {sphinx-8.2.0rc1.dist-info → sphinx-8.2.1.dist-info}/entry_points.txt +0 -0
- {sphinx-8.2.0rc1.dist-info → sphinx-8.2.1.dist-info/licenses}/LICENSE.rst +0 -0
sphinx/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = '8.2.
|
|
5
|
+
__version__ = '8.2.1'
|
|
6
6
|
__display_version__ = __version__ # used for command line version
|
|
7
7
|
|
|
8
8
|
# Keep this file executable as-is in Python 3!
|
|
@@ -34,7 +34,7 @@ warnings.filterwarnings(
|
|
|
34
34
|
#:
|
|
35
35
|
#: .. versionadded:: 1.2
|
|
36
36
|
#: Before version 1.2, check the string ``sphinx.__version__``.
|
|
37
|
-
version_info = (8, 2,
|
|
37
|
+
version_info = (8, 2, 1, 'final', 0)
|
|
38
38
|
|
|
39
39
|
package_dir = _StrPath(__file__).resolve().parent
|
|
40
40
|
|
sphinx/application.py
CHANGED
|
@@ -1116,7 +1116,7 @@ class Sphinx:
|
|
|
1116
1116
|
logger.debug('[app] adding directive: %r', (name, cls))
|
|
1117
1117
|
if not override and docutils.is_directive_registered(name):
|
|
1118
1118
|
logger.warning(
|
|
1119
|
-
__('directive %r is already registered
|
|
1119
|
+
__('directive %r is already registered and will not be overridden'),
|
|
1120
1120
|
name,
|
|
1121
1121
|
type='app',
|
|
1122
1122
|
subtype='add_directive',
|
|
@@ -1142,7 +1142,7 @@ class Sphinx:
|
|
|
1142
1142
|
logger.debug('[app] adding role: %r', (name, role))
|
|
1143
1143
|
if not override and docutils.is_role_registered(name):
|
|
1144
1144
|
logger.warning(
|
|
1145
|
-
__('role %r is already registered
|
|
1145
|
+
__('role %r is already registered and will not be overridden'),
|
|
1146
1146
|
name,
|
|
1147
1147
|
type='app',
|
|
1148
1148
|
subtype='add_role',
|
|
@@ -1170,7 +1170,7 @@ class Sphinx:
|
|
|
1170
1170
|
logger.debug('[app] adding generic role: %r', (name, nodeclass))
|
|
1171
1171
|
if not override and docutils.is_role_registered(name):
|
|
1172
1172
|
logger.warning(
|
|
1173
|
-
__('role %r is already registered
|
|
1173
|
+
__('role %r is already registered and will not be overridden'),
|
|
1174
1174
|
name,
|
|
1175
1175
|
type='app',
|
|
1176
1176
|
subtype='add_generic_role',
|
sphinx/domains/math.py
CHANGED
|
@@ -49,6 +49,8 @@ class MathDomain(Domain):
|
|
|
49
49
|
|
|
50
50
|
initial_data: dict[str, Any] = {
|
|
51
51
|
'objects': {}, # labelid -> (docname, eqno)
|
|
52
|
+
# backwards compatibility
|
|
53
|
+
'has_equations': {}, # https://github.com/sphinx-doc/sphinx/issues/13346
|
|
52
54
|
}
|
|
53
55
|
dangling_warnings = {
|
|
54
56
|
'eq': 'equation not found: %(target)s',
|
|
@@ -12,6 +12,7 @@ from docutils import nodes
|
|
|
12
12
|
|
|
13
13
|
from sphinx import addnodes
|
|
14
14
|
from sphinx.addnodes import pending_xref, pending_xref_condition
|
|
15
|
+
from sphinx.locale import _
|
|
15
16
|
from sphinx.pycode.parser import Token, TokenProcessor
|
|
16
17
|
from sphinx.util.inspect import signature_from_str
|
|
17
18
|
|
|
@@ -479,19 +480,13 @@ def _parse_arglist(
|
|
|
479
480
|
last_kind = None
|
|
480
481
|
for param in sig.parameters.values():
|
|
481
482
|
if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
|
|
482
|
-
|
|
483
|
-
params += addnodes.desc_parameter(
|
|
484
|
-
'', '', addnodes.desc_sig_operator('', '/')
|
|
485
|
-
)
|
|
483
|
+
params += _positional_only_separator()
|
|
486
484
|
if param.kind == param.KEYWORD_ONLY and last_kind in {
|
|
487
485
|
param.POSITIONAL_OR_KEYWORD,
|
|
488
486
|
param.POSITIONAL_ONLY,
|
|
489
487
|
None,
|
|
490
488
|
}:
|
|
491
|
-
|
|
492
|
-
params += addnodes.desc_parameter(
|
|
493
|
-
'', '', addnodes.desc_sig_operator('', '*')
|
|
494
|
-
)
|
|
489
|
+
params += _keyword_only_separator()
|
|
495
490
|
|
|
496
491
|
node = addnodes.desc_parameter()
|
|
497
492
|
if param.kind == param.VAR_POSITIONAL:
|
|
@@ -523,12 +518,33 @@ def _parse_arglist(
|
|
|
523
518
|
last_kind = param.kind
|
|
524
519
|
|
|
525
520
|
if last_kind == Parameter.POSITIONAL_ONLY:
|
|
526
|
-
|
|
527
|
-
params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/'))
|
|
521
|
+
params += _positional_only_separator()
|
|
528
522
|
|
|
529
523
|
return params
|
|
530
524
|
|
|
531
525
|
|
|
526
|
+
def _positional_only_separator() -> addnodes.desc_parameter:
|
|
527
|
+
# PEP 570: Separator for positional only parameters: /
|
|
528
|
+
positional_only_abbr = nodes.abbreviation(
|
|
529
|
+
'/', '/', explanation=_('Positional-only parameter separator (PEP 570)')
|
|
530
|
+
)
|
|
531
|
+
positional_only_op = addnodes.desc_sig_operator(
|
|
532
|
+
'/', '', positional_only_abbr, classes=['positional-only-separator']
|
|
533
|
+
)
|
|
534
|
+
return addnodes.desc_parameter('/', '', positional_only_op)
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def _keyword_only_separator() -> addnodes.desc_parameter:
|
|
538
|
+
# PEP 3102: Separator for keyword only parameters: *
|
|
539
|
+
keyword_only_abbr = nodes.abbreviation(
|
|
540
|
+
'*', '*', explanation=_('Keyword-only parameters separator (PEP 3102)')
|
|
541
|
+
)
|
|
542
|
+
keyword_only_op = addnodes.desc_sig_operator(
|
|
543
|
+
'*', '', keyword_only_abbr, classes=['keyword-only-separator']
|
|
544
|
+
)
|
|
545
|
+
return addnodes.desc_parameter('*', '', keyword_only_op)
|
|
546
|
+
|
|
547
|
+
|
|
532
548
|
def _pseudo_parse_arglist(
|
|
533
549
|
signode: desc_signature,
|
|
534
550
|
arglist: str,
|
sphinx/domains/python/_object.py
CHANGED
|
@@ -352,11 +352,14 @@ class PyObject(ObjectDescription[tuple[str, str]]):
|
|
|
352
352
|
multi_line_parameter_list,
|
|
353
353
|
trailing_comma,
|
|
354
354
|
)
|
|
355
|
-
except SyntaxError:
|
|
355
|
+
except SyntaxError as exc:
|
|
356
356
|
# fallback to parse arglist original parser
|
|
357
357
|
# (this may happen if the argument list is incorrectly used
|
|
358
358
|
# as a list of bases when documenting a class)
|
|
359
359
|
# it supports to represent optional arguments (ex. "func(foo [, bar])")
|
|
360
|
+
logger.debug(
|
|
361
|
+
'syntax error in arglist (%r): %s', arglist, exc, location=signode
|
|
362
|
+
)
|
|
360
363
|
_pseudo_parse_arglist(
|
|
361
364
|
signode,
|
|
362
365
|
arglist,
|
sphinx/ext/apidoc/__init__.py
CHANGED
|
@@ -13,9 +13,54 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
+
import sphinx
|
|
16
17
|
from sphinx.ext.apidoc._cli import main
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from collections.abc import Sequence
|
|
20
21
|
|
|
21
|
-
|
|
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
|
+
}
|
sphinx/ext/apidoc/_cli.py
CHANGED
|
@@ -55,7 +55,7 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
55
55
|
'-o',
|
|
56
56
|
'--output-dir',
|
|
57
57
|
action='store',
|
|
58
|
-
dest='
|
|
58
|
+
dest='dest_dir',
|
|
59
59
|
required=True,
|
|
60
60
|
help=__('directory to place all output'),
|
|
61
61
|
)
|
|
@@ -69,7 +69,7 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
69
69
|
'-d',
|
|
70
70
|
'--maxdepth',
|
|
71
71
|
action='store',
|
|
72
|
-
dest='
|
|
72
|
+
dest='max_depth',
|
|
73
73
|
type=int,
|
|
74
74
|
default=4,
|
|
75
75
|
help=__('maximum depth of submodules to show in the TOC (default: 4)'),
|
|
@@ -85,7 +85,7 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
85
85
|
'-l',
|
|
86
86
|
'--follow-links',
|
|
87
87
|
action='store_true',
|
|
88
|
-
dest='
|
|
88
|
+
dest='follow_links',
|
|
89
89
|
default=False,
|
|
90
90
|
help=__(
|
|
91
91
|
'follow symbolic links. Powerful when combined with collective.recipe.omelette.'
|
|
@@ -95,27 +95,27 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
95
95
|
'-n',
|
|
96
96
|
'--dry-run',
|
|
97
97
|
action='store_true',
|
|
98
|
-
dest='
|
|
98
|
+
dest='dry_run',
|
|
99
99
|
help=__('run the script without creating files'),
|
|
100
100
|
)
|
|
101
101
|
parser.add_argument(
|
|
102
102
|
'-e',
|
|
103
103
|
'--separate',
|
|
104
104
|
action='store_true',
|
|
105
|
-
dest='
|
|
105
|
+
dest='separate_modules',
|
|
106
106
|
help=__('put documentation for each module on its own page'),
|
|
107
107
|
)
|
|
108
108
|
parser.add_argument(
|
|
109
109
|
'-P',
|
|
110
110
|
'--private',
|
|
111
111
|
action='store_true',
|
|
112
|
-
dest='
|
|
112
|
+
dest='include_private',
|
|
113
113
|
help=__('include "_private" modules'),
|
|
114
114
|
)
|
|
115
115
|
parser.add_argument(
|
|
116
116
|
'--tocfile',
|
|
117
117
|
action='store',
|
|
118
|
-
dest='
|
|
118
|
+
dest='toc_file',
|
|
119
119
|
default='modules',
|
|
120
120
|
help=__('filename of table of contents (default: modules)'),
|
|
121
121
|
)
|
|
@@ -123,14 +123,14 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
123
123
|
'-T',
|
|
124
124
|
'--no-toc',
|
|
125
125
|
action='store_false',
|
|
126
|
-
dest='
|
|
126
|
+
dest='toc_file',
|
|
127
127
|
help=__("don't create a table of contents file"),
|
|
128
128
|
)
|
|
129
129
|
parser.add_argument(
|
|
130
130
|
'-E',
|
|
131
131
|
'--no-headings',
|
|
132
132
|
action='store_true',
|
|
133
|
-
dest='
|
|
133
|
+
dest='no_headings',
|
|
134
134
|
help=__(
|
|
135
135
|
"don't create headings for the module/package "
|
|
136
136
|
'packages (e.g. when the docstrings already '
|
|
@@ -141,7 +141,7 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
141
141
|
'-M',
|
|
142
142
|
'--module-first',
|
|
143
143
|
action='store_true',
|
|
144
|
-
dest='
|
|
144
|
+
dest='module_first',
|
|
145
145
|
help=__('put module documentation before submodule documentation'),
|
|
146
146
|
)
|
|
147
147
|
parser.add_argument(
|
|
@@ -245,7 +245,7 @@ Note: By default this script will not overwrite already created files."""),
|
|
|
245
245
|
'-t',
|
|
246
246
|
'--templatedir',
|
|
247
247
|
metavar='TEMPLATEDIR',
|
|
248
|
-
dest='
|
|
248
|
+
dest='template_dir',
|
|
249
249
|
help=__('template directory for template files'),
|
|
250
250
|
)
|
|
251
251
|
|
|
@@ -264,17 +264,17 @@ def main(argv: Sequence[str] = (), /) -> int:
|
|
|
264
264
|
for exclude in dict.fromkeys(opts.exclude_pattern)
|
|
265
265
|
)
|
|
266
266
|
|
|
267
|
-
written_files, modules = recurse_tree(rootpath, excludes, opts, opts.
|
|
267
|
+
written_files, modules = recurse_tree(rootpath, excludes, opts, opts.template_dir)
|
|
268
268
|
|
|
269
269
|
if opts.full:
|
|
270
270
|
_full_quickstart(opts, modules=modules)
|
|
271
|
-
elif opts.
|
|
271
|
+
elif opts.toc_file:
|
|
272
272
|
written_files.append(
|
|
273
|
-
create_modules_toc_file(modules, opts, opts.
|
|
273
|
+
create_modules_toc_file(modules, opts, opts.toc_file, opts.template_dir)
|
|
274
274
|
)
|
|
275
275
|
|
|
276
|
-
if opts.remove_old and not opts.
|
|
277
|
-
_remove_old_files(written_files, opts.
|
|
276
|
+
if opts.remove_old and not opts.dry_run:
|
|
277
|
+
_remove_old_files(written_files, opts.dest_dir, opts.suffix)
|
|
278
278
|
|
|
279
279
|
return 0
|
|
280
280
|
|
|
@@ -286,7 +286,7 @@ def _parse_args(argv: Sequence[str], /) -> ApidocOptions:
|
|
|
286
286
|
# normalise options
|
|
287
287
|
|
|
288
288
|
args.module_path = root_path = Path(args.module_path).resolve()
|
|
289
|
-
args.
|
|
289
|
+
args.dest_dir = Path(args.dest_dir)
|
|
290
290
|
if not root_path.is_dir():
|
|
291
291
|
LOGGER.error(__('%s is not a directory.'), root_path)
|
|
292
292
|
raise SystemExit(1)
|
|
@@ -295,13 +295,13 @@ def _parse_args(argv: Sequence[str], /) -> ApidocOptions:
|
|
|
295
295
|
args.header = root_path.name
|
|
296
296
|
args.suffix = args.suffix.removeprefix('.')
|
|
297
297
|
|
|
298
|
-
if not args.
|
|
299
|
-
ensuredir(args.
|
|
298
|
+
if not args.dry_run:
|
|
299
|
+
ensuredir(args.dest_dir)
|
|
300
300
|
|
|
301
301
|
if not args.automodule_options:
|
|
302
|
-
args.automodule_options =
|
|
302
|
+
args.automodule_options = frozenset()
|
|
303
303
|
elif isinstance(args.automodule_options, str):
|
|
304
|
-
args.automodule_options =
|
|
304
|
+
args.automodule_options = frozenset(args.automodule_options.split(','))
|
|
305
305
|
|
|
306
306
|
return ApidocOptions(**args.__dict__)
|
|
307
307
|
|
|
@@ -318,7 +318,7 @@ def _full_quickstart(opts: ApidocOptions, /, *, modules: list[str]) -> None:
|
|
|
318
318
|
prev_module = module
|
|
319
319
|
text += f' {module}\n'
|
|
320
320
|
d: dict[str, Any] = {
|
|
321
|
-
'path': str(opts.
|
|
321
|
+
'path': str(opts.dest_dir),
|
|
322
322
|
'sep': False,
|
|
323
323
|
'dot': '_',
|
|
324
324
|
'project': opts.header,
|
|
@@ -336,7 +336,7 @@ def _full_quickstart(opts: ApidocOptions, /, *, modules: list[str]) -> None:
|
|
|
336
336
|
'makefile': True,
|
|
337
337
|
'batchfile': True,
|
|
338
338
|
'make_mode': True,
|
|
339
|
-
'mastertocmaxdepth': opts.
|
|
339
|
+
'mastertocmaxdepth': opts.max_depth,
|
|
340
340
|
'mastertoctree': text,
|
|
341
341
|
'language': 'en',
|
|
342
342
|
'module_path': str(opts.module_path),
|
|
@@ -352,5 +352,5 @@ def _full_quickstart(opts: ApidocOptions, /, *, modules: list[str]) -> None:
|
|
|
352
352
|
d['extensions'].remove(ext)
|
|
353
353
|
d['extensions'].extend(ext.split(','))
|
|
354
354
|
|
|
355
|
-
if not opts.
|
|
356
|
-
qs.generate(d, silent=True, overwrite=opts.force, templatedir=opts.
|
|
355
|
+
if not opts.dry_run:
|
|
356
|
+
qs.generate(d, silent=True, overwrite=opts.force, templatedir=opts.template_dir)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Sphinx extension for auto-generating API documentation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import fnmatch
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from sphinx._cli.util.colour import bold
|
|
11
|
+
from sphinx.ext.apidoc._generate import create_modules_toc_file, recurse_tree
|
|
12
|
+
from sphinx.ext.apidoc._shared import (
|
|
13
|
+
LOGGER,
|
|
14
|
+
ApidocDefaults,
|
|
15
|
+
ApidocOptions,
|
|
16
|
+
_remove_old_files,
|
|
17
|
+
)
|
|
18
|
+
from sphinx.locale import __
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from collections.abc import Collection, Sequence
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from sphinx.application import Sphinx
|
|
25
|
+
|
|
26
|
+
_BOOL_KEYS = frozenset({
|
|
27
|
+
'follow_links',
|
|
28
|
+
'separate_modules',
|
|
29
|
+
'include_private',
|
|
30
|
+
'no_headings',
|
|
31
|
+
'module_first',
|
|
32
|
+
'implicit_namespaces',
|
|
33
|
+
})
|
|
34
|
+
_ALLOWED_KEYS = _BOOL_KEYS | frozenset({
|
|
35
|
+
'path',
|
|
36
|
+
'destination',
|
|
37
|
+
'exclude_patterns',
|
|
38
|
+
'automodule_options',
|
|
39
|
+
'max_depth',
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def run_apidoc(app: Sphinx) -> None:
|
|
44
|
+
"""Run the apidoc extension."""
|
|
45
|
+
defaults = ApidocDefaults.from_config(app.config)
|
|
46
|
+
apidoc_modules: Sequence[dict[str, Any]] = app.config.apidoc_modules
|
|
47
|
+
srcdir: Path = app.srcdir
|
|
48
|
+
confdir: Path = app.confdir
|
|
49
|
+
|
|
50
|
+
LOGGER.info(bold(__('Running apidoc')))
|
|
51
|
+
|
|
52
|
+
module_options: dict[str, Any]
|
|
53
|
+
for i, module_options in enumerate(apidoc_modules):
|
|
54
|
+
_run_apidoc_module(
|
|
55
|
+
i,
|
|
56
|
+
options=module_options,
|
|
57
|
+
defaults=defaults,
|
|
58
|
+
srcdir=srcdir,
|
|
59
|
+
confdir=confdir,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _run_apidoc_module(
|
|
64
|
+
i: int,
|
|
65
|
+
*,
|
|
66
|
+
options: dict[str, Any],
|
|
67
|
+
defaults: ApidocDefaults,
|
|
68
|
+
srcdir: Path,
|
|
69
|
+
confdir: Path,
|
|
70
|
+
) -> None:
|
|
71
|
+
"""Run apidoc for a single module."""
|
|
72
|
+
args = _parse_module_options(
|
|
73
|
+
i, options=options, defaults=defaults, srcdir=srcdir, confdir=confdir
|
|
74
|
+
)
|
|
75
|
+
if args is None:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
exclude_patterns_compiled: list[re.Pattern[str]] = [
|
|
79
|
+
re.compile(fnmatch.translate(exclude)) for exclude in args.exclude_pattern
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
written_files, modules = recurse_tree(
|
|
83
|
+
args.module_path, exclude_patterns_compiled, args, args.template_dir
|
|
84
|
+
)
|
|
85
|
+
if args.toc_file:
|
|
86
|
+
written_files.append(
|
|
87
|
+
create_modules_toc_file(modules, args, args.toc_file, args.template_dir)
|
|
88
|
+
)
|
|
89
|
+
if args.remove_old:
|
|
90
|
+
_remove_old_files(written_files, args.dest_dir, args.suffix)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _parse_module_options(
|
|
94
|
+
i: int,
|
|
95
|
+
*,
|
|
96
|
+
options: dict[str, Any],
|
|
97
|
+
defaults: ApidocDefaults,
|
|
98
|
+
srcdir: Path,
|
|
99
|
+
confdir: Path,
|
|
100
|
+
) -> ApidocOptions | None:
|
|
101
|
+
if not isinstance(options, dict):
|
|
102
|
+
LOGGER.warning(__('apidoc_modules item %i must be a dict'), i, type='apidoc')
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
# module path should be absolute or relative to the conf directory
|
|
106
|
+
try:
|
|
107
|
+
path = Path(options['path'])
|
|
108
|
+
except KeyError:
|
|
109
|
+
LOGGER.warning(
|
|
110
|
+
__("apidoc_modules item %i must have a 'path' key"), i, type='apidoc'
|
|
111
|
+
)
|
|
112
|
+
return None
|
|
113
|
+
except TypeError:
|
|
114
|
+
LOGGER.warning(
|
|
115
|
+
__("apidoc_modules item %i 'path' must be a string"), i, type='apidoc'
|
|
116
|
+
)
|
|
117
|
+
return None
|
|
118
|
+
module_path = confdir / path
|
|
119
|
+
if not module_path.is_dir():
|
|
120
|
+
LOGGER.warning(
|
|
121
|
+
__("apidoc_modules item %i 'path' is not an existing folder: %s"),
|
|
122
|
+
i,
|
|
123
|
+
module_path,
|
|
124
|
+
type='apidoc',
|
|
125
|
+
)
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# destination path should be relative to the source directory
|
|
129
|
+
try:
|
|
130
|
+
destination = Path(options['destination'])
|
|
131
|
+
except KeyError:
|
|
132
|
+
LOGGER.warning(
|
|
133
|
+
__("apidoc_modules item %i must have a 'destination' key"),
|
|
134
|
+
i,
|
|
135
|
+
type='apidoc',
|
|
136
|
+
)
|
|
137
|
+
return None
|
|
138
|
+
except TypeError:
|
|
139
|
+
LOGGER.warning(
|
|
140
|
+
__("apidoc_modules item %i 'destination' must be a string"),
|
|
141
|
+
i,
|
|
142
|
+
type='apidoc',
|
|
143
|
+
)
|
|
144
|
+
return None
|
|
145
|
+
if destination.is_absolute():
|
|
146
|
+
LOGGER.warning(
|
|
147
|
+
__("apidoc_modules item %i 'destination' should be a relative path"),
|
|
148
|
+
i,
|
|
149
|
+
type='apidoc',
|
|
150
|
+
)
|
|
151
|
+
return None
|
|
152
|
+
dest_path = srcdir / destination
|
|
153
|
+
try:
|
|
154
|
+
dest_path.mkdir(parents=True, exist_ok=True)
|
|
155
|
+
except OSError as exc:
|
|
156
|
+
LOGGER.warning(
|
|
157
|
+
__('apidoc_modules item %i cannot create destination directory: %s'),
|
|
158
|
+
i,
|
|
159
|
+
exc.strerror,
|
|
160
|
+
type='apidoc',
|
|
161
|
+
)
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
# exclude patterns should be absolute or relative to the conf directory
|
|
165
|
+
exclude_patterns: list[str] = [
|
|
166
|
+
str(confdir / pattern)
|
|
167
|
+
for pattern in _check_collection_of_strings(
|
|
168
|
+
i, options, key='exclude_patterns', default=defaults.exclude_patterns
|
|
169
|
+
)
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
# TODO template_dir
|
|
173
|
+
|
|
174
|
+
max_depth = defaults.max_depth
|
|
175
|
+
if 'max_depth' in options:
|
|
176
|
+
if not isinstance(options['max_depth'], int):
|
|
177
|
+
LOGGER.warning(
|
|
178
|
+
__("apidoc_modules item %i '%s' must be an int"),
|
|
179
|
+
i,
|
|
180
|
+
'max_depth',
|
|
181
|
+
type='apidoc',
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
max_depth = options['max_depth']
|
|
185
|
+
|
|
186
|
+
bool_options: dict[str, bool] = {}
|
|
187
|
+
for key in sorted(_BOOL_KEYS):
|
|
188
|
+
if key not in options:
|
|
189
|
+
bool_options[key] = getattr(defaults, key)
|
|
190
|
+
elif not isinstance(options[key], bool):
|
|
191
|
+
LOGGER.warning(
|
|
192
|
+
__("apidoc_modules item %i '%s' must be a boolean"),
|
|
193
|
+
i,
|
|
194
|
+
key,
|
|
195
|
+
type='apidoc',
|
|
196
|
+
)
|
|
197
|
+
bool_options[key] = getattr(defaults, key)
|
|
198
|
+
else:
|
|
199
|
+
bool_options[key] = options[key]
|
|
200
|
+
|
|
201
|
+
# TODO per-module automodule_options
|
|
202
|
+
automodule_options = frozenset(
|
|
203
|
+
_check_collection_of_strings(
|
|
204
|
+
i, options, key='automodule_options', default=defaults.automodule_options
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if diff := options.keys() - _ALLOWED_KEYS:
|
|
209
|
+
LOGGER.warning(
|
|
210
|
+
__('apidoc_modules item %i has unexpected keys: %s'),
|
|
211
|
+
i,
|
|
212
|
+
', '.join(sorted(diff)),
|
|
213
|
+
type='apidoc',
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
return ApidocOptions(
|
|
217
|
+
dest_dir=dest_path,
|
|
218
|
+
module_path=module_path,
|
|
219
|
+
exclude_pattern=exclude_patterns,
|
|
220
|
+
automodule_options=automodule_options,
|
|
221
|
+
max_depth=max_depth,
|
|
222
|
+
quiet=True,
|
|
223
|
+
follow_links=bool_options['follow_links'],
|
|
224
|
+
separate_modules=bool_options['separate_modules'],
|
|
225
|
+
include_private=bool_options['include_private'],
|
|
226
|
+
no_headings=bool_options['no_headings'],
|
|
227
|
+
module_first=bool_options['module_first'],
|
|
228
|
+
implicit_namespaces=bool_options['implicit_namespaces'],
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _check_collection_of_strings(
|
|
233
|
+
index: int,
|
|
234
|
+
options: dict[str, Any],
|
|
235
|
+
*,
|
|
236
|
+
key: str,
|
|
237
|
+
default: Collection[str],
|
|
238
|
+
) -> Collection[str]:
|
|
239
|
+
"""Check that a key's value is a collection of strings in the options.
|
|
240
|
+
|
|
241
|
+
:returns: The value of the key, or None if invalid.
|
|
242
|
+
"""
|
|
243
|
+
if key not in options:
|
|
244
|
+
return default
|
|
245
|
+
if not isinstance(options[key], list | tuple | set | frozenset):
|
|
246
|
+
LOGGER.warning(
|
|
247
|
+
__("apidoc_modules item %i '%s' must be a sequence"),
|
|
248
|
+
index,
|
|
249
|
+
key,
|
|
250
|
+
type='apidoc',
|
|
251
|
+
)
|
|
252
|
+
return default
|
|
253
|
+
for item in options[key]:
|
|
254
|
+
if not isinstance(item, str):
|
|
255
|
+
LOGGER.warning(
|
|
256
|
+
__("apidoc_modules item %i '%s' must contain strings"),
|
|
257
|
+
index,
|
|
258
|
+
key,
|
|
259
|
+
type='apidoc',
|
|
260
|
+
)
|
|
261
|
+
return default
|
|
262
|
+
return options[key]
|