Sphinx 8.1.3__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 +829 -480
- 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.3.dist-info → sphinx-8.2.0rc1.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/METADATA +23 -15
- {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/RECORD +190 -186
- {sphinx-8.1.3.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.3.dist-info → sphinx-8.2.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -4,10 +4,9 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
6
|
from itertools import starmap
|
|
7
|
-
from typing import TYPE_CHECKING,
|
|
7
|
+
from typing import TYPE_CHECKING, cast
|
|
8
8
|
|
|
9
9
|
from docutils import nodes
|
|
10
|
-
from docutils.nodes import Element, Node
|
|
11
10
|
|
|
12
11
|
from sphinx import addnodes
|
|
13
12
|
from sphinx.errors import NoUri
|
|
@@ -19,6 +18,9 @@ from sphinx.util.nodes import find_pending_xref_condition, process_only_nodes
|
|
|
19
18
|
|
|
20
19
|
if TYPE_CHECKING:
|
|
21
20
|
from collections.abc import Sequence
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from docutils.nodes import Element, Node
|
|
22
24
|
|
|
23
25
|
from sphinx.addnodes import pending_xref
|
|
24
26
|
from sphinx.application import Sphinx
|
|
@@ -58,9 +60,7 @@ class SphinxPostTransform(SphinxTransform):
|
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class ReferencesResolver(SphinxPostTransform):
|
|
61
|
-
"""
|
|
62
|
-
Resolves cross-references on doctrees.
|
|
63
|
-
"""
|
|
63
|
+
"""Resolves cross-references on doctrees."""
|
|
64
64
|
|
|
65
65
|
default_priority = 10
|
|
66
66
|
|
|
@@ -68,105 +68,158 @@ class ReferencesResolver(SphinxPostTransform):
|
|
|
68
68
|
for node in self.document.findall(addnodes.pending_xref):
|
|
69
69
|
content = self.find_pending_xref_condition(node, ('resolved', '*'))
|
|
70
70
|
if content:
|
|
71
|
-
contnode = cast(Element, content[0].deepcopy())
|
|
71
|
+
contnode = cast('Element', content[0].deepcopy())
|
|
72
72
|
else:
|
|
73
|
-
contnode = cast(Element, node[0].deepcopy())
|
|
74
|
-
|
|
75
|
-
newnode = None
|
|
73
|
+
contnode = cast('Element', node[0].deepcopy())
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
refdoc = node.get('refdoc')
|
|
81
|
-
domain = None
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
if node.get('refdomain', False):
|
|
85
|
-
# let the domain try to resolve the reference
|
|
86
|
-
try:
|
|
87
|
-
domain = self.env.domains[node['refdomain']]
|
|
88
|
-
except KeyError as exc:
|
|
89
|
-
raise NoUri(target, typ) from exc
|
|
90
|
-
newnode = domain.resolve_xref(
|
|
91
|
-
self.env, refdoc, self.app.builder, typ, target, node, contnode
|
|
92
|
-
)
|
|
93
|
-
# really hardwired reference types
|
|
94
|
-
elif typ == 'any':
|
|
95
|
-
newnode = self.resolve_anyref(refdoc, node, contnode)
|
|
96
|
-
# no new node found? try the missing-reference event
|
|
97
|
-
if newnode is None:
|
|
98
|
-
newnode = self.app.emit_firstresult(
|
|
99
|
-
'missing-reference',
|
|
100
|
-
self.env,
|
|
101
|
-
node,
|
|
102
|
-
contnode,
|
|
103
|
-
allowed_exceptions=(NoUri,),
|
|
104
|
-
)
|
|
105
|
-
# still not found? warn if node wishes to be warned about or
|
|
106
|
-
# we are in nitpicky mode
|
|
107
|
-
if newnode is None:
|
|
108
|
-
self.warn_missing_reference(refdoc, typ, target, node, domain)
|
|
109
|
-
except NoUri:
|
|
110
|
-
newnode = None
|
|
111
|
-
|
|
112
|
-
if newnode:
|
|
113
|
-
newnodes: list[Node] = [newnode]
|
|
75
|
+
new_node = self._resolve_pending_xref(node, contnode)
|
|
76
|
+
if new_node:
|
|
77
|
+
new_nodes: list[Node] = [new_node]
|
|
114
78
|
else:
|
|
115
|
-
|
|
116
|
-
if
|
|
79
|
+
new_nodes = [contnode]
|
|
80
|
+
if new_node is None and isinstance(
|
|
117
81
|
node[0], addnodes.pending_xref_condition
|
|
118
82
|
):
|
|
119
83
|
matched = self.find_pending_xref_condition(node, ('*',))
|
|
120
84
|
if matched:
|
|
121
|
-
|
|
85
|
+
new_nodes = matched
|
|
122
86
|
else:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
'cross-reference. Might be a bug.'
|
|
127
|
-
),
|
|
128
|
-
location=node,
|
|
87
|
+
msg = __(
|
|
88
|
+
'Could not determine the fallback text for the '
|
|
89
|
+
'cross-reference. Might be a bug.'
|
|
129
90
|
)
|
|
91
|
+
logger.warning(msg, location=node)
|
|
92
|
+
|
|
93
|
+
node.replace_self(new_nodes)
|
|
94
|
+
|
|
95
|
+
def _resolve_pending_xref(
|
|
96
|
+
self, node: addnodes.pending_xref, contnode: Element
|
|
97
|
+
) -> nodes.reference | None:
|
|
98
|
+
new_node: nodes.reference | None
|
|
99
|
+
typ = node['reftype']
|
|
100
|
+
target = node['reftarget']
|
|
101
|
+
ref_doc = node.setdefault('refdoc', self.env.docname)
|
|
102
|
+
ref_domain = node.get('refdomain', '')
|
|
103
|
+
domain: Domain | None
|
|
104
|
+
if ref_domain:
|
|
105
|
+
try:
|
|
106
|
+
domain = self.env.domains[ref_domain]
|
|
107
|
+
except KeyError:
|
|
108
|
+
return None
|
|
109
|
+
else:
|
|
110
|
+
domain = None
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
new_node = self._resolve_pending_xref_in_domain(
|
|
114
|
+
domain=domain,
|
|
115
|
+
node=node,
|
|
116
|
+
contnode=contnode,
|
|
117
|
+
ref_doc=ref_doc,
|
|
118
|
+
typ=typ,
|
|
119
|
+
target=target,
|
|
120
|
+
)
|
|
121
|
+
except NoUri:
|
|
122
|
+
return None
|
|
123
|
+
if new_node is not None:
|
|
124
|
+
return new_node
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
# no new node found? try the missing-reference event
|
|
128
|
+
new_node = self.app.events.emit_firstresult(
|
|
129
|
+
'missing-reference',
|
|
130
|
+
self.env,
|
|
131
|
+
node,
|
|
132
|
+
contnode,
|
|
133
|
+
allowed_exceptions=(NoUri,),
|
|
134
|
+
)
|
|
135
|
+
except NoUri:
|
|
136
|
+
return None
|
|
137
|
+
if new_node is not None:
|
|
138
|
+
return new_node
|
|
130
139
|
|
|
131
|
-
|
|
140
|
+
# Is this a self-referential intersphinx reference?
|
|
141
|
+
if 'intersphinx_self_referential' in node:
|
|
142
|
+
del node.attributes['intersphinx_self_referential']
|
|
143
|
+
try:
|
|
144
|
+
new_node = self._resolve_pending_xref_in_domain(
|
|
145
|
+
domain=domain,
|
|
146
|
+
node=node,
|
|
147
|
+
contnode=contnode,
|
|
148
|
+
ref_doc=ref_doc,
|
|
149
|
+
typ=typ,
|
|
150
|
+
target=node['reftarget'],
|
|
151
|
+
)
|
|
152
|
+
except NoUri:
|
|
153
|
+
return None
|
|
154
|
+
if new_node is not None:
|
|
155
|
+
return new_node
|
|
132
156
|
|
|
133
|
-
|
|
157
|
+
# Still not found? Emit a warning if we are in nitpicky mode
|
|
158
|
+
# or if the node wishes to be warned about.
|
|
159
|
+
self.warn_missing_reference(ref_doc, typ, target, node, domain)
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
def _resolve_pending_xref_in_domain(
|
|
134
163
|
self,
|
|
135
|
-
|
|
136
|
-
|
|
164
|
+
*,
|
|
165
|
+
domain: Domain | None,
|
|
166
|
+
node: addnodes.pending_xref,
|
|
137
167
|
contnode: Element,
|
|
138
|
-
|
|
168
|
+
ref_doc: str,
|
|
169
|
+
typ: str,
|
|
170
|
+
target: str,
|
|
171
|
+
) -> nodes.reference | None:
|
|
172
|
+
# let the domain try to resolve the reference
|
|
173
|
+
if domain is not None:
|
|
174
|
+
return domain.resolve_xref(
|
|
175
|
+
self.env, ref_doc, self.app.builder, typ, target, node, contnode
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# really hardwired reference types
|
|
179
|
+
if typ == 'any':
|
|
180
|
+
return self._resolve_pending_any_xref(
|
|
181
|
+
node=node, contnode=contnode, ref_doc=ref_doc, target=target
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
def _resolve_pending_any_xref(
|
|
187
|
+
self,
|
|
188
|
+
*,
|
|
189
|
+
node: addnodes.pending_xref,
|
|
190
|
+
contnode: Element,
|
|
191
|
+
ref_doc: str,
|
|
192
|
+
target: str,
|
|
193
|
+
) -> nodes.reference | None:
|
|
139
194
|
"""Resolve reference generated by the "any" role."""
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
195
|
+
env = self.env
|
|
196
|
+
builder = self.app.builder
|
|
197
|
+
domains = env.domains
|
|
198
|
+
|
|
199
|
+
results: list[tuple[str, nodes.reference]] = []
|
|
143
200
|
# first, try resolving as :doc:
|
|
144
|
-
doc_ref =
|
|
145
|
-
|
|
201
|
+
doc_ref = domains.standard_domain.resolve_xref(
|
|
202
|
+
env, ref_doc, builder, 'doc', target, node, contnode
|
|
146
203
|
)
|
|
147
204
|
if doc_ref:
|
|
148
205
|
results.append(('doc', doc_ref))
|
|
149
206
|
# next, do the standard domain (makes this a priority)
|
|
150
|
-
results.
|
|
151
|
-
|
|
152
|
-
self.env, refdoc, self.app.builder, target, node, contnode
|
|
153
|
-
)
|
|
207
|
+
results += domains.standard_domain.resolve_any_xref(
|
|
208
|
+
env, ref_doc, builder, target, node, contnode
|
|
154
209
|
)
|
|
155
|
-
for domain in
|
|
210
|
+
for domain in domains.sorted():
|
|
156
211
|
if domain.name == 'std':
|
|
157
212
|
continue # we did this one already
|
|
158
213
|
try:
|
|
159
|
-
results.
|
|
160
|
-
|
|
161
|
-
self.env, refdoc, self.app.builder, target, node, contnode
|
|
162
|
-
)
|
|
214
|
+
results += domain.resolve_any_xref(
|
|
215
|
+
env, ref_doc, builder, target, node, contnode
|
|
163
216
|
)
|
|
164
217
|
except NotImplementedError:
|
|
165
218
|
# the domain doesn't yet support the new interface
|
|
166
219
|
# we have to manually collect possible references (SLOW)
|
|
167
220
|
for role in domain.roles:
|
|
168
221
|
res = domain.resolve_xref(
|
|
169
|
-
|
|
222
|
+
env, ref_doc, builder, role, target, node, contnode
|
|
170
223
|
)
|
|
171
224
|
if res and len(res) > 0 and isinstance(res[0], nodes.Element):
|
|
172
225
|
results.append((f'{domain.name}:{role}', res))
|
|
@@ -180,27 +233,23 @@ class ReferencesResolver(SphinxPostTransform):
|
|
|
180
233
|
return f':{name}:`{reftitle}`'
|
|
181
234
|
|
|
182
235
|
candidates = ' or '.join(starmap(stringify, results))
|
|
236
|
+
msg = __(
|
|
237
|
+
"more than one target found for 'any' cross-reference %r: could be %s"
|
|
238
|
+
)
|
|
183
239
|
logger.warning(
|
|
184
|
-
|
|
185
|
-
"more than one target found for 'any' cross-"
|
|
186
|
-
'reference %r: could be %s'
|
|
187
|
-
),
|
|
188
|
-
target,
|
|
189
|
-
candidates,
|
|
190
|
-
location=node,
|
|
240
|
+
msg, target, candidates, location=node, type='ref', subtype='any'
|
|
191
241
|
)
|
|
192
|
-
res_role,
|
|
242
|
+
res_role, new_node = results[0]
|
|
193
243
|
# Override "any" class with the actual role type to get the styling
|
|
194
244
|
# approximately correct.
|
|
195
|
-
res_domain = res_role.
|
|
245
|
+
res_domain = res_role.partition(':')[0]
|
|
196
246
|
if (
|
|
197
|
-
len(
|
|
198
|
-
and isinstance(
|
|
199
|
-
and
|
|
247
|
+
len(new_node) > 0
|
|
248
|
+
and isinstance(new_node[0], nodes.Element)
|
|
249
|
+
and new_node[0].get('classes')
|
|
200
250
|
):
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return newnode
|
|
251
|
+
new_node[0]['classes'].extend((res_domain, res_role.replace(':', '-')))
|
|
252
|
+
return new_node
|
|
204
253
|
|
|
205
254
|
def warn_missing_reference(
|
|
206
255
|
self,
|
|
@@ -242,11 +291,11 @@ class ReferencesResolver(SphinxPostTransform):
|
|
|
242
291
|
if not warn:
|
|
243
292
|
return
|
|
244
293
|
|
|
245
|
-
if self.app.emit_firstresult('warn-missing-reference', domain, node):
|
|
294
|
+
if self.app.events.emit_firstresult('warn-missing-reference', domain, node):
|
|
246
295
|
return
|
|
247
296
|
elif domain and typ in domain.dangling_warnings:
|
|
248
297
|
msg = domain.dangling_warnings[typ] % {'target': target}
|
|
249
|
-
elif node.get('refdomain', 'std') not in
|
|
298
|
+
elif node.get('refdomain', 'std') not in {'', 'std'}:
|
|
250
299
|
msg = __('%s:%s reference target not found: %s') % (
|
|
251
300
|
node['refdomain'],
|
|
252
301
|
typ,
|
|
@@ -276,7 +325,7 @@ class OnlyNodeTransform(SphinxPostTransform):
|
|
|
276
325
|
# result in a "Losing ids" exception if there is a target node before
|
|
277
326
|
# the only node, so we make sure docutils can transfer the id to
|
|
278
327
|
# something, even if it's just a comment and will lose the id anyway...
|
|
279
|
-
process_only_nodes(self.document, self.app.
|
|
328
|
+
process_only_nodes(self.document, self.app.tags)
|
|
280
329
|
|
|
281
330
|
|
|
282
331
|
class SigElementFallbackTransform(SphinxPostTransform):
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import sys
|
|
6
|
-
from typing import TYPE_CHECKING,
|
|
6
|
+
from typing import TYPE_CHECKING, NamedTuple
|
|
7
7
|
|
|
8
8
|
from docutils import nodes
|
|
9
9
|
from pygments.lexers import PythonConsoleLexer, guess_lexer
|
|
@@ -13,6 +13,8 @@ from sphinx.ext import doctest
|
|
|
13
13
|
from sphinx.transforms import SphinxTransform
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
16
18
|
from docutils.nodes import Node, TextElement
|
|
17
19
|
|
|
18
20
|
from sphinx.application import Sphinx
|
|
@@ -26,8 +28,7 @@ class HighlightSetting(NamedTuple):
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class HighlightLanguageTransform(SphinxTransform):
|
|
29
|
-
"""
|
|
30
|
-
Apply highlight_language to all literal_block nodes.
|
|
31
|
+
"""Apply highlight_language to all literal_block nodes.
|
|
31
32
|
|
|
32
33
|
This refers both :confval:`highlight_language` setting and
|
|
33
34
|
:rst:dir:`highlight` directive. After processing, this transform
|
|
@@ -86,8 +87,7 @@ class HighlightLanguageVisitor(nodes.NodeVisitor):
|
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
class TrimDoctestFlagsTransform(SphinxTransform):
|
|
89
|
-
"""
|
|
90
|
-
Trim doctest flags like ``# doctest: +FLAG`` from python code-blocks.
|
|
90
|
+
"""Trim doctest flags like ``# doctest: +FLAG`` from python code-blocks.
|
|
91
91
|
|
|
92
92
|
see :confval:`trim_doctest_flags` for more information.
|
|
93
93
|
"""
|
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
from hashlib import sha1
|
|
8
8
|
from math import ceil
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
11
|
|
|
12
12
|
from docutils import nodes
|
|
13
13
|
|
|
@@ -20,6 +20,8 @@ from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_u
|
|
|
20
20
|
from sphinx.util.osutil import ensuredir
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
23
25
|
from sphinx.application import Sphinx
|
|
24
26
|
from sphinx.util.typing import ExtensionMetadata
|
|
25
27
|
|
|
@@ -42,8 +44,8 @@ class BaseImageConverter(SphinxTransform):
|
|
|
42
44
|
pass
|
|
43
45
|
|
|
44
46
|
@property
|
|
45
|
-
def imagedir(self) ->
|
|
46
|
-
return
|
|
47
|
+
def imagedir(self) -> _StrPath:
|
|
48
|
+
return self.app.doctreedir / 'images'
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
class ImageDownloader(BaseImageConverter):
|
|
@@ -61,7 +63,7 @@ class ImageDownloader(BaseImageConverter):
|
|
|
61
63
|
basename = os.path.basename(node['uri'])
|
|
62
64
|
if '?' in basename:
|
|
63
65
|
basename = basename.split('?')[0]
|
|
64
|
-
if basename
|
|
66
|
+
if not basename or len(basename) > MAX_FILENAME_LEN:
|
|
65
67
|
filename, ext = os.path.splitext(node['uri'])
|
|
66
68
|
basename = (
|
|
67
69
|
sha1(filename.encode(), usedforsecurity=False).hexdigest() + ext
|
|
@@ -83,7 +85,7 @@ class ImageDownloader(BaseImageConverter):
|
|
|
83
85
|
timestamp: float = ceil(path.stat().st_mtime)
|
|
84
86
|
headers['If-Modified-Since'] = epoch_to_rfc1123(timestamp)
|
|
85
87
|
|
|
86
|
-
config = self.
|
|
88
|
+
config = self.config
|
|
87
89
|
r = requests.get(
|
|
88
90
|
node['uri'],
|
|
89
91
|
headers=headers,
|
|
@@ -94,7 +96,7 @@ class ImageDownloader(BaseImageConverter):
|
|
|
94
96
|
msg = __('Could not fetch remote image: %s [%d]')
|
|
95
97
|
logger.warning(msg, node['uri'], r.status_code)
|
|
96
98
|
else:
|
|
97
|
-
self.
|
|
99
|
+
self.env.original_image_uri[_StrPath(path)] = node['uri']
|
|
98
100
|
|
|
99
101
|
if r.status_code == 200:
|
|
100
102
|
path.write_bytes(r.content)
|
|
@@ -106,22 +108,22 @@ class ImageDownloader(BaseImageConverter):
|
|
|
106
108
|
|
|
107
109
|
def _process_image(self, node: nodes.image, path: Path) -> None:
|
|
108
110
|
str_path = _StrPath(path)
|
|
109
|
-
self.
|
|
111
|
+
self.env.original_image_uri[str_path] = node['uri']
|
|
110
112
|
|
|
111
113
|
mimetype = guess_mimetype(path, default='*')
|
|
112
|
-
if mimetype != '*' and path.suffix
|
|
114
|
+
if mimetype != '*' and not path.suffix:
|
|
113
115
|
# append a suffix if URI does not contain suffix
|
|
114
116
|
ext = get_image_extension(mimetype) or ''
|
|
115
117
|
with_ext = path.with_name(path.name + ext)
|
|
116
|
-
|
|
117
|
-
self.
|
|
118
|
-
self.
|
|
118
|
+
path.replace(with_ext)
|
|
119
|
+
self.env.original_image_uri.pop(str_path)
|
|
120
|
+
self.env.original_image_uri[_StrPath(with_ext)] = node['uri']
|
|
119
121
|
path = with_ext
|
|
120
122
|
path_str = str(path)
|
|
121
123
|
node['candidates'].pop('?')
|
|
122
124
|
node['candidates'][mimetype] = path_str
|
|
123
125
|
node['uri'] = path_str
|
|
124
|
-
self.
|
|
126
|
+
self.env.images.add_file(self.env.docname, path_str)
|
|
125
127
|
|
|
126
128
|
|
|
127
129
|
class DataURIExtractor(BaseImageConverter):
|
|
@@ -142,10 +144,10 @@ class DataURIExtractor(BaseImageConverter):
|
|
|
142
144
|
)
|
|
143
145
|
return
|
|
144
146
|
|
|
145
|
-
ensuredir(
|
|
147
|
+
ensuredir(self.imagedir / 'embeded')
|
|
146
148
|
digest = sha1(image.data, usedforsecurity=False).hexdigest()
|
|
147
|
-
path =
|
|
148
|
-
self.
|
|
149
|
+
path = self.imagedir / 'embeded' / (digest + ext)
|
|
150
|
+
self.env.original_image_uri[path] = node['uri']
|
|
149
151
|
|
|
150
152
|
with open(path, 'wb') as f:
|
|
151
153
|
f.write(image.data)
|
|
@@ -154,7 +156,7 @@ class DataURIExtractor(BaseImageConverter):
|
|
|
154
156
|
node['candidates'].pop('?')
|
|
155
157
|
node['candidates'][image.mimetype] = path_str
|
|
156
158
|
node['uri'] = path_str
|
|
157
|
-
self.
|
|
159
|
+
self.env.images.add_file(self.env.docname, path_str)
|
|
158
160
|
|
|
159
161
|
|
|
160
162
|
def get_filename_for(filename: str, mimetype: str) -> str:
|
|
@@ -248,7 +250,7 @@ class ImageConverter(BaseImageConverter):
|
|
|
248
250
|
if '?' in node['candidates']:
|
|
249
251
|
return []
|
|
250
252
|
elif '*' in node['candidates']:
|
|
251
|
-
path =
|
|
253
|
+
path = self.app.srcdir / node['uri']
|
|
252
254
|
guessed = guess_mimetype(path)
|
|
253
255
|
return [guessed] if guessed is not None else []
|
|
254
256
|
else:
|
|
@@ -265,20 +267,22 @@ class ImageConverter(BaseImageConverter):
|
|
|
265
267
|
filename = self.env.images[srcpath][1]
|
|
266
268
|
filename = get_filename_for(filename, _to)
|
|
267
269
|
ensuredir(self.imagedir)
|
|
268
|
-
destpath =
|
|
270
|
+
destpath = self.imagedir / filename
|
|
269
271
|
|
|
270
|
-
abs_srcpath =
|
|
272
|
+
abs_srcpath = self.app.srcdir / srcpath
|
|
271
273
|
if self.convert(abs_srcpath, destpath):
|
|
272
274
|
if '*' in node['candidates']:
|
|
273
|
-
node['candidates']['*'] = destpath
|
|
275
|
+
node['candidates']['*'] = str(destpath)
|
|
274
276
|
else:
|
|
275
|
-
node['candidates'][_to] = destpath
|
|
276
|
-
node['uri'] = destpath
|
|
277
|
+
node['candidates'][_to] = str(destpath)
|
|
278
|
+
node['uri'] = str(destpath)
|
|
277
279
|
|
|
278
|
-
self.env.original_image_uri[
|
|
280
|
+
self.env.original_image_uri[destpath] = srcpath
|
|
279
281
|
self.env.images.add_file(self.env.docname, destpath)
|
|
280
282
|
|
|
281
|
-
def convert(
|
|
283
|
+
def convert(
|
|
284
|
+
self, _from: str | os.PathLike[str], _to: str | os.PathLike[str]
|
|
285
|
+
) -> bool:
|
|
282
286
|
"""Convert an image file to the expected format.
|
|
283
287
|
|
|
284
288
|
*_from* is a path of the source image file, and *_to* is a path
|
sphinx/transforms/references.py
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from docutils.transforms.references import DanglingReferences
|
|
8
8
|
|
|
9
9
|
from sphinx.transforms import SphinxTransform
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
12
14
|
from sphinx.application import Sphinx
|
|
13
15
|
from sphinx.util.typing import ExtensionMetadata
|
|
14
16
|
|