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.

Files changed (193) hide show
  1. sphinx/__init__.py +8 -4
  2. sphinx/__main__.py +2 -0
  3. sphinx/_cli/__init__.py +2 -5
  4. sphinx/_cli/util/colour.py +34 -11
  5. sphinx/_cli/util/errors.py +128 -61
  6. sphinx/addnodes.py +51 -35
  7. sphinx/application.py +362 -230
  8. sphinx/builders/__init__.py +87 -64
  9. sphinx/builders/_epub_base.py +65 -56
  10. sphinx/builders/changes.py +17 -23
  11. sphinx/builders/dirhtml.py +8 -13
  12. sphinx/builders/epub3.py +70 -38
  13. sphinx/builders/gettext.py +93 -73
  14. sphinx/builders/html/__init__.py +240 -186
  15. sphinx/builders/html/_assets.py +9 -2
  16. sphinx/builders/html/_build_info.py +3 -0
  17. sphinx/builders/latex/__init__.py +64 -54
  18. sphinx/builders/latex/constants.py +14 -11
  19. sphinx/builders/latex/nodes.py +2 -0
  20. sphinx/builders/latex/theming.py +8 -9
  21. sphinx/builders/latex/transforms.py +7 -5
  22. sphinx/builders/linkcheck.py +193 -149
  23. sphinx/builders/manpage.py +17 -17
  24. sphinx/builders/singlehtml.py +28 -16
  25. sphinx/builders/texinfo.py +28 -21
  26. sphinx/builders/text.py +10 -15
  27. sphinx/builders/xml.py +10 -19
  28. sphinx/cmd/build.py +49 -119
  29. sphinx/cmd/make_mode.py +35 -31
  30. sphinx/cmd/quickstart.py +78 -62
  31. sphinx/config.py +265 -163
  32. sphinx/directives/__init__.py +51 -54
  33. sphinx/directives/admonitions.py +107 -0
  34. sphinx/directives/code.py +24 -19
  35. sphinx/directives/other.py +21 -42
  36. sphinx/directives/patches.py +28 -16
  37. sphinx/domains/__init__.py +54 -31
  38. sphinx/domains/_domains_container.py +22 -17
  39. sphinx/domains/_index.py +5 -8
  40. sphinx/domains/c/__init__.py +366 -245
  41. sphinx/domains/c/_ast.py +378 -256
  42. sphinx/domains/c/_ids.py +89 -31
  43. sphinx/domains/c/_parser.py +283 -214
  44. sphinx/domains/c/_symbol.py +269 -198
  45. sphinx/domains/changeset.py +39 -24
  46. sphinx/domains/citation.py +54 -24
  47. sphinx/domains/cpp/__init__.py +517 -362
  48. sphinx/domains/cpp/_ast.py +999 -682
  49. sphinx/domains/cpp/_ids.py +133 -65
  50. sphinx/domains/cpp/_parser.py +746 -588
  51. sphinx/domains/cpp/_symbol.py +692 -489
  52. sphinx/domains/index.py +10 -8
  53. sphinx/domains/javascript.py +152 -74
  54. sphinx/domains/math.py +48 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +114 -57
  57. sphinx/domains/python/_object.py +151 -67
  58. sphinx/domains/rst.py +94 -49
  59. sphinx/domains/std/__init__.py +510 -249
  60. sphinx/environment/__init__.py +345 -61
  61. sphinx/environment/adapters/asset.py +7 -1
  62. sphinx/environment/adapters/indexentries.py +15 -20
  63. sphinx/environment/adapters/toctree.py +19 -9
  64. sphinx/environment/collectors/__init__.py +3 -1
  65. sphinx/environment/collectors/asset.py +18 -15
  66. sphinx/environment/collectors/dependencies.py +8 -10
  67. sphinx/environment/collectors/metadata.py +6 -4
  68. sphinx/environment/collectors/title.py +3 -1
  69. sphinx/environment/collectors/toctree.py +4 -4
  70. sphinx/errors.py +1 -3
  71. sphinx/events.py +4 -4
  72. sphinx/ext/apidoc/__init__.py +21 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_generate.py +356 -0
  76. sphinx/ext/apidoc/_shared.py +66 -0
  77. sphinx/ext/autodoc/__init__.py +829 -480
  78. sphinx/ext/autodoc/directive.py +57 -21
  79. sphinx/ext/autodoc/importer.py +184 -67
  80. sphinx/ext/autodoc/mock.py +25 -10
  81. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  82. sphinx/ext/autodoc/type_comment.py +56 -29
  83. sphinx/ext/autodoc/typehints.py +49 -26
  84. sphinx/ext/autosectionlabel.py +28 -11
  85. sphinx/ext/autosummary/__init__.py +271 -143
  86. sphinx/ext/autosummary/generate.py +121 -51
  87. sphinx/ext/coverage.py +152 -91
  88. sphinx/ext/doctest.py +169 -101
  89. sphinx/ext/duration.py +12 -6
  90. sphinx/ext/extlinks.py +33 -21
  91. sphinx/ext/githubpages.py +8 -8
  92. sphinx/ext/graphviz.py +175 -109
  93. sphinx/ext/ifconfig.py +11 -6
  94. sphinx/ext/imgconverter.py +48 -25
  95. sphinx/ext/imgmath.py +127 -97
  96. sphinx/ext/inheritance_diagram.py +177 -103
  97. sphinx/ext/intersphinx/__init__.py +22 -13
  98. sphinx/ext/intersphinx/__main__.py +3 -1
  99. sphinx/ext/intersphinx/_cli.py +18 -14
  100. sphinx/ext/intersphinx/_load.py +91 -82
  101. sphinx/ext/intersphinx/_resolve.py +108 -74
  102. sphinx/ext/intersphinx/_shared.py +2 -2
  103. sphinx/ext/linkcode.py +28 -12
  104. sphinx/ext/mathjax.py +60 -29
  105. sphinx/ext/napoleon/__init__.py +19 -7
  106. sphinx/ext/napoleon/docstring.py +229 -231
  107. sphinx/ext/todo.py +44 -49
  108. sphinx/ext/viewcode.py +105 -57
  109. sphinx/extension.py +3 -1
  110. sphinx/highlighting.py +13 -7
  111. sphinx/io.py +9 -13
  112. sphinx/jinja2glue.py +29 -26
  113. sphinx/locale/__init__.py +8 -9
  114. sphinx/parsers.py +8 -7
  115. sphinx/project.py +2 -2
  116. sphinx/pycode/__init__.py +31 -21
  117. sphinx/pycode/ast.py +6 -3
  118. sphinx/pycode/parser.py +14 -8
  119. sphinx/pygments_styles.py +4 -5
  120. sphinx/registry.py +192 -92
  121. sphinx/roles.py +58 -7
  122. sphinx/search/__init__.py +75 -54
  123. sphinx/search/en.py +11 -13
  124. sphinx/search/fi.py +1 -1
  125. sphinx/search/ja.py +8 -6
  126. sphinx/search/nl.py +1 -1
  127. sphinx/search/zh.py +19 -21
  128. sphinx/testing/fixtures.py +26 -29
  129. sphinx/testing/path.py +26 -62
  130. sphinx/testing/restructuredtext.py +14 -8
  131. sphinx/testing/util.py +21 -19
  132. sphinx/texinputs/make.bat.jinja +50 -50
  133. sphinx/texinputs/sphinx.sty +4 -3
  134. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  135. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  136. sphinx/themes/basic/static/searchtools.js +8 -5
  137. sphinx/theming.py +49 -61
  138. sphinx/transforms/__init__.py +17 -38
  139. sphinx/transforms/compact_bullet_list.py +5 -3
  140. sphinx/transforms/i18n.py +8 -21
  141. sphinx/transforms/post_transforms/__init__.py +142 -93
  142. sphinx/transforms/post_transforms/code.py +5 -5
  143. sphinx/transforms/post_transforms/images.py +28 -24
  144. sphinx/transforms/references.py +3 -1
  145. sphinx/util/__init__.py +109 -60
  146. sphinx/util/_files.py +39 -23
  147. sphinx/util/_importer.py +4 -1
  148. sphinx/util/_inventory_file_reader.py +76 -0
  149. sphinx/util/_io.py +2 -2
  150. sphinx/util/_lines.py +6 -3
  151. sphinx/util/_pathlib.py +40 -2
  152. sphinx/util/build_phase.py +2 -0
  153. sphinx/util/cfamily.py +19 -14
  154. sphinx/util/console.py +44 -179
  155. sphinx/util/display.py +9 -10
  156. sphinx/util/docfields.py +140 -122
  157. sphinx/util/docstrings.py +1 -1
  158. sphinx/util/docutils.py +118 -77
  159. sphinx/util/fileutil.py +25 -26
  160. sphinx/util/http_date.py +2 -0
  161. sphinx/util/i18n.py +77 -64
  162. sphinx/util/images.py +8 -6
  163. sphinx/util/inspect.py +147 -38
  164. sphinx/util/inventory.py +215 -116
  165. sphinx/util/logging.py +33 -33
  166. sphinx/util/matching.py +12 -4
  167. sphinx/util/nodes.py +18 -13
  168. sphinx/util/osutil.py +38 -39
  169. sphinx/util/parallel.py +22 -13
  170. sphinx/util/parsing.py +2 -1
  171. sphinx/util/png.py +6 -2
  172. sphinx/util/requests.py +33 -2
  173. sphinx/util/rst.py +3 -2
  174. sphinx/util/tags.py +1 -1
  175. sphinx/util/template.py +18 -10
  176. sphinx/util/texescape.py +8 -6
  177. sphinx/util/typing.py +148 -122
  178. sphinx/versioning.py +3 -3
  179. sphinx/writers/html.py +3 -1
  180. sphinx/writers/html5.py +61 -50
  181. sphinx/writers/latex.py +80 -65
  182. sphinx/writers/manpage.py +19 -38
  183. sphinx/writers/texinfo.py +44 -45
  184. sphinx/writers/text.py +48 -30
  185. sphinx/writers/xml.py +11 -8
  186. {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/LICENSE.rst +1 -1
  187. {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/METADATA +23 -15
  188. {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/RECORD +190 -186
  189. {sphinx-8.1.3.dist-info → sphinx-8.2.0rc1.dist-info}/WHEEL +1 -1
  190. sphinx/builders/html/transforms.py +0 -90
  191. sphinx/ext/apidoc.py +0 -721
  192. sphinx/util/exceptions.py +0 -74
  193. {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 typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, cast
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
- # xref https://github.com/sphinx-doc/sphinx/issues/12295
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() # noqa: F821
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) -> list[nodes.Node]:
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
- return [addnodes.desc_sig_keyword('', 'async'),
91
- addnodes.desc_sig_space()]
92
- else:
93
- return []
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(self, name_cls: tuple[str, str], sig: str,
99
- signode: desc_signature) -> None:
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(typ, '',
151
- addnodes.desc_sig_punctuation('', ':'),
152
- addnodes.desc_sig_space(), *annotations)
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(value, '',
157
- addnodes.desc_sig_space(),
158
- addnodes.desc_sig_punctuation('', '='),
159
- addnodes.desc_sig_space(),
160
- nodes.Text(value))
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) -> list[nodes.Node]:
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
- return [nodes.Text('final'), addnodes.desc_sig_space(),
187
- nodes.Text(self.objtype), addnodes.desc_sig_space()]
188
- else:
189
- return [nodes.Text(self.objtype), addnodes.desc_sig_space()]
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) -> list[nodes.Node]:
218
- prefix: list[nodes.Node] = []
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.append(nodes.Text('final'))
221
- prefix.append(addnodes.desc_sig_space())
222
- if 'abstractmethod' in self.options:
223
- prefix.append(nodes.Text('abstract'))
224
- prefix.append(addnodes.desc_sig_space())
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.append(nodes.Text('async'))
227
- prefix.append(addnodes.desc_sig_space())
254
+ prefix.extend((
255
+ addnodes.desc_sig_keyword('', 'async'),
256
+ addnodes.desc_sig_space(),
257
+ ))
228
258
  if 'classmethod' in self.options:
229
- prefix.append(nodes.Text('classmethod'))
230
- prefix.append(addnodes.desc_sig_space())
259
+ prefix.extend((
260
+ addnodes.desc_sig_keyword('', 'classmethod'),
261
+ addnodes.desc_sig_space(),
262
+ ))
231
263
  if 'staticmethod' in self.options:
232
- prefix.append(nodes.Text('static'))
233
- prefix.append(addnodes.desc_sig_space())
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.env.config.add_module_names:
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(typ, '',
312
- addnodes.desc_sig_punctuation('', ':'),
313
- addnodes.desc_sig_space(),
314
- *annotations)
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(value, '',
319
- addnodes.desc_sig_space(),
320
- addnodes.desc_sig_punctuation('', '='),
321
- addnodes.desc_sig_space(),
322
- nodes.Text(value))
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.env.config.add_module_names:
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(typ, '',
358
- addnodes.desc_sig_punctuation('', ':'),
359
- addnodes.desc_sig_space(),
360
- *annotations)
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) -> list[nodes.Node]:
365
- prefix: list[nodes.Node] = []
366
- if 'abstractmethod' in self.options:
367
- prefix.append(nodes.Text('abstract'))
368
- prefix.append(addnodes.desc_sig_space())
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.append(nodes.Text('class'))
371
- prefix.append(addnodes.desc_sig_space())
372
-
373
- prefix.append(nodes.Text('property'))
374
- prefix.append(addnodes.desc_sig_space())
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.env.config.add_module_names:
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) -> list[nodes.Node]:
401
- return [nodes.Text('type'), addnodes.desc_sig_space()]
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.env.config.add_module_names:
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(modname,
475
- node_id,
476
- self.options.get('synopsis', ''),
477
- self.options.get('platform', ''),
478
- 'deprecated' in self.options)
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
- indextext = f'module; {modname}'
484
- inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)])
485
- # The node order is: index node first, then target node.
486
- ret.append(inode)
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(self, env: BuildEnvironment, refnode: Element,
515
- has_explicit_title: bool, title: str, target: str) -> tuple[str, str]:
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('.') # only has a meaning for the target
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
- def filter_meta_fields(app: Sphinx, domain: str, objtype: str, content: Element) -> None:
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] = self.domain.env.config['modindex_common_prefix']
565
- ignores = sorted(ignores, key=len, reverse=True)
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.data['modules'].items(),
568
- key=lambda x: x[0].lower())
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
- num_toplevels = 0
572
- for modname, (docname, node_id, synopsis, platforms, deprecated) in modules:
573
- if docnames and docname not in docnames:
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[len(ignore):]
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(last[0], 1, last[2], last[3],
598
- last[4], last[5], last[6])
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
- entries.append(IndexEntry(stripped + package, 1, '', '', '', '', ''))
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
- num_toplevels += 1
708
+ num_top_levels += 1
605
709
  subtype = 0
606
710
 
607
- qualifier = _('Deprecated') if deprecated else ''
608
- entries.append(IndexEntry(stripped + modname, subtype, docname,
609
- node_id, platforms, qualifier, synopsis))
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) - num_toplevels < num_toplevels
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': ObjType(_('function'), 'func', 'obj'),
630
- 'data': ObjType(_('data'), 'data', 'obj'),
631
- 'class': ObjType(_('class'), 'class', 'exc', 'obj'),
632
- 'exception': ObjType(_('exception'), 'exc', 'class', 'obj'),
633
- 'method': ObjType(_('method'), 'meth', 'obj'),
634
- 'classmethod': ObjType(_('class method'), 'meth', 'obj'),
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': ObjType(_('attribute'), 'attr', 'obj'),
637
- 'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
638
- 'type': ObjType(_('type alias'), 'type', 'obj'),
639
- 'module': ObjType(_('module'), 'mod', 'obj'),
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': PyFunction,
644
- 'data': PyVariable,
645
- 'class': PyClasslike,
646
- 'exception': PyClasslike,
647
- 'method': PyMethod,
648
- 'classmethod': PyClassMethod,
649
- 'staticmethod': PyStaticMethod,
650
- 'attribute': PyAttribute,
651
- 'property': PyProperty,
652
- 'type': PyTypeAlias,
653
- 'module': PyModule,
654
- 'currentmodule': PyCurrentModule,
655
- 'decorator': PyDecoratorFunction,
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': PyXRefRole(),
660
- 'exc': PyXRefRole(),
661
- 'func': PyXRefRole(fix_parens=True),
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': PyXRefRole(),
665
- 'type': PyXRefRole(),
666
- 'meth': PyXRefRole(fix_parens=True),
667
- 'mod': PyXRefRole(),
668
- 'obj': PyXRefRole(),
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(self, name: str, objtype: str, node_id: str,
683
- aliased: bool = False, location: Any = None) -> None:
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(__('duplicate object description of %s, '
699
- 'other instance in %s, use :no-index: for one of them'),
700
- name, other.docname, location=location)
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(self, name: str, node_id: str, synopsis: str,
708
- platform: str, deprecated: bool) -> None:
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(self.env.docname, node_id,
714
- synopsis, platform, deprecated)
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
- for fullname, obj in list(self.objects.items()):
718
- if obj.docname == docname:
719
- del self.objects[fullname]
720
- for modname, mod in list(self.modules.items()):
721
- if mod.docname == docname:
722
- del self.modules[modname]
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(self, env: BuildEnvironment, modname: str, classname: str,
734
- name: str, type: str | None, searchmode: int = 0,
735
- ) -> list[tuple[str, ObjectEntry]]:
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 fullname in self.objects and self.objects[fullname].objtype in objtypes:
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 modname and modname + '.' + name in self.objects and \
760
- self.objects[modname + '.' + name].objtype in objtypes:
761
- newname = modname + '.' + name
762
- elif name in self.objects and self.objects[name].objtype in objtypes:
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 = '.' + name
767
- matches = [(oname, self.objects[oname]) for oname in self.objects
768
- if oname.endswith(searchname) and
769
- self.objects[oname].objtype in objtypes]
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 modname and classname and \
782
- modname + '.' + classname + '.' + name in self.objects:
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(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
789
- type: str, target: str, node: pending_xref, contnode: Element,
790
- ) -> Element | None:
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(__('more than one target found for cross-reference %r: %s'),
818
- target, ', '.join(match[0] for match in matches),
819
- type='ref', subtype='python', location=node)
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(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
836
- target: str, node: pending_xref, contnode: Element,
837
- ) -> list[tuple[str, Element]]:
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, Element]] = []
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(('py:mod',
854
- self._make_module_refnode(builder, fromdocname,
855
- name, contnode)))
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((role, make_refnode(builder, fromdocname, obj[0], obj[1],
867
- children, name)))
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(self, builder: Builder, fromdocname: str, name: str,
871
- contnode: Node) -> Element:
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
- title = name
1047
+ module: ModuleEntry = self.modules[name]
1048
+ title_parts = [name]
875
1049
  if module.synopsis:
876
- title += ': ' + module.synopsis
1050
+ title_parts.append(f': {module.synopsis}')
877
1051
  if module.deprecated:
878
- title += _(' (deprecated)')
1052
+ title_parts.append(_(' (deprecated)'))
879
1053
  if module.platform:
880
- title += ' (' + module.platform + ')'
881
- return make_refnode(builder, fromdocname, module.docname, module.node_id,
882
- contnode, title)
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 (modname, modname, 'module', mod.docname, mod.node_id, 0)
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 (refname, refname, obj.objtype, obj.docname, obj.node_id, -1)
1067
+ yield refname, refname, obj.objtype, obj.docname, obj.node_id, -1
892
1068
  else:
893
- yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
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(app: Sphinx, env: BuildEnvironment,
906
- node: pending_xref, contnode: Element) -> Element | None:
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 ('class', 'obj') and node.get('reftarget') == 'None':
1094
+ elif node.get('reftype') in {'class', 'obj'} and node.get('reftarget') == 'None':
917
1095
  return contnode
918
- elif node.get('reftype') in ('class', 'obj', 'exc'):
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
- 'python_maximum_signature_line_length', None, 'env', {int, type(None)},
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