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
|
@@ -5,7 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import builtins
|
|
6
6
|
import inspect
|
|
7
7
|
import typing
|
|
8
|
-
from
|
|
8
|
+
from types import NoneType
|
|
9
|
+
from typing import TYPE_CHECKING, NamedTuple, cast
|
|
9
10
|
|
|
10
11
|
from docutils import nodes
|
|
11
12
|
from docutils.parsers.rst import directives
|
|
@@ -25,9 +26,10 @@ from sphinx.util.nodes import (
|
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
if TYPE_CHECKING:
|
|
28
|
-
from collections.abc import Iterable, Iterator, Set
|
|
29
|
+
from collections.abc import Iterable, Iterator, Sequence, Set
|
|
30
|
+
from typing import Any, ClassVar
|
|
29
31
|
|
|
30
|
-
from docutils.nodes import Element, Node
|
|
32
|
+
from docutils.nodes import Element, Node, TextElement
|
|
31
33
|
|
|
32
34
|
from sphinx.addnodes import desc_signature, pending_xref
|
|
33
35
|
from sphinx.application import Sphinx
|
|
@@ -36,7 +38,8 @@ if TYPE_CHECKING:
|
|
|
36
38
|
from sphinx.util.typing import ExtensionMetadata, OptionSpec
|
|
37
39
|
|
|
38
40
|
# re-export objects for backwards compatibility
|
|
39
|
-
#
|
|
41
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/12295
|
|
42
|
+
|
|
40
43
|
from sphinx.domains.python._annotations import ( # NoQA: F401
|
|
41
44
|
_parse_arglist, # for sphinx-immaterial
|
|
42
45
|
type_to_xref,
|
|
@@ -80,23 +83,26 @@ class ModuleEntry(NamedTuple):
|
|
|
80
83
|
class PyFunction(PyObject):
|
|
81
84
|
"""Description of a function."""
|
|
82
85
|
|
|
83
|
-
option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
|
|
86
|
+
option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
|
|
84
87
|
option_spec.update({
|
|
85
88
|
'async': directives.flag,
|
|
86
89
|
})
|
|
87
90
|
|
|
88
|
-
def get_signature_prefix(self, sig: str) ->
|
|
91
|
+
def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
|
|
92
|
+
prefix: list[addnodes.desc_sig_element] = []
|
|
89
93
|
if 'async' in self.options:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
prefix.extend((
|
|
95
|
+
addnodes.desc_sig_keyword('', 'async'),
|
|
96
|
+
addnodes.desc_sig_space(),
|
|
97
|
+
))
|
|
98
|
+
return prefix
|
|
94
99
|
|
|
95
100
|
def needs_arglist(self) -> bool:
|
|
96
101
|
return True
|
|
97
102
|
|
|
98
|
-
def add_target_and_index(
|
|
99
|
-
|
|
103
|
+
def add_target_and_index(
|
|
104
|
+
self, name_cls: tuple[str, str], sig: str, signode: desc_signature
|
|
105
|
+
) -> None:
|
|
100
106
|
super().add_target_and_index(name_cls, sig, signode)
|
|
101
107
|
if 'no-index-entry' not in self.options:
|
|
102
108
|
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
|
@@ -147,17 +153,24 @@ class PyVariable(PyObject):
|
|
|
147
153
|
typ = self.options.get('type')
|
|
148
154
|
if typ:
|
|
149
155
|
annotations = _parse_annotation(typ, self.env)
|
|
150
|
-
signode += addnodes.desc_annotation(
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
signode += addnodes.desc_annotation(
|
|
157
|
+
typ,
|
|
158
|
+
'',
|
|
159
|
+
addnodes.desc_sig_punctuation('', ':'),
|
|
160
|
+
addnodes.desc_sig_space(),
|
|
161
|
+
*annotations,
|
|
162
|
+
)
|
|
153
163
|
|
|
154
164
|
value = self.options.get('value')
|
|
155
165
|
if value:
|
|
156
|
-
signode += addnodes.desc_annotation(
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
166
|
+
signode += addnodes.desc_annotation(
|
|
167
|
+
value,
|
|
168
|
+
'',
|
|
169
|
+
addnodes.desc_sig_space(),
|
|
170
|
+
addnodes.desc_sig_punctuation('', '='),
|
|
171
|
+
addnodes.desc_sig_space(),
|
|
172
|
+
nodes.Text(value),
|
|
173
|
+
)
|
|
161
174
|
|
|
162
175
|
return fullname, prefix
|
|
163
176
|
|
|
@@ -170,23 +183,33 @@ class PyVariable(PyObject):
|
|
|
170
183
|
|
|
171
184
|
|
|
172
185
|
class PyClasslike(PyObject):
|
|
173
|
-
"""
|
|
174
|
-
Description of a class-like object (classes, interfaces, exceptions).
|
|
175
|
-
"""
|
|
186
|
+
"""Description of a class-like object (classes, interfaces, exceptions)."""
|
|
176
187
|
|
|
177
188
|
option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
|
|
178
189
|
option_spec.update({
|
|
190
|
+
'abstract': directives.flag,
|
|
179
191
|
'final': directives.flag,
|
|
180
192
|
})
|
|
181
193
|
|
|
182
194
|
allow_nesting = True
|
|
183
195
|
|
|
184
|
-
def get_signature_prefix(self, sig: str) ->
|
|
196
|
+
def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
|
|
197
|
+
prefix: list[addnodes.desc_sig_element] = []
|
|
185
198
|
if 'final' in self.options:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
prefix.extend((
|
|
200
|
+
addnodes.desc_sig_keyword('', 'final'),
|
|
201
|
+
addnodes.desc_sig_space(),
|
|
202
|
+
))
|
|
203
|
+
if 'abstract' in self.options:
|
|
204
|
+
prefix.extend((
|
|
205
|
+
addnodes.desc_sig_keyword('', 'abstract'),
|
|
206
|
+
addnodes.desc_sig_space(),
|
|
207
|
+
))
|
|
208
|
+
prefix.extend((
|
|
209
|
+
addnodes.desc_sig_keyword('', self.objtype),
|
|
210
|
+
addnodes.desc_sig_space(),
|
|
211
|
+
))
|
|
212
|
+
return prefix
|
|
190
213
|
|
|
191
214
|
def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
|
|
192
215
|
if self.objtype == 'class':
|
|
@@ -204,6 +227,7 @@ class PyMethod(PyObject):
|
|
|
204
227
|
|
|
205
228
|
option_spec: ClassVar[OptionSpec] = PyObject.option_spec.copy()
|
|
206
229
|
option_spec.update({
|
|
230
|
+
'abstract': directives.flag,
|
|
207
231
|
'abstractmethod': directives.flag,
|
|
208
232
|
'async': directives.flag,
|
|
209
233
|
'classmethod': directives.flag,
|
|
@@ -214,30 +238,40 @@ class PyMethod(PyObject):
|
|
|
214
238
|
def needs_arglist(self) -> bool:
|
|
215
239
|
return True
|
|
216
240
|
|
|
217
|
-
def get_signature_prefix(self, sig: str) ->
|
|
218
|
-
prefix: list[
|
|
241
|
+
def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
|
|
242
|
+
prefix: list[addnodes.desc_sig_element] = []
|
|
219
243
|
if 'final' in self.options:
|
|
220
|
-
prefix.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
244
|
+
prefix.extend((
|
|
245
|
+
addnodes.desc_sig_keyword('', 'final'),
|
|
246
|
+
addnodes.desc_sig_space(),
|
|
247
|
+
))
|
|
248
|
+
if 'abstract' in self.options or 'abstractmethod' in self.options:
|
|
249
|
+
prefix.extend((
|
|
250
|
+
addnodes.desc_sig_keyword('', 'abstractmethod'),
|
|
251
|
+
addnodes.desc_sig_space(),
|
|
252
|
+
))
|
|
225
253
|
if 'async' in self.options:
|
|
226
|
-
prefix.
|
|
227
|
-
|
|
254
|
+
prefix.extend((
|
|
255
|
+
addnodes.desc_sig_keyword('', 'async'),
|
|
256
|
+
addnodes.desc_sig_space(),
|
|
257
|
+
))
|
|
228
258
|
if 'classmethod' in self.options:
|
|
229
|
-
prefix.
|
|
230
|
-
|
|
259
|
+
prefix.extend((
|
|
260
|
+
addnodes.desc_sig_keyword('', 'classmethod'),
|
|
261
|
+
addnodes.desc_sig_space(),
|
|
262
|
+
))
|
|
231
263
|
if 'staticmethod' in self.options:
|
|
232
|
-
prefix.
|
|
233
|
-
|
|
264
|
+
prefix.extend((
|
|
265
|
+
addnodes.desc_sig_keyword('', 'static'),
|
|
266
|
+
addnodes.desc_sig_space(),
|
|
267
|
+
))
|
|
234
268
|
return prefix
|
|
235
269
|
|
|
236
270
|
def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
|
|
237
271
|
name, cls = name_cls
|
|
238
272
|
try:
|
|
239
273
|
clsname, methname = name.rsplit('.', 1)
|
|
240
|
-
if modname and self.
|
|
274
|
+
if modname and self.config.add_module_names:
|
|
241
275
|
clsname = f'{modname}.{clsname}'
|
|
242
276
|
except ValueError:
|
|
243
277
|
if modname:
|
|
@@ -308,18 +342,24 @@ class PyAttribute(PyObject):
|
|
|
308
342
|
typ = self.options.get('type')
|
|
309
343
|
if typ:
|
|
310
344
|
annotations = _parse_annotation(typ, self.env)
|
|
311
|
-
signode += addnodes.desc_annotation(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
345
|
+
signode += addnodes.desc_annotation(
|
|
346
|
+
typ,
|
|
347
|
+
'',
|
|
348
|
+
addnodes.desc_sig_punctuation('', ':'),
|
|
349
|
+
addnodes.desc_sig_space(),
|
|
350
|
+
*annotations,
|
|
351
|
+
)
|
|
315
352
|
|
|
316
353
|
value = self.options.get('value')
|
|
317
354
|
if value:
|
|
318
|
-
signode += addnodes.desc_annotation(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
355
|
+
signode += addnodes.desc_annotation(
|
|
356
|
+
value,
|
|
357
|
+
'',
|
|
358
|
+
addnodes.desc_sig_space(),
|
|
359
|
+
addnodes.desc_sig_punctuation('', '='),
|
|
360
|
+
addnodes.desc_sig_space(),
|
|
361
|
+
nodes.Text(value),
|
|
362
|
+
)
|
|
323
363
|
|
|
324
364
|
return fullname, prefix
|
|
325
365
|
|
|
@@ -327,7 +367,7 @@ class PyAttribute(PyObject):
|
|
|
327
367
|
name, cls = name_cls
|
|
328
368
|
try:
|
|
329
369
|
clsname, attrname = name.rsplit('.', 1)
|
|
330
|
-
if modname and self.
|
|
370
|
+
if modname and self.config.add_module_names:
|
|
331
371
|
clsname = f'{modname}.{clsname}'
|
|
332
372
|
except ValueError:
|
|
333
373
|
if modname:
|
|
@@ -343,6 +383,7 @@ class PyProperty(PyObject):
|
|
|
343
383
|
|
|
344
384
|
option_spec = PyObject.option_spec.copy()
|
|
345
385
|
option_spec.update({
|
|
386
|
+
'abstract': directives.flag,
|
|
346
387
|
'abstractmethod': directives.flag,
|
|
347
388
|
'classmethod': directives.flag,
|
|
348
389
|
'type': directives.unchanged,
|
|
@@ -354,31 +395,39 @@ class PyProperty(PyObject):
|
|
|
354
395
|
typ = self.options.get('type')
|
|
355
396
|
if typ:
|
|
356
397
|
annotations = _parse_annotation(typ, self.env)
|
|
357
|
-
signode += addnodes.desc_annotation(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
398
|
+
signode += addnodes.desc_annotation(
|
|
399
|
+
typ,
|
|
400
|
+
'',
|
|
401
|
+
addnodes.desc_sig_punctuation('', ':'),
|
|
402
|
+
addnodes.desc_sig_space(),
|
|
403
|
+
*annotations,
|
|
404
|
+
)
|
|
361
405
|
|
|
362
406
|
return fullname, prefix
|
|
363
407
|
|
|
364
|
-
def get_signature_prefix(self, sig: str) ->
|
|
365
|
-
prefix: list[
|
|
366
|
-
if 'abstractmethod' in self.options:
|
|
367
|
-
prefix.
|
|
368
|
-
|
|
408
|
+
def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
|
|
409
|
+
prefix: list[addnodes.desc_sig_element] = []
|
|
410
|
+
if 'abstract' in self.options or 'abstractmethod' in self.options:
|
|
411
|
+
prefix.extend((
|
|
412
|
+
addnodes.desc_sig_keyword('', 'abstract'),
|
|
413
|
+
addnodes.desc_sig_space(),
|
|
414
|
+
))
|
|
369
415
|
if 'classmethod' in self.options:
|
|
370
|
-
prefix.
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
prefix.
|
|
416
|
+
prefix.extend((
|
|
417
|
+
addnodes.desc_sig_keyword('', 'class'),
|
|
418
|
+
addnodes.desc_sig_space(),
|
|
419
|
+
))
|
|
420
|
+
prefix.extend((
|
|
421
|
+
addnodes.desc_sig_keyword('', 'property'),
|
|
422
|
+
addnodes.desc_sig_space(),
|
|
423
|
+
))
|
|
375
424
|
return prefix
|
|
376
425
|
|
|
377
426
|
def get_index_text(self, modname: str, name_cls: tuple[str, str]) -> str:
|
|
378
427
|
name, cls = name_cls
|
|
379
428
|
try:
|
|
380
429
|
clsname, attrname = name.rsplit('.', 1)
|
|
381
|
-
if modname and self.
|
|
430
|
+
if modname and self.config.add_module_names:
|
|
382
431
|
clsname = f'{modname}.{clsname}'
|
|
383
432
|
except ValueError:
|
|
384
433
|
if modname:
|
|
@@ -397,15 +446,16 @@ class PyTypeAlias(PyObject):
|
|
|
397
446
|
'canonical': directives.unchanged,
|
|
398
447
|
})
|
|
399
448
|
|
|
400
|
-
def get_signature_prefix(self, sig: str) ->
|
|
401
|
-
return [
|
|
449
|
+
def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
|
|
450
|
+
return [addnodes.desc_sig_keyword('', 'type'), addnodes.desc_sig_space()]
|
|
402
451
|
|
|
403
452
|
def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str]:
|
|
404
453
|
fullname, prefix = super().handle_signature(sig, signode)
|
|
405
454
|
if canonical := self.options.get('canonical'):
|
|
406
455
|
canonical_annotations = _parse_annotation(canonical, self.env)
|
|
407
456
|
signode += addnodes.desc_annotation(
|
|
408
|
-
canonical,
|
|
457
|
+
canonical,
|
|
458
|
+
'',
|
|
409
459
|
addnodes.desc_sig_space(),
|
|
410
460
|
addnodes.desc_sig_punctuation('', '='),
|
|
411
461
|
addnodes.desc_sig_space(),
|
|
@@ -417,7 +467,7 @@ class PyTypeAlias(PyObject):
|
|
|
417
467
|
name, cls = name_cls
|
|
418
468
|
try:
|
|
419
469
|
clsname, attrname = name.rsplit('.', 1)
|
|
420
|
-
if modname and self.
|
|
470
|
+
if modname and self.config.add_module_names:
|
|
421
471
|
clsname = f'{modname}.{clsname}'
|
|
422
472
|
except ValueError:
|
|
423
473
|
if modname:
|
|
@@ -429,9 +479,7 @@ class PyTypeAlias(PyObject):
|
|
|
429
479
|
|
|
430
480
|
|
|
431
481
|
class PyModule(SphinxDirective):
|
|
432
|
-
"""
|
|
433
|
-
Directive to mark description of a new module.
|
|
434
|
-
"""
|
|
482
|
+
"""Directive to mark description of a new module."""
|
|
435
483
|
|
|
436
484
|
has_content = True
|
|
437
485
|
required_arguments = 1
|
|
@@ -441,6 +489,7 @@ class PyModule(SphinxDirective):
|
|
|
441
489
|
'platform': lambda x: x,
|
|
442
490
|
'synopsis': lambda x: x,
|
|
443
491
|
'no-index': directives.flag,
|
|
492
|
+
'no-index-entry': directives.flag,
|
|
444
493
|
'no-contents-entry': directives.flag,
|
|
445
494
|
'no-typesetting': directives.flag,
|
|
446
495
|
'noindex': directives.flag,
|
|
@@ -471,27 +520,32 @@ class PyModule(SphinxDirective):
|
|
|
471
520
|
self.set_source_info(target)
|
|
472
521
|
self.state.document.note_explicit_target(target)
|
|
473
522
|
|
|
474
|
-
domain.note_module(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
523
|
+
domain.note_module(
|
|
524
|
+
name=modname,
|
|
525
|
+
node_id=node_id,
|
|
526
|
+
synopsis=self.options.get('synopsis', ''),
|
|
527
|
+
platform=self.options.get('platform', ''),
|
|
528
|
+
deprecated='deprecated' in self.options,
|
|
529
|
+
)
|
|
479
530
|
domain.note_object(modname, 'module', node_id, location=target)
|
|
480
531
|
|
|
481
532
|
# the platform and synopsis aren't printed; in fact, they are only
|
|
482
533
|
# used in the modindex currently
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
534
|
+
|
|
535
|
+
if 'no-index-entry' not in self.options:
|
|
536
|
+
index_text = f'module; {modname}'
|
|
537
|
+
inode = addnodes.index(
|
|
538
|
+
entries=[('pair', index_text, node_id, '', None)]
|
|
539
|
+
)
|
|
540
|
+
# The node order is: index node first, then target node.
|
|
541
|
+
ret.append(inode)
|
|
487
542
|
ret.append(target)
|
|
488
543
|
ret.extend(content_nodes)
|
|
489
544
|
return ret
|
|
490
545
|
|
|
491
546
|
|
|
492
547
|
class PyCurrentModule(SphinxDirective):
|
|
493
|
-
"""
|
|
494
|
-
This directive is just to tell Sphinx that we're documenting
|
|
548
|
+
"""This directive is just to tell Sphinx that we're documenting
|
|
495
549
|
stuff in module foo, but links to module foo won't lead here.
|
|
496
550
|
"""
|
|
497
551
|
|
|
@@ -511,12 +565,18 @@ class PyCurrentModule(SphinxDirective):
|
|
|
511
565
|
|
|
512
566
|
|
|
513
567
|
class PyXRefRole(XRefRole):
|
|
514
|
-
def process_link(
|
|
515
|
-
|
|
568
|
+
def process_link(
|
|
569
|
+
self,
|
|
570
|
+
env: BuildEnvironment,
|
|
571
|
+
refnode: Element,
|
|
572
|
+
has_explicit_title: bool,
|
|
573
|
+
title: str,
|
|
574
|
+
target: str,
|
|
575
|
+
) -> tuple[str, str]:
|
|
516
576
|
refnode['py:module'] = env.ref_context.get('py:module')
|
|
517
577
|
refnode['py:class'] = env.ref_context.get('py:class')
|
|
518
578
|
if not has_explicit_title:
|
|
519
|
-
title = title.lstrip('.')
|
|
579
|
+
title = title.lstrip('.') # only has a meaning for the target
|
|
520
580
|
target = target.lstrip('~') # only has a meaning for the title
|
|
521
581
|
# if the first character is a tilde, don't display the module/class
|
|
522
582
|
# parts of the contents
|
|
@@ -524,7 +584,7 @@ class PyXRefRole(XRefRole):
|
|
|
524
584
|
title = title[1:]
|
|
525
585
|
dot = title.rfind('.')
|
|
526
586
|
if dot != -1:
|
|
527
|
-
title = title[dot + 1:]
|
|
587
|
+
title = title[dot + 1 :]
|
|
528
588
|
# if the first character is a dot, search more specific namespaces first
|
|
529
589
|
# else search builtins first
|
|
530
590
|
if target[0:1] == '.':
|
|
@@ -533,49 +593,77 @@ class PyXRefRole(XRefRole):
|
|
|
533
593
|
return title, target
|
|
534
594
|
|
|
535
595
|
|
|
536
|
-
|
|
596
|
+
class _PyDecoXRefRole(PyXRefRole):
|
|
597
|
+
def __init__(
|
|
598
|
+
self,
|
|
599
|
+
fix_parens: bool = False,
|
|
600
|
+
lowercase: bool = False,
|
|
601
|
+
nodeclass: type[Element] | None = None,
|
|
602
|
+
innernodeclass: type[TextElement] | None = None,
|
|
603
|
+
warn_dangling: bool = False,
|
|
604
|
+
) -> None:
|
|
605
|
+
super().__init__(
|
|
606
|
+
fix_parens=True,
|
|
607
|
+
lowercase=lowercase,
|
|
608
|
+
nodeclass=nodeclass,
|
|
609
|
+
innernodeclass=innernodeclass,
|
|
610
|
+
warn_dangling=warn_dangling,
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
def update_title_and_target(self, title: str, target: str) -> tuple[str, str]:
|
|
614
|
+
return f'@{title}', target
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def filter_meta_fields(
|
|
618
|
+
app: Sphinx, domain: str, objtype: str, content: Element
|
|
619
|
+
) -> None:
|
|
537
620
|
"""Filter ``:meta:`` field from its docstring."""
|
|
538
621
|
if domain != 'py':
|
|
539
622
|
return
|
|
540
623
|
|
|
541
624
|
for node in content:
|
|
542
625
|
if isinstance(node, nodes.field_list):
|
|
543
|
-
fields = cast(list[nodes.field], node)
|
|
626
|
+
fields = cast('list[nodes.field]', node)
|
|
544
627
|
# removing list items while iterating the list needs reversed()
|
|
545
628
|
for field in reversed(fields):
|
|
546
|
-
field_name = cast(nodes.field_body, field[0]).astext().strip()
|
|
629
|
+
field_name = cast('nodes.field_body', field[0]).astext().strip()
|
|
547
630
|
if field_name == 'meta' or field_name.startswith('meta '):
|
|
548
631
|
node.remove(field)
|
|
549
632
|
|
|
550
633
|
|
|
551
634
|
class PythonModuleIndex(Index):
|
|
552
|
-
"""
|
|
553
|
-
Index subclass to provide the Python module index.
|
|
554
|
-
"""
|
|
635
|
+
"""Index subclass to provide the Python module index."""
|
|
555
636
|
|
|
556
637
|
name = 'modindex'
|
|
557
638
|
localname = _('Python Module Index')
|
|
558
639
|
shortname = _('modules')
|
|
640
|
+
domain: PythonDomain
|
|
641
|
+
|
|
642
|
+
def generate(
|
|
643
|
+
self, docnames: Iterable[str] | None = None
|
|
644
|
+
) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
|
|
645
|
+
doc_names = frozenset(docnames) if docnames is not None else None
|
|
559
646
|
|
|
560
|
-
def generate(self, docnames: Iterable[str] | None = None,
|
|
561
|
-
) -> tuple[list[tuple[str, list[IndexEntry]]], bool]:
|
|
562
647
|
content: dict[str, list[IndexEntry]] = {}
|
|
563
648
|
# list of prefixes to ignore
|
|
564
|
-
ignores: list[str] =
|
|
565
|
-
|
|
649
|
+
ignores: list[str] = sorted(
|
|
650
|
+
self.domain.env.config['modindex_common_prefix'], key=len, reverse=True
|
|
651
|
+
)
|
|
652
|
+
|
|
566
653
|
# list of all modules, sorted by module name
|
|
567
|
-
modules = sorted(self.domain.
|
|
568
|
-
|
|
654
|
+
modules = sorted(self.domain.modules.items(), key=lambda t: t[0].lower())
|
|
655
|
+
|
|
569
656
|
# sort out collapsible modules
|
|
570
657
|
prev_modname = ''
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
658
|
+
|
|
659
|
+
num_top_levels = 0
|
|
660
|
+
for modname, module in modules:
|
|
661
|
+
if doc_names and module.docname not in doc_names:
|
|
574
662
|
continue
|
|
575
663
|
|
|
576
664
|
for ignore in ignores:
|
|
577
665
|
if modname.startswith(ignore):
|
|
578
|
-
modname = modname
|
|
666
|
+
modname = modname.removeprefix(ignore)
|
|
579
667
|
stripped = ignore
|
|
580
668
|
break
|
|
581
669
|
else:
|
|
@@ -587,32 +675,55 @@ class PythonModuleIndex(Index):
|
|
|
587
675
|
|
|
588
676
|
entries = content.setdefault(modname[0].lower(), [])
|
|
589
677
|
|
|
590
|
-
package = modname.split('.')[0]
|
|
678
|
+
package = modname.split('.', maxsplit=1)[0]
|
|
591
679
|
if package != modname:
|
|
592
680
|
# it's a submodule
|
|
593
681
|
if prev_modname == package:
|
|
594
682
|
# first submodule - make parent a group head
|
|
595
683
|
if entries:
|
|
596
684
|
last = entries[-1]
|
|
597
|
-
entries[-1] = IndexEntry(
|
|
598
|
-
|
|
685
|
+
entries[-1] = IndexEntry(
|
|
686
|
+
name=last.name,
|
|
687
|
+
subtype=1,
|
|
688
|
+
docname=last.docname,
|
|
689
|
+
anchor=last.anchor,
|
|
690
|
+
extra=last.extra,
|
|
691
|
+
qualifier=last.qualifier,
|
|
692
|
+
descr=last.descr,
|
|
693
|
+
)
|
|
599
694
|
elif not prev_modname.startswith(package):
|
|
600
695
|
# submodule without parent in list, add dummy entry
|
|
601
|
-
|
|
696
|
+
dummy_entry = IndexEntry(
|
|
697
|
+
name=stripped + package,
|
|
698
|
+
subtype=1,
|
|
699
|
+
docname='',
|
|
700
|
+
anchor='',
|
|
701
|
+
extra='',
|
|
702
|
+
qualifier='',
|
|
703
|
+
descr='',
|
|
704
|
+
)
|
|
705
|
+
entries.append(dummy_entry)
|
|
602
706
|
subtype = 2
|
|
603
707
|
else:
|
|
604
|
-
|
|
708
|
+
num_top_levels += 1
|
|
605
709
|
subtype = 0
|
|
606
710
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
711
|
+
entry = IndexEntry(
|
|
712
|
+
name=stripped + modname,
|
|
713
|
+
subtype=subtype,
|
|
714
|
+
docname=module.docname,
|
|
715
|
+
anchor=module.node_id,
|
|
716
|
+
extra=module.platform,
|
|
717
|
+
qualifier=_('Deprecated') if module.deprecated else '',
|
|
718
|
+
descr=module.synopsis,
|
|
719
|
+
)
|
|
720
|
+
entries.append(entry)
|
|
610
721
|
prev_modname = modname
|
|
611
722
|
|
|
612
723
|
# apply heuristics when to collapse modindex at page load:
|
|
613
724
|
# only collapse if number of toplevel modules is larger than
|
|
614
725
|
# number of submodules
|
|
615
|
-
collapse = len(modules) -
|
|
726
|
+
collapse = len(modules) - num_top_levels < num_top_levels
|
|
616
727
|
|
|
617
728
|
# sort by first letter
|
|
618
729
|
sorted_content = sorted(content.items())
|
|
@@ -626,46 +737,47 @@ class PythonDomain(Domain):
|
|
|
626
737
|
name = 'py'
|
|
627
738
|
label = 'Python'
|
|
628
739
|
object_types: dict[str, ObjType] = {
|
|
629
|
-
'function':
|
|
630
|
-
'data':
|
|
631
|
-
'class':
|
|
632
|
-
'exception':
|
|
633
|
-
'method':
|
|
634
|
-
'classmethod':
|
|
740
|
+
'function': ObjType(_('function'), 'func', 'obj'),
|
|
741
|
+
'data': ObjType(_('data'), 'data', 'obj'),
|
|
742
|
+
'class': ObjType(_('class'), 'class', 'exc', 'obj'),
|
|
743
|
+
'exception': ObjType(_('exception'), 'exc', 'class', 'obj'),
|
|
744
|
+
'method': ObjType(_('method'), 'meth', 'obj'),
|
|
745
|
+
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
|
635
746
|
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
|
636
|
-
'attribute':
|
|
637
|
-
'property':
|
|
638
|
-
'type':
|
|
639
|
-
'module':
|
|
747
|
+
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
|
748
|
+
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
|
|
749
|
+
'type': ObjType(_('type alias'), 'type', 'obj'),
|
|
750
|
+
'module': ObjType(_('module'), 'mod', 'obj'),
|
|
640
751
|
}
|
|
641
752
|
|
|
642
753
|
directives = {
|
|
643
|
-
'function':
|
|
644
|
-
'data':
|
|
645
|
-
'class':
|
|
646
|
-
'exception':
|
|
647
|
-
'method':
|
|
648
|
-
'classmethod':
|
|
649
|
-
'staticmethod':
|
|
650
|
-
'attribute':
|
|
651
|
-
'property':
|
|
652
|
-
'type':
|
|
653
|
-
'module':
|
|
654
|
-
'currentmodule':
|
|
655
|
-
'decorator':
|
|
754
|
+
'function': PyFunction,
|
|
755
|
+
'data': PyVariable,
|
|
756
|
+
'class': PyClasslike,
|
|
757
|
+
'exception': PyClasslike,
|
|
758
|
+
'method': PyMethod,
|
|
759
|
+
'classmethod': PyClassMethod,
|
|
760
|
+
'staticmethod': PyStaticMethod,
|
|
761
|
+
'attribute': PyAttribute,
|
|
762
|
+
'property': PyProperty,
|
|
763
|
+
'type': PyTypeAlias,
|
|
764
|
+
'module': PyModule,
|
|
765
|
+
'currentmodule': PyCurrentModule,
|
|
766
|
+
'decorator': PyDecoratorFunction,
|
|
656
767
|
'decoratormethod': PyDecoratorMethod,
|
|
657
768
|
}
|
|
658
769
|
roles = {
|
|
659
|
-
'data':
|
|
660
|
-
'exc':
|
|
661
|
-
'func':
|
|
770
|
+
'data': PyXRefRole(),
|
|
771
|
+
'exc': PyXRefRole(),
|
|
772
|
+
'func': PyXRefRole(fix_parens=True),
|
|
773
|
+
'deco': _PyDecoXRefRole(),
|
|
662
774
|
'class': PyXRefRole(),
|
|
663
775
|
'const': PyXRefRole(),
|
|
664
|
-
'attr':
|
|
665
|
-
'type':
|
|
666
|
-
'meth':
|
|
667
|
-
'mod':
|
|
668
|
-
'obj':
|
|
776
|
+
'attr': PyXRefRole(),
|
|
777
|
+
'type': PyXRefRole(),
|
|
778
|
+
'meth': PyXRefRole(fix_parens=True),
|
|
779
|
+
'mod': PyXRefRole(),
|
|
780
|
+
'obj': PyXRefRole(),
|
|
669
781
|
}
|
|
670
782
|
initial_data: dict[str, dict[str, tuple[Any]]] = {
|
|
671
783
|
'objects': {}, # fullname -> docname, objtype
|
|
@@ -679,8 +791,14 @@ class PythonDomain(Domain):
|
|
|
679
791
|
def objects(self) -> dict[str, ObjectEntry]:
|
|
680
792
|
return self.data.setdefault('objects', {}) # fullname -> ObjectEntry
|
|
681
793
|
|
|
682
|
-
def note_object(
|
|
683
|
-
|
|
794
|
+
def note_object(
|
|
795
|
+
self,
|
|
796
|
+
name: str,
|
|
797
|
+
objtype: str,
|
|
798
|
+
node_id: str,
|
|
799
|
+
aliased: bool = False,
|
|
800
|
+
location: Any = None,
|
|
801
|
+
) -> None:
|
|
684
802
|
"""Note a python object for cross reference.
|
|
685
803
|
|
|
686
804
|
.. versionadded:: 2.1
|
|
@@ -695,31 +813,47 @@ class PythonDomain(Domain):
|
|
|
695
813
|
return
|
|
696
814
|
else:
|
|
697
815
|
# duplicated
|
|
698
|
-
logger.warning(
|
|
699
|
-
|
|
700
|
-
|
|
816
|
+
logger.warning(
|
|
817
|
+
__(
|
|
818
|
+
'duplicate object description of %s, '
|
|
819
|
+
'other instance in %s, use :no-index: for one of them'
|
|
820
|
+
),
|
|
821
|
+
name,
|
|
822
|
+
other.docname,
|
|
823
|
+
location=location,
|
|
824
|
+
)
|
|
701
825
|
self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, aliased)
|
|
702
826
|
|
|
703
827
|
@property
|
|
704
828
|
def modules(self) -> dict[str, ModuleEntry]:
|
|
705
829
|
return self.data.setdefault('modules', {}) # modname -> ModuleEntry
|
|
706
830
|
|
|
707
|
-
def note_module(
|
|
708
|
-
|
|
831
|
+
def note_module(
|
|
832
|
+
self, name: str, node_id: str, synopsis: str, platform: str, deprecated: bool
|
|
833
|
+
) -> None:
|
|
709
834
|
"""Note a python module for cross reference.
|
|
710
835
|
|
|
711
836
|
.. versionadded:: 2.1
|
|
712
837
|
"""
|
|
713
|
-
self.modules[name] = ModuleEntry(
|
|
714
|
-
|
|
838
|
+
self.modules[name] = ModuleEntry(
|
|
839
|
+
docname=self.env.docname,
|
|
840
|
+
node_id=node_id,
|
|
841
|
+
synopsis=synopsis,
|
|
842
|
+
platform=platform,
|
|
843
|
+
deprecated=deprecated,
|
|
844
|
+
)
|
|
715
845
|
|
|
716
846
|
def clear_doc(self, docname: str) -> None:
|
|
717
|
-
|
|
718
|
-
if obj.docname == docname
|
|
719
|
-
|
|
720
|
-
for
|
|
721
|
-
|
|
722
|
-
|
|
847
|
+
to_remove = [
|
|
848
|
+
fullname for fullname, obj in self.objects.items() if obj.docname == docname
|
|
849
|
+
]
|
|
850
|
+
for fullname in to_remove:
|
|
851
|
+
del self.objects[fullname]
|
|
852
|
+
to_remove = [
|
|
853
|
+
modname for modname, mod in self.modules.items() if mod.docname == docname
|
|
854
|
+
]
|
|
855
|
+
for fullname in to_remove:
|
|
856
|
+
del self.modules[fullname]
|
|
723
857
|
|
|
724
858
|
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
|
|
725
859
|
# XXX check duplicates?
|
|
@@ -730,9 +864,15 @@ class PythonDomain(Domain):
|
|
|
730
864
|
if mod.docname in docnames:
|
|
731
865
|
self.modules[modname] = mod
|
|
732
866
|
|
|
733
|
-
def find_obj(
|
|
734
|
-
|
|
735
|
-
|
|
867
|
+
def find_obj(
|
|
868
|
+
self,
|
|
869
|
+
env: BuildEnvironment,
|
|
870
|
+
modname: str,
|
|
871
|
+
classname: str,
|
|
872
|
+
name: str,
|
|
873
|
+
type: str | None,
|
|
874
|
+
searchmode: int = 0,
|
|
875
|
+
) -> list[tuple[str, ObjectEntry]]:
|
|
736
876
|
"""Find a Python object for "name", perhaps using the given module
|
|
737
877
|
and/or classname. Returns a list of (name, object entry) tuples.
|
|
738
878
|
"""
|
|
@@ -753,20 +893,31 @@ class PythonDomain(Domain):
|
|
|
753
893
|
if objtypes is not None:
|
|
754
894
|
if modname and classname:
|
|
755
895
|
fullname = modname + '.' + classname + '.' + name
|
|
756
|
-
if
|
|
896
|
+
if (
|
|
897
|
+
fullname in self.objects
|
|
898
|
+
and self.objects[fullname].objtype in objtypes
|
|
899
|
+
):
|
|
757
900
|
newname = fullname
|
|
758
901
|
if not newname:
|
|
759
|
-
if
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
902
|
+
if (
|
|
903
|
+
modname
|
|
904
|
+
and f'{modname}.{name}' in self.objects
|
|
905
|
+
and self.objects[f'{modname}.{name}'].objtype in objtypes
|
|
906
|
+
):
|
|
907
|
+
newname = f'{modname}.{name}'
|
|
908
|
+
elif (
|
|
909
|
+
name in self.objects and self.objects[name].objtype in objtypes
|
|
910
|
+
):
|
|
763
911
|
newname = name
|
|
764
912
|
else:
|
|
765
913
|
# "fuzzy" searching mode
|
|
766
|
-
searchname = '.'
|
|
767
|
-
matches = [
|
|
768
|
-
|
|
769
|
-
|
|
914
|
+
searchname = f'.{name}'
|
|
915
|
+
matches = [
|
|
916
|
+
(oname, self.objects[oname])
|
|
917
|
+
for oname in self.objects
|
|
918
|
+
if oname.endswith(searchname)
|
|
919
|
+
and self.objects[oname].objtype in objtypes
|
|
920
|
+
]
|
|
770
921
|
else:
|
|
771
922
|
# NOTE: searching for exact match, object type is not considered
|
|
772
923
|
if name in self.objects:
|
|
@@ -778,21 +929,30 @@ class PythonDomain(Domain):
|
|
|
778
929
|
newname = classname + '.' + name
|
|
779
930
|
elif modname and modname + '.' + name in self.objects:
|
|
780
931
|
newname = modname + '.' + name
|
|
781
|
-
elif
|
|
782
|
-
|
|
932
|
+
elif (
|
|
933
|
+
modname
|
|
934
|
+
and classname
|
|
935
|
+
and modname + '.' + classname + '.' + name in self.objects
|
|
936
|
+
):
|
|
783
937
|
newname = modname + '.' + classname + '.' + name
|
|
784
938
|
if newname is not None:
|
|
785
939
|
matches.append((newname, self.objects[newname]))
|
|
786
940
|
return matches
|
|
787
941
|
|
|
788
|
-
def resolve_xref(
|
|
789
|
-
|
|
790
|
-
|
|
942
|
+
def resolve_xref(
|
|
943
|
+
self,
|
|
944
|
+
env: BuildEnvironment,
|
|
945
|
+
fromdocname: str,
|
|
946
|
+
builder: Builder,
|
|
947
|
+
type: str,
|
|
948
|
+
target: str,
|
|
949
|
+
node: pending_xref,
|
|
950
|
+
contnode: Element,
|
|
951
|
+
) -> nodes.reference | None:
|
|
791
952
|
modname = node.get('py:module')
|
|
792
953
|
clsname = node.get('py:class')
|
|
793
954
|
searchmode = 1 if node.hasattr('refspecific') else 0
|
|
794
|
-
matches = self.find_obj(env, modname, clsname, target,
|
|
795
|
-
type, searchmode)
|
|
955
|
+
matches = self.find_obj(env, modname, clsname, target, type, searchmode)
|
|
796
956
|
|
|
797
957
|
if not matches and type == 'attr':
|
|
798
958
|
# fallback to meth (for property; Sphinx 2.4.x)
|
|
@@ -814,9 +974,14 @@ class PythonDomain(Domain):
|
|
|
814
974
|
if len(canonicals) == 1:
|
|
815
975
|
matches = canonicals
|
|
816
976
|
else:
|
|
817
|
-
logger.warning(
|
|
818
|
-
|
|
819
|
-
|
|
977
|
+
logger.warning(
|
|
978
|
+
__('more than one target found for cross-reference %r: %s'),
|
|
979
|
+
target,
|
|
980
|
+
', '.join(match[0] for match in matches),
|
|
981
|
+
type='ref',
|
|
982
|
+
subtype='python',
|
|
983
|
+
location=node,
|
|
984
|
+
)
|
|
820
985
|
name, obj = matches[0]
|
|
821
986
|
|
|
822
987
|
if obj[2] == 'module':
|
|
@@ -832,27 +997,33 @@ class PythonDomain(Domain):
|
|
|
832
997
|
|
|
833
998
|
return make_refnode(builder, fromdocname, obj[0], obj[1], children, name)
|
|
834
999
|
|
|
835
|
-
def resolve_any_xref(
|
|
836
|
-
|
|
837
|
-
|
|
1000
|
+
def resolve_any_xref(
|
|
1001
|
+
self,
|
|
1002
|
+
env: BuildEnvironment,
|
|
1003
|
+
fromdocname: str,
|
|
1004
|
+
builder: Builder,
|
|
1005
|
+
target: str,
|
|
1006
|
+
node: pending_xref,
|
|
1007
|
+
contnode: Element,
|
|
1008
|
+
) -> list[tuple[str, nodes.reference]]:
|
|
838
1009
|
modname = node.get('py:module')
|
|
839
1010
|
clsname = node.get('py:class')
|
|
840
|
-
results: list[tuple[str,
|
|
1011
|
+
results: list[tuple[str, nodes.reference]] = []
|
|
841
1012
|
|
|
842
1013
|
# always search in "refspecific" mode with the :any: role
|
|
843
1014
|
matches = self.find_obj(env, modname, clsname, target, None, 1)
|
|
844
1015
|
multiple_matches = len(matches) > 1
|
|
845
1016
|
|
|
846
1017
|
for name, obj in matches:
|
|
847
|
-
|
|
848
1018
|
if multiple_matches and obj.aliased:
|
|
849
1019
|
# Skip duplicated matches
|
|
850
1020
|
continue
|
|
851
1021
|
|
|
852
1022
|
if obj[2] == 'module':
|
|
853
|
-
results.append((
|
|
854
|
-
|
|
855
|
-
|
|
1023
|
+
results.append((
|
|
1024
|
+
'py:mod',
|
|
1025
|
+
self._make_module_refnode(builder, fromdocname, name, contnode),
|
|
1026
|
+
))
|
|
856
1027
|
else:
|
|
857
1028
|
# determine the content of the reference by conditions
|
|
858
1029
|
content = find_pending_xref_condition(node, 'resolved')
|
|
@@ -863,34 +1034,39 @@ class PythonDomain(Domain):
|
|
|
863
1034
|
children = [contnode]
|
|
864
1035
|
|
|
865
1036
|
role = 'py:' + self.role_for_objtype(obj[2]) # type: ignore[operator]
|
|
866
|
-
results.append((
|
|
867
|
-
|
|
1037
|
+
results.append((
|
|
1038
|
+
role,
|
|
1039
|
+
make_refnode(builder, fromdocname, obj[0], obj[1], children, name),
|
|
1040
|
+
))
|
|
868
1041
|
return results
|
|
869
1042
|
|
|
870
|
-
def _make_module_refnode(
|
|
871
|
-
|
|
1043
|
+
def _make_module_refnode(
|
|
1044
|
+
self, builder: Builder, fromdocname: str, name: str, contnode: Node
|
|
1045
|
+
) -> nodes.reference:
|
|
872
1046
|
# get additional info for modules
|
|
873
|
-
module = self.modules[name]
|
|
874
|
-
|
|
1047
|
+
module: ModuleEntry = self.modules[name]
|
|
1048
|
+
title_parts = [name]
|
|
875
1049
|
if module.synopsis:
|
|
876
|
-
|
|
1050
|
+
title_parts.append(f': {module.synopsis}')
|
|
877
1051
|
if module.deprecated:
|
|
878
|
-
|
|
1052
|
+
title_parts.append(_(' (deprecated)'))
|
|
879
1053
|
if module.platform:
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1054
|
+
title_parts.append(f' ({module.platform})')
|
|
1055
|
+
title = ''.join(title_parts)
|
|
1056
|
+
return make_refnode(
|
|
1057
|
+
builder, fromdocname, module.docname, module.node_id, contnode, title
|
|
1058
|
+
)
|
|
883
1059
|
|
|
884
1060
|
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
|
|
885
1061
|
for modname, mod in self.modules.items():
|
|
886
|
-
yield
|
|
1062
|
+
yield modname, modname, 'module', mod.docname, mod.node_id, 0
|
|
887
1063
|
for refname, obj in self.objects.items():
|
|
888
1064
|
if obj.objtype != 'module': # modules are already handled
|
|
889
1065
|
if obj.aliased:
|
|
890
1066
|
# aliased names are not full-text searchable.
|
|
891
|
-
yield
|
|
1067
|
+
yield refname, refname, obj.objtype, obj.docname, obj.node_id, -1
|
|
892
1068
|
else:
|
|
893
|
-
yield
|
|
1069
|
+
yield refname, refname, obj.objtype, obj.docname, obj.node_id, 1
|
|
894
1070
|
|
|
895
1071
|
def get_full_qualified_name(self, node: Element) -> str | None:
|
|
896
1072
|
modname = node.get('py:module')
|
|
@@ -902,9 +1078,11 @@ class PythonDomain(Domain):
|
|
|
902
1078
|
return '.'.join(filter(None, [modname, clsname, target]))
|
|
903
1079
|
|
|
904
1080
|
|
|
905
|
-
def builtin_resolver(
|
|
906
|
-
|
|
1081
|
+
def builtin_resolver(
|
|
1082
|
+
app: Sphinx, env: BuildEnvironment, node: pending_xref, contnode: Element
|
|
1083
|
+
) -> Element | None:
|
|
907
1084
|
"""Do not emit nitpicky warnings for built-in types."""
|
|
1085
|
+
|
|
908
1086
|
def istyping(s: str) -> bool:
|
|
909
1087
|
if s.startswith('typing.'):
|
|
910
1088
|
s = s.split('.', 1)[1]
|
|
@@ -913,9 +1091,9 @@ def builtin_resolver(app: Sphinx, env: BuildEnvironment,
|
|
|
913
1091
|
|
|
914
1092
|
if node.get('refdomain') != 'py':
|
|
915
1093
|
return None
|
|
916
|
-
elif node.get('reftype') in
|
|
1094
|
+
elif node.get('reftype') in {'class', 'obj'} and node.get('reftarget') == 'None':
|
|
917
1095
|
return contnode
|
|
918
|
-
elif node.get('reftype') in
|
|
1096
|
+
elif node.get('reftype') in {'class', 'obj', 'exc'}:
|
|
919
1097
|
reftarget = node.get('reftarget')
|
|
920
1098
|
if inspect.isclass(getattr(builtins, reftarget, None)):
|
|
921
1099
|
# built-in class
|
|
@@ -931,11 +1109,24 @@ def setup(app: Sphinx) -> ExtensionMetadata:
|
|
|
931
1109
|
app.setup_extension('sphinx.directives')
|
|
932
1110
|
|
|
933
1111
|
app.add_domain(PythonDomain)
|
|
934
|
-
app.add_config_value('python_use_unqualified_type_names', False, 'env')
|
|
935
1112
|
app.add_config_value(
|
|
936
|
-
'
|
|
1113
|
+
'python_use_unqualified_type_names', False, 'env', types=frozenset({bool})
|
|
1114
|
+
)
|
|
1115
|
+
app.add_config_value(
|
|
1116
|
+
'python_maximum_signature_line_length',
|
|
1117
|
+
None,
|
|
1118
|
+
'env',
|
|
1119
|
+
types=frozenset({int, NoneType}),
|
|
1120
|
+
)
|
|
1121
|
+
app.add_config_value(
|
|
1122
|
+
'python_trailing_comma_in_multi_line_signatures',
|
|
1123
|
+
True,
|
|
1124
|
+
'env',
|
|
1125
|
+
types=frozenset({bool}),
|
|
1126
|
+
)
|
|
1127
|
+
app.add_config_value(
|
|
1128
|
+
'python_display_short_literal_types', False, 'env', types=frozenset({bool})
|
|
937
1129
|
)
|
|
938
|
-
app.add_config_value('python_display_short_literal_types', False, 'env')
|
|
939
1130
|
app.connect('object-description-transform', filter_meta_fields)
|
|
940
1131
|
app.connect('missing-reference', builtin_resolver, priority=900)
|
|
941
1132
|
|