Sphinx 8.1.2__py3-none-any.whl → 8.2.0rc1__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 +8 -4
- sphinx/__main__.py +2 -0
- sphinx/_cli/__init__.py +2 -5
- sphinx/_cli/util/colour.py +34 -11
- sphinx/_cli/util/errors.py +128 -61
- sphinx/addnodes.py +51 -35
- sphinx/application.py +362 -230
- sphinx/builders/__init__.py +87 -64
- sphinx/builders/_epub_base.py +65 -56
- sphinx/builders/changes.py +17 -23
- sphinx/builders/dirhtml.py +8 -13
- sphinx/builders/epub3.py +70 -38
- sphinx/builders/gettext.py +93 -73
- sphinx/builders/html/__init__.py +240 -186
- sphinx/builders/html/_assets.py +9 -2
- sphinx/builders/html/_build_info.py +3 -0
- sphinx/builders/latex/__init__.py +64 -54
- sphinx/builders/latex/constants.py +14 -11
- sphinx/builders/latex/nodes.py +2 -0
- sphinx/builders/latex/theming.py +8 -9
- sphinx/builders/latex/transforms.py +7 -5
- sphinx/builders/linkcheck.py +193 -149
- sphinx/builders/manpage.py +17 -17
- sphinx/builders/singlehtml.py +28 -16
- sphinx/builders/texinfo.py +28 -21
- sphinx/builders/text.py +10 -15
- sphinx/builders/xml.py +10 -19
- sphinx/cmd/build.py +49 -119
- sphinx/cmd/make_mode.py +35 -31
- sphinx/cmd/quickstart.py +78 -62
- sphinx/config.py +265 -163
- sphinx/directives/__init__.py +51 -54
- sphinx/directives/admonitions.py +107 -0
- sphinx/directives/code.py +24 -19
- sphinx/directives/other.py +21 -42
- sphinx/directives/patches.py +28 -16
- sphinx/domains/__init__.py +54 -31
- sphinx/domains/_domains_container.py +22 -17
- sphinx/domains/_index.py +5 -8
- sphinx/domains/c/__init__.py +366 -245
- sphinx/domains/c/_ast.py +378 -256
- sphinx/domains/c/_ids.py +89 -31
- sphinx/domains/c/_parser.py +283 -214
- sphinx/domains/c/_symbol.py +269 -198
- sphinx/domains/changeset.py +39 -24
- sphinx/domains/citation.py +54 -24
- sphinx/domains/cpp/__init__.py +517 -362
- sphinx/domains/cpp/_ast.py +999 -682
- sphinx/domains/cpp/_ids.py +133 -65
- sphinx/domains/cpp/_parser.py +746 -588
- sphinx/domains/cpp/_symbol.py +692 -489
- sphinx/domains/index.py +10 -8
- sphinx/domains/javascript.py +152 -74
- sphinx/domains/math.py +48 -40
- sphinx/domains/python/__init__.py +402 -211
- sphinx/domains/python/_annotations.py +114 -57
- sphinx/domains/python/_object.py +151 -67
- sphinx/domains/rst.py +94 -49
- sphinx/domains/std/__init__.py +510 -249
- sphinx/environment/__init__.py +345 -61
- sphinx/environment/adapters/asset.py +7 -1
- sphinx/environment/adapters/indexentries.py +15 -20
- sphinx/environment/adapters/toctree.py +19 -9
- sphinx/environment/collectors/__init__.py +3 -1
- sphinx/environment/collectors/asset.py +18 -15
- sphinx/environment/collectors/dependencies.py +8 -10
- sphinx/environment/collectors/metadata.py +6 -4
- sphinx/environment/collectors/title.py +3 -1
- sphinx/environment/collectors/toctree.py +4 -4
- sphinx/errors.py +1 -3
- sphinx/events.py +4 -4
- sphinx/ext/apidoc/__init__.py +21 -0
- sphinx/ext/apidoc/__main__.py +9 -0
- sphinx/ext/apidoc/_cli.py +356 -0
- sphinx/ext/apidoc/_generate.py +356 -0
- sphinx/ext/apidoc/_shared.py +66 -0
- sphinx/ext/autodoc/__init__.py +837 -483
- sphinx/ext/autodoc/directive.py +57 -21
- sphinx/ext/autodoc/importer.py +184 -67
- sphinx/ext/autodoc/mock.py +25 -10
- sphinx/ext/autodoc/preserve_defaults.py +17 -9
- sphinx/ext/autodoc/type_comment.py +56 -29
- sphinx/ext/autodoc/typehints.py +49 -26
- sphinx/ext/autosectionlabel.py +28 -11
- sphinx/ext/autosummary/__init__.py +271 -143
- sphinx/ext/autosummary/generate.py +121 -51
- sphinx/ext/coverage.py +152 -91
- sphinx/ext/doctest.py +169 -101
- sphinx/ext/duration.py +12 -6
- sphinx/ext/extlinks.py +33 -21
- sphinx/ext/githubpages.py +8 -8
- sphinx/ext/graphviz.py +175 -109
- sphinx/ext/ifconfig.py +11 -6
- sphinx/ext/imgconverter.py +48 -25
- sphinx/ext/imgmath.py +127 -97
- sphinx/ext/inheritance_diagram.py +177 -103
- sphinx/ext/intersphinx/__init__.py +22 -13
- sphinx/ext/intersphinx/__main__.py +3 -1
- sphinx/ext/intersphinx/_cli.py +18 -14
- sphinx/ext/intersphinx/_load.py +91 -82
- sphinx/ext/intersphinx/_resolve.py +108 -74
- sphinx/ext/intersphinx/_shared.py +2 -2
- sphinx/ext/linkcode.py +28 -12
- sphinx/ext/mathjax.py +60 -29
- sphinx/ext/napoleon/__init__.py +19 -7
- sphinx/ext/napoleon/docstring.py +229 -231
- sphinx/ext/todo.py +44 -49
- sphinx/ext/viewcode.py +105 -57
- sphinx/extension.py +3 -1
- sphinx/highlighting.py +13 -7
- sphinx/io.py +9 -13
- sphinx/jinja2glue.py +29 -26
- sphinx/locale/__init__.py +8 -9
- sphinx/parsers.py +8 -7
- sphinx/project.py +2 -2
- sphinx/pycode/__init__.py +31 -21
- sphinx/pycode/ast.py +6 -3
- sphinx/pycode/parser.py +14 -8
- sphinx/pygments_styles.py +4 -5
- sphinx/registry.py +192 -92
- sphinx/roles.py +58 -7
- sphinx/search/__init__.py +75 -54
- sphinx/search/en.py +11 -13
- sphinx/search/fi.py +1 -1
- sphinx/search/ja.py +8 -6
- sphinx/search/nl.py +1 -1
- sphinx/search/zh.py +19 -21
- sphinx/testing/fixtures.py +26 -29
- sphinx/testing/path.py +26 -62
- sphinx/testing/restructuredtext.py +14 -8
- sphinx/testing/util.py +21 -19
- sphinx/texinputs/make.bat.jinja +50 -50
- sphinx/texinputs/sphinx.sty +4 -3
- sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
- sphinx/texinputs/sphinxlatexobjects.sty +29 -10
- sphinx/themes/basic/static/searchtools.js +8 -5
- sphinx/theming.py +49 -61
- sphinx/transforms/__init__.py +17 -38
- sphinx/transforms/compact_bullet_list.py +5 -3
- sphinx/transforms/i18n.py +8 -21
- sphinx/transforms/post_transforms/__init__.py +142 -93
- sphinx/transforms/post_transforms/code.py +5 -5
- sphinx/transforms/post_transforms/images.py +28 -24
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +109 -60
- sphinx/util/_files.py +39 -23
- sphinx/util/_importer.py +4 -1
- sphinx/util/_inventory_file_reader.py +76 -0
- sphinx/util/_io.py +2 -2
- sphinx/util/_lines.py +6 -3
- sphinx/util/_pathlib.py +40 -2
- sphinx/util/build_phase.py +2 -0
- sphinx/util/cfamily.py +19 -14
- sphinx/util/console.py +44 -179
- sphinx/util/display.py +9 -10
- sphinx/util/docfields.py +140 -122
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -77
- sphinx/util/fileutil.py +25 -26
- sphinx/util/http_date.py +2 -0
- sphinx/util/i18n.py +77 -64
- sphinx/util/images.py +8 -6
- sphinx/util/inspect.py +147 -38
- sphinx/util/inventory.py +215 -116
- sphinx/util/logging.py +33 -33
- sphinx/util/matching.py +12 -4
- sphinx/util/nodes.py +18 -13
- sphinx/util/osutil.py +38 -39
- sphinx/util/parallel.py +22 -13
- sphinx/util/parsing.py +2 -1
- sphinx/util/png.py +6 -2
- sphinx/util/requests.py +33 -2
- sphinx/util/rst.py +3 -2
- sphinx/util/tags.py +1 -1
- sphinx/util/template.py +18 -10
- sphinx/util/texescape.py +8 -6
- sphinx/util/typing.py +148 -122
- sphinx/versioning.py +3 -3
- sphinx/writers/html.py +3 -1
- sphinx/writers/html5.py +61 -50
- sphinx/writers/latex.py +80 -65
- sphinx/writers/manpage.py +19 -38
- sphinx/writers/texinfo.py +44 -45
- sphinx/writers/text.py +48 -30
- sphinx/writers/xml.py +11 -8
- {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/METADATA +23 -15
- {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/RECORD +190 -186
- {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/WHEEL +1 -1
- sphinx/builders/html/transforms.py +0 -90
- sphinx/ext/apidoc.py +0 -721
- sphinx/util/exceptions.py +0 -74
- {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/entry_points.txt +0 -0
sphinx/registry.py
CHANGED
|
@@ -6,9 +6,9 @@ import traceback
|
|
|
6
6
|
from importlib import import_module
|
|
7
7
|
from importlib.metadata import entry_points
|
|
8
8
|
from types import MethodType
|
|
9
|
-
from typing import TYPE_CHECKING
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
10
|
|
|
11
|
-
from sphinx.domains import
|
|
11
|
+
from sphinx.domains import ObjType
|
|
12
12
|
from sphinx.domains.std import GenericObject, Target
|
|
13
13
|
from sphinx.errors import ExtensionError, SphinxError, VersionRequirementError
|
|
14
14
|
from sphinx.extension import Extension
|
|
@@ -17,10 +17,13 @@ from sphinx.locale import __
|
|
|
17
17
|
from sphinx.parsers import Parser as SphinxParser
|
|
18
18
|
from sphinx.roles import XRefRole
|
|
19
19
|
from sphinx.util import logging
|
|
20
|
+
from sphinx.util._pathlib import _StrPath
|
|
20
21
|
from sphinx.util.logging import prefixed_warnings
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
23
|
-
|
|
24
|
+
import os
|
|
25
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
26
|
+
from typing import Any, TypeAlias
|
|
24
27
|
|
|
25
28
|
from docutils import nodes
|
|
26
29
|
from docutils.core import Publisher
|
|
@@ -29,26 +32,43 @@ if TYPE_CHECKING:
|
|
|
29
32
|
from docutils.parsers.rst import Directive
|
|
30
33
|
from docutils.transforms import Transform
|
|
31
34
|
|
|
35
|
+
from sphinx import addnodes
|
|
32
36
|
from sphinx.application import Sphinx
|
|
33
37
|
from sphinx.builders import Builder
|
|
34
38
|
from sphinx.config import Config
|
|
39
|
+
from sphinx.domains import Domain, Index
|
|
35
40
|
from sphinx.environment import BuildEnvironment
|
|
36
41
|
from sphinx.ext.autodoc import Documenter
|
|
42
|
+
from sphinx.util.docfields import Field
|
|
37
43
|
from sphinx.util.typing import (
|
|
38
44
|
ExtensionMetadata,
|
|
39
45
|
RoleFunction,
|
|
40
46
|
TitleGetter,
|
|
41
47
|
_ExtensionSetupFunc,
|
|
42
48
|
)
|
|
49
|
+
from sphinx.writers.html5 import HTML5Translator
|
|
50
|
+
|
|
51
|
+
# visit/depart function
|
|
52
|
+
# the parameters should be (SphinxTranslator, Element)
|
|
53
|
+
# or any subtype of either, but mypy rejects this.
|
|
54
|
+
_NodeHandler: TypeAlias = Callable[[Any, Any], None]
|
|
55
|
+
_NodeHandlerPair: TypeAlias = tuple[_NodeHandler, _NodeHandler | None]
|
|
56
|
+
|
|
57
|
+
_MathsRenderer: TypeAlias = Callable[[HTML5Translator, nodes.math], None]
|
|
58
|
+
_MathsBlockRenderer: TypeAlias = Callable[[HTML5Translator, nodes.math_block], None]
|
|
59
|
+
_MathsInlineRenderers: TypeAlias = tuple[_MathsRenderer, _MathsRenderer | None]
|
|
60
|
+
_MathsBlockRenderers: TypeAlias = tuple[
|
|
61
|
+
_MathsBlockRenderer, _MathsBlockRenderer | None
|
|
62
|
+
]
|
|
43
63
|
|
|
44
64
|
logger = logging.getLogger(__name__)
|
|
45
65
|
|
|
46
66
|
# list of deprecated extensions. Keys are extension name.
|
|
47
67
|
# Values are Sphinx version that merge the extension.
|
|
48
68
|
EXTENSION_BLACKLIST = {
|
|
49
|
-
|
|
69
|
+
'sphinxjp.themecore': '1.2',
|
|
50
70
|
'sphinxcontrib-napoleon': '1.3',
|
|
51
|
-
|
|
71
|
+
'sphinxprettysearchresults': '2.0.0',
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
|
|
@@ -91,16 +111,20 @@ class SphinxComponentRegistry:
|
|
|
91
111
|
|
|
92
112
|
#: HTML inline and block math renderers
|
|
93
113
|
#: a dict of name -> tuple of visit function and depart function
|
|
94
|
-
self.html_inline_math_renderers: dict[
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
114
|
+
self.html_inline_math_renderers: dict[
|
|
115
|
+
str,
|
|
116
|
+
_MathsInlineRenderers,
|
|
117
|
+
] = {}
|
|
118
|
+
self.html_block_math_renderers: dict[
|
|
119
|
+
str,
|
|
120
|
+
_MathsBlockRenderers,
|
|
121
|
+
] = {}
|
|
98
122
|
|
|
99
123
|
#: HTML assets
|
|
100
124
|
self.html_assets_policy: str = 'per_page'
|
|
101
125
|
|
|
102
126
|
#: HTML themes
|
|
103
|
-
self.html_themes: dict[str,
|
|
127
|
+
self.html_themes: dict[str, _StrPath] = {}
|
|
104
128
|
|
|
105
129
|
#: js_files; list of JS paths or URLs
|
|
106
130
|
self.js_files: list[tuple[str | None, dict[str, Any]]] = []
|
|
@@ -124,7 +148,7 @@ class SphinxComponentRegistry:
|
|
|
124
148
|
|
|
125
149
|
#: custom handlers for translators
|
|
126
150
|
#: a dict of builder name -> dict of node name -> visitor and departure functions
|
|
127
|
-
self.translation_handlers: dict[str, dict[str,
|
|
151
|
+
self.translation_handlers: dict[str, dict[str, _NodeHandlerPair]] = {}
|
|
128
152
|
|
|
129
153
|
#: additional transforms; list of transforms
|
|
130
154
|
self.transforms: list[type[Transform]] = []
|
|
@@ -139,10 +163,14 @@ class SphinxComponentRegistry:
|
|
|
139
163
|
def add_builder(self, builder: type[Builder], override: bool = False) -> None:
|
|
140
164
|
logger.debug('[app] adding builder: %r', builder)
|
|
141
165
|
if not hasattr(builder, 'name'):
|
|
142
|
-
raise ExtensionError(
|
|
166
|
+
raise ExtensionError(
|
|
167
|
+
__('Builder class %s has no "name" attribute') % builder
|
|
168
|
+
)
|
|
143
169
|
if builder.name in self.builders and not override:
|
|
144
|
-
raise ExtensionError(
|
|
145
|
-
|
|
170
|
+
raise ExtensionError(
|
|
171
|
+
__('Builder %r already exists (in module %s)')
|
|
172
|
+
% (builder.name, self.builders[builder.name].__module__)
|
|
173
|
+
)
|
|
146
174
|
self.builders[builder.name] = builder
|
|
147
175
|
|
|
148
176
|
def preload_builder(self, app: Sphinx, name: str) -> None:
|
|
@@ -154,8 +182,13 @@ class SphinxComponentRegistry:
|
|
|
154
182
|
try:
|
|
155
183
|
entry_point = builder_entry_points[name]
|
|
156
184
|
except KeyError as exc:
|
|
157
|
-
raise SphinxError(
|
|
158
|
-
|
|
185
|
+
raise SphinxError(
|
|
186
|
+
__(
|
|
187
|
+
'Builder name %s not registered or available'
|
|
188
|
+
' through entry point'
|
|
189
|
+
)
|
|
190
|
+
% name
|
|
191
|
+
) from exc
|
|
159
192
|
|
|
160
193
|
self.load_extension(app, entry_point.module)
|
|
161
194
|
|
|
@@ -187,39 +220,52 @@ class SphinxComponentRegistry:
|
|
|
187
220
|
|
|
188
221
|
yield domain
|
|
189
222
|
|
|
190
|
-
def add_directive_to_domain(
|
|
191
|
-
|
|
223
|
+
def add_directive_to_domain(
|
|
224
|
+
self, domain: str, name: str, cls: type[Directive], override: bool = False
|
|
225
|
+
) -> None:
|
|
192
226
|
logger.debug('[app] adding directive to domain: %r', (domain, name, cls))
|
|
193
227
|
if domain not in self.domains:
|
|
194
228
|
raise ExtensionError(__('domain %s not yet registered') % domain)
|
|
195
229
|
|
|
196
|
-
directives: dict[str, type[Directive]] = self.domain_directives.setdefault(
|
|
230
|
+
directives: dict[str, type[Directive]] = self.domain_directives.setdefault(
|
|
231
|
+
domain, {}
|
|
232
|
+
)
|
|
197
233
|
if name in directives and not override:
|
|
198
|
-
raise ExtensionError(
|
|
199
|
-
|
|
234
|
+
raise ExtensionError(
|
|
235
|
+
__('The %r directive is already registered to domain %s')
|
|
236
|
+
% (name, domain)
|
|
237
|
+
)
|
|
200
238
|
directives[name] = cls
|
|
201
239
|
|
|
202
|
-
def add_role_to_domain(
|
|
203
|
-
|
|
204
|
-
|
|
240
|
+
def add_role_to_domain(
|
|
241
|
+
self,
|
|
242
|
+
domain: str,
|
|
243
|
+
name: str,
|
|
244
|
+
role: RoleFunction | XRefRole,
|
|
245
|
+
override: bool = False,
|
|
246
|
+
) -> None:
|
|
205
247
|
logger.debug('[app] adding role to domain: %r', (domain, name, role))
|
|
206
248
|
if domain not in self.domains:
|
|
207
249
|
raise ExtensionError(__('domain %s not yet registered') % domain)
|
|
208
250
|
roles = self.domain_roles.setdefault(domain, {})
|
|
209
251
|
if name in roles and not override:
|
|
210
|
-
raise ExtensionError(
|
|
211
|
-
|
|
252
|
+
raise ExtensionError(
|
|
253
|
+
__('The %r role is already registered to domain %s') % (name, domain)
|
|
254
|
+
)
|
|
212
255
|
roles[name] = role
|
|
213
256
|
|
|
214
|
-
def add_index_to_domain(
|
|
215
|
-
|
|
257
|
+
def add_index_to_domain(
|
|
258
|
+
self, domain: str, index: type[Index], override: bool = False
|
|
259
|
+
) -> None:
|
|
216
260
|
logger.debug('[app] adding index to domain: %r', (domain, index))
|
|
217
261
|
if domain not in self.domains:
|
|
218
262
|
raise ExtensionError(__('domain %s not yet registered') % domain)
|
|
219
263
|
indices = self.domain_indices.setdefault(domain, [])
|
|
220
264
|
if index in indices and not override:
|
|
221
|
-
raise ExtensionError(
|
|
222
|
-
|
|
265
|
+
raise ExtensionError(
|
|
266
|
+
__('The %r index is already registered to domain %s')
|
|
267
|
+
% (index.name, domain)
|
|
268
|
+
)
|
|
223
269
|
indices.append(index)
|
|
224
270
|
|
|
225
271
|
def add_object_type(
|
|
@@ -227,30 +273,45 @@ class SphinxComponentRegistry:
|
|
|
227
273
|
directivename: str,
|
|
228
274
|
rolename: str,
|
|
229
275
|
indextemplate: str = '',
|
|
230
|
-
parse_node: Callable
|
|
276
|
+
parse_node: Callable[[BuildEnvironment, str, addnodes.desc_signature], str]
|
|
277
|
+
| None = None,
|
|
231
278
|
ref_nodeclass: type[TextElement] | None = None,
|
|
232
279
|
objname: str = '',
|
|
233
|
-
doc_field_types: Sequence = (),
|
|
280
|
+
doc_field_types: Sequence[Field] = (),
|
|
234
281
|
override: bool = False,
|
|
235
282
|
) -> None:
|
|
236
|
-
logger.debug(
|
|
237
|
-
|
|
238
|
-
|
|
283
|
+
logger.debug(
|
|
284
|
+
'[app] adding object type: %r',
|
|
285
|
+
(
|
|
286
|
+
directivename,
|
|
287
|
+
rolename,
|
|
288
|
+
indextemplate,
|
|
289
|
+
parse_node,
|
|
290
|
+
ref_nodeclass,
|
|
291
|
+
objname,
|
|
292
|
+
doc_field_types,
|
|
293
|
+
),
|
|
294
|
+
)
|
|
239
295
|
|
|
240
296
|
# create a subclass of GenericObject as the new directive
|
|
241
|
-
directive = type(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
297
|
+
directive = type(
|
|
298
|
+
directivename,
|
|
299
|
+
(GenericObject, object),
|
|
300
|
+
{
|
|
301
|
+
'indextemplate': indextemplate,
|
|
302
|
+
'parse_node': parse_node and staticmethod(parse_node),
|
|
303
|
+
'doc_field_types': doc_field_types,
|
|
304
|
+
},
|
|
305
|
+
)
|
|
246
306
|
|
|
247
307
|
self.add_directive_to_domain('std', directivename, directive)
|
|
248
308
|
self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
|
|
249
309
|
|
|
250
310
|
object_types = self.domain_object_types.setdefault('std', {})
|
|
251
311
|
if directivename in object_types and not override:
|
|
252
|
-
raise ExtensionError(
|
|
253
|
-
|
|
312
|
+
raise ExtensionError(
|
|
313
|
+
__('The %r object_type is already registered') % directivename
|
|
314
|
+
)
|
|
254
315
|
object_types[directivename] = ObjType(objname or directivename, rolename)
|
|
255
316
|
|
|
256
317
|
def add_crossref_type(
|
|
@@ -262,24 +323,31 @@ class SphinxComponentRegistry:
|
|
|
262
323
|
objname: str = '',
|
|
263
324
|
override: bool = False,
|
|
264
325
|
) -> None:
|
|
265
|
-
logger.debug(
|
|
266
|
-
|
|
326
|
+
logger.debug(
|
|
327
|
+
'[app] adding crossref type: %r',
|
|
328
|
+
(directivename, rolename, indextemplate, ref_nodeclass, objname),
|
|
329
|
+
)
|
|
267
330
|
|
|
268
331
|
# create a subclass of Target as the new directive
|
|
269
|
-
directive = type(
|
|
270
|
-
|
|
271
|
-
|
|
332
|
+
directive = type(
|
|
333
|
+
directivename,
|
|
334
|
+
(Target, object),
|
|
335
|
+
{'indextemplate': indextemplate},
|
|
336
|
+
)
|
|
272
337
|
|
|
273
338
|
self.add_directive_to_domain('std', directivename, directive)
|
|
274
339
|
self.add_role_to_domain('std', rolename, XRefRole(innernodeclass=ref_nodeclass))
|
|
275
340
|
|
|
276
341
|
object_types = self.domain_object_types.setdefault('std', {})
|
|
277
342
|
if directivename in object_types and not override:
|
|
278
|
-
raise ExtensionError(
|
|
279
|
-
|
|
343
|
+
raise ExtensionError(
|
|
344
|
+
__('The %r crossref_type is already registered') % directivename
|
|
345
|
+
)
|
|
280
346
|
object_types[directivename] = ObjType(objname or directivename, rolename)
|
|
281
347
|
|
|
282
|
-
def add_source_suffix(
|
|
348
|
+
def add_source_suffix(
|
|
349
|
+
self, suffix: str, filetype: str, override: bool = False
|
|
350
|
+
) -> None:
|
|
283
351
|
logger.debug('[app] adding source_suffix: %r, %r', suffix, filetype)
|
|
284
352
|
if suffix in self.source_suffix and not override:
|
|
285
353
|
raise ExtensionError(__('source_suffix %r is already registered') % suffix)
|
|
@@ -291,15 +359,18 @@ class SphinxComponentRegistry:
|
|
|
291
359
|
# create a map from filetype to parser
|
|
292
360
|
for filetype in parser.supported:
|
|
293
361
|
if filetype in self.source_parsers and not override:
|
|
294
|
-
raise ExtensionError(
|
|
295
|
-
|
|
362
|
+
raise ExtensionError(
|
|
363
|
+
__('source_parser for %r is already registered') % filetype
|
|
364
|
+
)
|
|
296
365
|
self.source_parsers[filetype] = parser
|
|
297
366
|
|
|
298
367
|
def get_source_parser(self, filetype: str) -> type[Parser]:
|
|
299
368
|
try:
|
|
300
369
|
return self.source_parsers[filetype]
|
|
301
370
|
except KeyError as exc:
|
|
302
|
-
raise SphinxError(
|
|
371
|
+
raise SphinxError(
|
|
372
|
+
__('Source parser for %s not registered') % filetype
|
|
373
|
+
) from exc
|
|
303
374
|
|
|
304
375
|
def get_source_parsers(self) -> dict[str, type[Parser]]:
|
|
305
376
|
return self.source_parsers
|
|
@@ -311,28 +382,32 @@ class SphinxComponentRegistry:
|
|
|
311
382
|
parser.set_application(app)
|
|
312
383
|
return parser
|
|
313
384
|
|
|
314
|
-
def add_translator(
|
|
315
|
-
|
|
385
|
+
def add_translator(
|
|
386
|
+
self, name: str, translator: type[nodes.NodeVisitor], override: bool = False
|
|
387
|
+
) -> None:
|
|
316
388
|
logger.debug('[app] Change of translator for the %s builder.', name)
|
|
317
389
|
if name in self.translators and not override:
|
|
318
390
|
raise ExtensionError(__('Translator for %r already exists') % name)
|
|
319
391
|
self.translators[name] = translator
|
|
320
392
|
|
|
321
393
|
def add_translation_handlers(
|
|
322
|
-
self,
|
|
323
|
-
node: type[Element],
|
|
324
|
-
**kwargs: tuple[Callable, Callable | None],
|
|
394
|
+
self, node: type[Element], **kwargs: _NodeHandlerPair
|
|
325
395
|
) -> None:
|
|
326
396
|
logger.debug('[app] adding translation_handlers: %r, %r', node, kwargs)
|
|
327
397
|
for builder_name, handlers in kwargs.items():
|
|
328
|
-
translation_handlers = self.translation_handlers.setdefault(
|
|
398
|
+
translation_handlers = self.translation_handlers.setdefault(
|
|
399
|
+
builder_name, {}
|
|
400
|
+
)
|
|
329
401
|
try:
|
|
330
402
|
visit, depart = handlers # unpack once for assertion
|
|
331
403
|
translation_handlers[node.__name__] = (visit, depart)
|
|
332
404
|
except ValueError as exc:
|
|
333
405
|
raise ExtensionError(
|
|
334
|
-
__(
|
|
335
|
-
|
|
406
|
+
__(
|
|
407
|
+
'kwargs for add_node() must be a (visit, depart) '
|
|
408
|
+
'function tuple: %r=%r'
|
|
409
|
+
)
|
|
410
|
+
% (builder_name, handlers),
|
|
336
411
|
) from exc
|
|
337
412
|
|
|
338
413
|
def get_translator_class(self, builder: Builder) -> type[nodes.NodeVisitor]:
|
|
@@ -379,8 +454,9 @@ class SphinxComponentRegistry:
|
|
|
379
454
|
def add_documenter(self, objtype: str, documenter: type[Documenter]) -> None:
|
|
380
455
|
self.documenters[objtype] = documenter
|
|
381
456
|
|
|
382
|
-
def add_autodoc_attrgetter(
|
|
383
|
-
|
|
457
|
+
def add_autodoc_attrgetter(
|
|
458
|
+
self, typ: type, attrgetter: Callable[[Any, str, Any], Any]
|
|
459
|
+
) -> None:
|
|
384
460
|
self.autodoc_attrgetters[typ] = attrgetter
|
|
385
461
|
|
|
386
462
|
def add_css_files(self, filename: str, **attributes: Any) -> None:
|
|
@@ -395,7 +471,7 @@ class SphinxComponentRegistry:
|
|
|
395
471
|
return bool([x for x in packages if x[0] == name])
|
|
396
472
|
|
|
397
473
|
def add_latex_package(
|
|
398
|
-
self, name: str, options: str | None, after_hyperref: bool = False
|
|
474
|
+
self, name: str, options: str | None, after_hyperref: bool = False
|
|
399
475
|
) -> None:
|
|
400
476
|
if self.has_latex_package(name):
|
|
401
477
|
logger.warning("latex package '%s' already included", name)
|
|
@@ -410,9 +486,12 @@ class SphinxComponentRegistry:
|
|
|
410
486
|
self,
|
|
411
487
|
node: type[Node],
|
|
412
488
|
figtype: str,
|
|
413
|
-
title_getter: TitleGetter | None = None,
|
|
489
|
+
title_getter: TitleGetter | None = None,
|
|
490
|
+
override: bool = False,
|
|
414
491
|
) -> None:
|
|
415
|
-
logger.debug(
|
|
492
|
+
logger.debug(
|
|
493
|
+
'[app] adding enumerable node: (%r, %r, %r)', node, figtype, title_getter
|
|
494
|
+
)
|
|
416
495
|
if node in self.enumerable_nodes and not override:
|
|
417
496
|
raise ExtensionError(__('enumerable_node %r already registered') % node)
|
|
418
497
|
self.enumerable_nodes[node] = (figtype, title_getter)
|
|
@@ -420,11 +499,15 @@ class SphinxComponentRegistry:
|
|
|
420
499
|
def add_html_math_renderer(
|
|
421
500
|
self,
|
|
422
501
|
name: str,
|
|
423
|
-
inline_renderers:
|
|
424
|
-
block_renderers:
|
|
502
|
+
inline_renderers: _MathsInlineRenderers | None,
|
|
503
|
+
block_renderers: _MathsBlockRenderers | None,
|
|
425
504
|
) -> None:
|
|
426
|
-
logger.debug(
|
|
427
|
-
|
|
505
|
+
logger.debug(
|
|
506
|
+
'[app] adding html_math_renderer: %s, %r, %r',
|
|
507
|
+
name,
|
|
508
|
+
inline_renderers,
|
|
509
|
+
block_renderers,
|
|
510
|
+
)
|
|
428
511
|
if name in self.html_inline_math_renderers:
|
|
429
512
|
raise ExtensionError(__('math renderer %s is already registered') % name)
|
|
430
513
|
|
|
@@ -433,17 +516,22 @@ class SphinxComponentRegistry:
|
|
|
433
516
|
if block_renderers is not None:
|
|
434
517
|
self.html_block_math_renderers[name] = block_renderers
|
|
435
518
|
|
|
436
|
-
def add_html_theme(self, name: str, theme_path: str) -> None:
|
|
437
|
-
self.html_themes[name] = theme_path
|
|
519
|
+
def add_html_theme(self, name: str, theme_path: str | os.PathLike[str]) -> None:
|
|
520
|
+
self.html_themes[name] = _StrPath(theme_path)
|
|
438
521
|
|
|
439
522
|
def load_extension(self, app: Sphinx, extname: str) -> None:
|
|
440
523
|
"""Load a Sphinx extension."""
|
|
441
524
|
if extname in app.extensions: # already loaded
|
|
442
525
|
return
|
|
443
526
|
if extname in EXTENSION_BLACKLIST:
|
|
444
|
-
logger.warning(
|
|
445
|
-
|
|
446
|
-
|
|
527
|
+
logger.warning(
|
|
528
|
+
__(
|
|
529
|
+
'the extension %r was already merged with Sphinx since '
|
|
530
|
+
'version %s; this extension is ignored.'
|
|
531
|
+
),
|
|
532
|
+
extname,
|
|
533
|
+
EXTENSION_BLACKLIST[extname],
|
|
534
|
+
)
|
|
447
535
|
return
|
|
448
536
|
|
|
449
537
|
# update loading context
|
|
@@ -453,13 +541,19 @@ class SphinxComponentRegistry:
|
|
|
453
541
|
mod = import_module(extname)
|
|
454
542
|
except ImportError as err:
|
|
455
543
|
logger.verbose(__('Original exception:\n') + traceback.format_exc())
|
|
456
|
-
raise ExtensionError(
|
|
457
|
-
|
|
544
|
+
raise ExtensionError(
|
|
545
|
+
__('Could not import extension %s') % extname, err
|
|
546
|
+
) from err
|
|
458
547
|
|
|
459
548
|
setup: _ExtensionSetupFunc | None = getattr(mod, 'setup', None)
|
|
460
549
|
if setup is None:
|
|
461
|
-
logger.warning(
|
|
462
|
-
|
|
550
|
+
logger.warning(
|
|
551
|
+
__(
|
|
552
|
+
'extension %r has no setup() function; is it really '
|
|
553
|
+
'a Sphinx extension module?'
|
|
554
|
+
),
|
|
555
|
+
extname,
|
|
556
|
+
)
|
|
463
557
|
metadata: ExtensionMetadata = {}
|
|
464
558
|
else:
|
|
465
559
|
try:
|
|
@@ -467,27 +561,33 @@ class SphinxComponentRegistry:
|
|
|
467
561
|
except VersionRequirementError as err:
|
|
468
562
|
# add the extension name to the version required
|
|
469
563
|
raise VersionRequirementError(
|
|
470
|
-
__(
|
|
471
|
-
|
|
472
|
-
|
|
564
|
+
__(
|
|
565
|
+
'The %s extension used by this project needs at least '
|
|
566
|
+
'Sphinx v%s; it therefore cannot be built with this '
|
|
567
|
+
'version.'
|
|
568
|
+
)
|
|
569
|
+
% (extname, err),
|
|
473
570
|
) from err
|
|
474
571
|
|
|
475
572
|
if metadata is None:
|
|
476
573
|
metadata = {}
|
|
477
574
|
elif not isinstance(metadata, dict):
|
|
478
|
-
logger.warning(
|
|
479
|
-
|
|
480
|
-
|
|
575
|
+
logger.warning(
|
|
576
|
+
__(
|
|
577
|
+
'extension %r returned an unsupported object from '
|
|
578
|
+
'its setup() function; it should return None or a '
|
|
579
|
+
'metadata dictionary'
|
|
580
|
+
),
|
|
581
|
+
extname,
|
|
582
|
+
)
|
|
481
583
|
metadata = {}
|
|
482
584
|
|
|
483
585
|
app.extensions[extname] = Extension(extname, mod, **metadata)
|
|
484
586
|
|
|
485
|
-
def get_envversion(self, app: Sphinx) ->
|
|
486
|
-
from sphinx.environment import
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
envversion['sphinx'] = ENV_VERSION
|
|
490
|
-
return envversion
|
|
587
|
+
def get_envversion(self, app: Sphinx) -> Mapping[str, int]:
|
|
588
|
+
from sphinx.environment import _get_env_version
|
|
589
|
+
|
|
590
|
+
return _get_env_version(app.extensions)
|
|
491
591
|
|
|
492
592
|
def get_publisher(self, app: Sphinx, filetype: str) -> Publisher:
|
|
493
593
|
try:
|
|
@@ -502,7 +602,7 @@ class SphinxComponentRegistry:
|
|
|
502
602
|
def merge_source_suffix(app: Sphinx, config: Config) -> None:
|
|
503
603
|
"""Merge any user-specified source_suffix with any added by extensions."""
|
|
504
604
|
for suffix, filetype in app.registry.source_suffix.items():
|
|
505
|
-
if suffix not in app.config.source_suffix:
|
|
605
|
+
if suffix not in app.config.source_suffix:
|
|
506
606
|
app.config.source_suffix[suffix] = filetype
|
|
507
607
|
elif app.config.source_suffix[suffix] == 'restructuredtext':
|
|
508
608
|
# The filetype is not specified (default filetype).
|
sphinx/roles.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import docutils.parsers.rst.directives
|
|
9
9
|
import docutils.parsers.rst.roles
|
|
@@ -17,7 +17,7 @@ from sphinx.util.docutils import ReferenceRole, SphinxRole
|
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from collections.abc import Sequence
|
|
20
|
-
from typing import Final
|
|
20
|
+
from typing import Any, Final
|
|
21
21
|
|
|
22
22
|
from docutils.nodes import Element, Node, TextElement, system_message
|
|
23
23
|
|
|
@@ -29,7 +29,6 @@ if TYPE_CHECKING:
|
|
|
29
29
|
generic_docroles = {
|
|
30
30
|
'command': addnodes.literal_strong,
|
|
31
31
|
'dfn': nodes.emphasis,
|
|
32
|
-
'kbd': nodes.literal,
|
|
33
32
|
'mailheader': addnodes.literal_emphasis,
|
|
34
33
|
'makevar': addnodes.literal_strong,
|
|
35
34
|
'mimetype': addnodes.literal_emphasis,
|
|
@@ -43,8 +42,7 @@ generic_docroles = {
|
|
|
43
42
|
|
|
44
43
|
|
|
45
44
|
class XRefRole(ReferenceRole):
|
|
46
|
-
"""
|
|
47
|
-
A generic cross-referencing role. To create a callable that can be used as
|
|
45
|
+
"""A generic cross-referencing role. To create a callable that can be used as
|
|
48
46
|
a role function, create an instance of this class.
|
|
49
47
|
|
|
50
48
|
The general features of this role are:
|
|
@@ -371,8 +369,7 @@ class RFC(ReferenceRole):
|
|
|
371
369
|
|
|
372
370
|
|
|
373
371
|
def _format_rfc_target(target: str, /) -> str:
|
|
374
|
-
"""
|
|
375
|
-
Takes an RFC number with an optional anchor (like ``123#section-2.5.3``)
|
|
372
|
+
"""Takes an RFC number with an optional anchor (like ``123#section-2.5.3``)
|
|
376
373
|
and attempts to produce a human-friendly title for it.
|
|
377
374
|
|
|
378
375
|
We have a set of known anchors that we format nicely,
|
|
@@ -479,6 +476,59 @@ class Abbreviation(SphinxRole):
|
|
|
479
476
|
return [nodes.abbreviation(self.rawtext, text, **options)], []
|
|
480
477
|
|
|
481
478
|
|
|
479
|
+
class Keyboard(SphinxRole):
|
|
480
|
+
"""Implement the :kbd: role.
|
|
481
|
+
|
|
482
|
+
Split words in the text by separator or whitespace,
|
|
483
|
+
but keep multi-word keys together.
|
|
484
|
+
"""
|
|
485
|
+
|
|
486
|
+
# capture ('-', '+', '^', or whitespace) in between any two characters
|
|
487
|
+
_pattern: Final = re.compile(r'(?<=.)([\-+^]| +)(?=.)')
|
|
488
|
+
|
|
489
|
+
def run(self) -> tuple[list[Node], list[system_message]]:
|
|
490
|
+
classes = ['kbd']
|
|
491
|
+
if 'classes' in self.options:
|
|
492
|
+
classes.extend(self.options['classes'])
|
|
493
|
+
|
|
494
|
+
parts = self._pattern.split(self.text)
|
|
495
|
+
if len(parts) == 1 or self._is_multi_word_key(parts):
|
|
496
|
+
return [nodes.literal(self.rawtext, self.text, classes=classes)], []
|
|
497
|
+
|
|
498
|
+
compound: list[Node] = []
|
|
499
|
+
while parts:
|
|
500
|
+
if self._is_multi_word_key(parts):
|
|
501
|
+
key = ''.join(parts[:3])
|
|
502
|
+
parts[:3] = []
|
|
503
|
+
else:
|
|
504
|
+
key = parts.pop(0)
|
|
505
|
+
compound.append(nodes.literal(key, key, classes=classes))
|
|
506
|
+
|
|
507
|
+
try:
|
|
508
|
+
sep = parts.pop(0) # key separator ('-', '+', '^', etc)
|
|
509
|
+
except IndexError:
|
|
510
|
+
break
|
|
511
|
+
else:
|
|
512
|
+
compound.append(nodes.Text(sep))
|
|
513
|
+
|
|
514
|
+
return compound, []
|
|
515
|
+
|
|
516
|
+
@staticmethod
|
|
517
|
+
def _is_multi_word_key(parts: list[str]) -> bool:
|
|
518
|
+
if len(parts) <= 2 or not parts[1].isspace():
|
|
519
|
+
return False
|
|
520
|
+
name = parts[0].lower(), parts[2].lower()
|
|
521
|
+
return name in frozenset({
|
|
522
|
+
('back', 'space'),
|
|
523
|
+
('caps', 'lock'),
|
|
524
|
+
('num', 'lock'),
|
|
525
|
+
('page', 'down'),
|
|
526
|
+
('page', 'up'),
|
|
527
|
+
('scroll', 'lock'),
|
|
528
|
+
('sys', 'rq'),
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
|
|
482
532
|
class Manpage(ReferenceRole):
|
|
483
533
|
_manpage_re = re.compile(r'^(?P<path>(?P<page>.+)[(.](?P<section>[1-9]\w*)?\)?)$')
|
|
484
534
|
|
|
@@ -576,6 +626,7 @@ specific_docroles: dict[str, RoleFunction] = {
|
|
|
576
626
|
'samp': EmphasizedLiteral(),
|
|
577
627
|
# other
|
|
578
628
|
'abbr': Abbreviation(),
|
|
629
|
+
'kbd': Keyboard(),
|
|
579
630
|
'manpage': Manpage(),
|
|
580
631
|
}
|
|
581
632
|
|