Sphinx 8.1.2__py3-none-any.whl → 8.2.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Sphinx might be problematic. Click here for more details.

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 +837 -483
  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.2.dist-info → sphinx-8.2.0rc1.dist-info}/LICENSE.rst +1 -1
  187. {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/METADATA +23 -15
  188. {sphinx-8.1.2.dist-info → sphinx-8.2.0rc1.dist-info}/RECORD +190 -186
  189. {sphinx-8.1.2.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.2.dist-info → sphinx-8.2.0rc1.dist-info}/entry_points.txt +0 -0
@@ -2,13 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import contextlib
4
4
  import re
5
- from typing import TYPE_CHECKING, ClassVar
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from docutils import nodes
8
8
  from docutils.parsers.rst import directives
9
9
 
10
10
  from sphinx import addnodes
11
- from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
11
+ from sphinx.addnodes import pending_xref, pending_xref_condition
12
12
  from sphinx.directives import ObjectDescription
13
13
  from sphinx.domains.python._annotations import (
14
14
  _parse_annotation,
@@ -25,9 +25,13 @@ from sphinx.util.nodes import (
25
25
  )
26
26
 
27
27
  if TYPE_CHECKING:
28
+ from collections.abc import Sequence
29
+ from typing import ClassVar
30
+
28
31
  from docutils.nodes import Node
29
32
  from docutils.parsers.rst.states import Inliner
30
33
 
34
+ from sphinx.addnodes import desc_signature
31
35
  from sphinx.environment import BuildEnvironment
32
36
  from sphinx.util.typing import OptionSpec, TextlikeNode
33
37
 
@@ -35,13 +39,15 @@ logger = logging.getLogger(__name__)
35
39
 
36
40
  # REs for Python signatures
37
41
  py_sig_re = re.compile(
38
- r'''^ ([\w.]*\.)? # class name(s)
42
+ r"""^ ([\w.]*\.)? # class name(s)
39
43
  (\w+) \s* # thing name
40
- (?: \[\s*(.*)\s*])? # optional: type parameters list
44
+ (?: \[\s*(.*?)\s*])? # optional: type parameters list
41
45
  (?: \(\s*(.*)\s*\) # optional: arguments
42
46
  (?:\s* -> \s* (.*))? # return annotation
43
47
  )? $ # and nothing more
44
- ''', re.VERBOSE)
48
+ """,
49
+ re.VERBOSE,
50
+ )
45
51
 
46
52
 
47
53
  # This override allows our inline type specifiers to behave like :class: link
@@ -60,9 +66,16 @@ class PyXrefMixin:
60
66
  ) -> Node:
61
67
  # we use inliner=None to make sure we get the old behaviour with a single
62
68
  # pending_xref node
63
- result = super().make_xref(rolename, domain, target, # type: ignore[misc]
64
- innernode, contnode,
65
- env, inliner=None, location=None)
69
+ result = super().make_xref( # type: ignore[misc]
70
+ rolename,
71
+ domain,
72
+ target,
73
+ innernode,
74
+ contnode,
75
+ env,
76
+ inliner=None,
77
+ location=None,
78
+ )
66
79
  if isinstance(result, pending_xref):
67
80
  assert env is not None
68
81
  result['refspecific'] = True
@@ -82,8 +95,10 @@ class PyXrefMixin:
82
95
 
83
96
  shortname = target.split('.')[-1]
84
97
  textnode = innernode('', shortname) # type: ignore[call-arg]
85
- contnodes = [pending_xref_condition('', '', textnode, condition='resolved'),
86
- pending_xref_condition('', '', *children, condition='*')]
98
+ contnodes = [
99
+ pending_xref_condition('', '', textnode, condition='resolved'),
100
+ pending_xref_condition('', '', *children, condition='*'),
101
+ ]
87
102
  result.extend(contnodes)
88
103
 
89
104
  return result
@@ -116,8 +131,18 @@ class PyXrefMixin:
116
131
  if in_literal or self._delimiters_re.match(sub_target):
117
132
  results.append(contnode or innernode(sub_target, sub_target)) # type: ignore[call-arg]
118
133
  else:
119
- results.append(self.make_xref(rolename, domain, sub_target,
120
- innernode, contnode, env, inliner, location))
134
+ results.append(
135
+ self.make_xref(
136
+ rolename,
137
+ domain,
138
+ sub_target,
139
+ innernode,
140
+ contnode,
141
+ env,
142
+ inliner,
143
+ location,
144
+ )
145
+ )
121
146
 
122
147
  if sub_target in {'Literal', 'typing.Literal', '~typing.Literal'}:
123
148
  in_literal = True
@@ -138,8 +163,7 @@ class PyTypedField(PyXrefMixin, TypedField):
138
163
 
139
164
 
140
165
  class PyObject(ObjectDescription[tuple[str, str]]):
141
- """
142
- Description of a general Python object.
166
+ """Description of a general Python object.
143
167
 
144
168
  :cvar allow_nesting: Class is an object that allows for nested namespaces
145
169
  :vartype allow_nesting: bool
@@ -161,27 +185,55 @@ class PyObject(ObjectDescription[tuple[str, str]]):
161
185
  }
162
186
 
163
187
  doc_field_types = [
164
- PyTypedField('parameter', label=_('Parameters'),
165
- names=('param', 'parameter', 'arg', 'argument',
166
- 'keyword', 'kwarg', 'kwparam'),
167
- typerolename='class', typenames=('paramtype', 'type'),
168
- can_collapse=True),
169
- PyTypedField('variable', label=_('Variables'),
170
- names=('var', 'ivar', 'cvar'),
171
- typerolename='class', typenames=('vartype',),
172
- can_collapse=True),
173
- PyGroupedField('exceptions', label=_('Raises'), rolename='exc',
174
- names=('raises', 'raise', 'exception', 'except'),
175
- can_collapse=True),
176
- Field('returnvalue', label=_('Returns'), has_arg=False,
177
- names=('returns', 'return')),
178
- PyField('returntype', label=_('Return type'), has_arg=False,
179
- names=('rtype',), bodyrolename='class'),
188
+ PyTypedField(
189
+ 'parameter',
190
+ label=_('Parameters'),
191
+ names=(
192
+ 'param',
193
+ 'parameter',
194
+ 'arg',
195
+ 'argument',
196
+ 'keyword',
197
+ 'kwarg',
198
+ 'kwparam',
199
+ ),
200
+ typerolename='class',
201
+ typenames=('paramtype', 'type'),
202
+ can_collapse=True,
203
+ ),
204
+ PyTypedField(
205
+ 'variable',
206
+ label=_('Variables'),
207
+ names=('var', 'ivar', 'cvar'),
208
+ typerolename='class',
209
+ typenames=('vartype',),
210
+ can_collapse=True,
211
+ ),
212
+ PyGroupedField(
213
+ 'exceptions',
214
+ label=_('Raises'),
215
+ rolename='exc',
216
+ names=('raises', 'raise', 'exception', 'except'),
217
+ can_collapse=True,
218
+ ),
219
+ Field(
220
+ 'returnvalue',
221
+ label=_('Returns'),
222
+ has_arg=False,
223
+ names=('returns', 'return'),
224
+ ),
225
+ PyField(
226
+ 'returntype',
227
+ label=_('Return type'),
228
+ has_arg=False,
229
+ names=('rtype',),
230
+ bodyrolename='class',
231
+ ),
180
232
  ]
181
233
 
182
234
  allow_nesting = False
183
235
 
184
- def get_signature_prefix(self, sig: str) -> list[nodes.Node]:
236
+ def get_signature_prefix(self, sig: str) -> Sequence[nodes.Node]:
185
237
  """May return a prefix to put before the object name in the
186
238
  signature.
187
239
  """
@@ -212,18 +264,17 @@ class PyObject(ObjectDescription[tuple[str, str]]):
212
264
  classname = self.env.ref_context.get('py:class')
213
265
  if classname:
214
266
  add_module = False
215
- if prefix and (prefix == classname or
216
- prefix.startswith(classname + ".")):
267
+ if prefix and (prefix == classname or prefix.startswith(f'{classname}.')):
217
268
  fullname = prefix + name
218
269
  # class name is given again in the signature
219
- prefix = prefix[len(classname):].lstrip('.')
270
+ prefix = prefix[len(classname) :].lstrip('.')
220
271
  elif prefix:
221
272
  # class name is given in the signature, but different
222
273
  # (shouldn't happen)
223
- fullname = classname + '.' + prefix + name
274
+ fullname = f'{classname}.{prefix}{name}'
224
275
  else:
225
276
  # class name is not given in the signature
226
- fullname = classname + '.' + name
277
+ fullname = f'{classname}.{name}'
227
278
  else:
228
279
  add_module = True
229
280
  if prefix:
@@ -237,9 +288,11 @@ class PyObject(ObjectDescription[tuple[str, str]]):
237
288
  signode['class'] = classname
238
289
  signode['fullname'] = fullname
239
290
 
240
- max_len = (self.env.config.python_maximum_signature_line_length
241
- or self.env.config.maximum_signature_line_length
242
- or 0)
291
+ max_len = (
292
+ self.config.python_maximum_signature_line_length
293
+ or self.config.maximum_signature_line_length
294
+ or 0
295
+ )
243
296
 
244
297
  # determine if the function arguments (without its type parameters)
245
298
  # should be formatted on a multiline or not by removing the width of
@@ -258,44 +311,69 @@ class PyObject(ObjectDescription[tuple[str, str]]):
258
311
  and (sig_len - (arglist_span[1] - arglist_span[0])) > max_len > 0
259
312
  )
260
313
 
314
+ trailing_comma = self.env.config.python_trailing_comma_in_multi_line_signatures
261
315
  sig_prefix = self.get_signature_prefix(sig)
262
316
  if sig_prefix:
263
317
  if type(sig_prefix) is str:
264
- msg = ("Python directive method get_signature_prefix()"
265
- " must return a list of nodes."
266
- f" Return value was '{sig_prefix}'.")
318
+ msg = (
319
+ 'Python directive method get_signature_prefix()'
320
+ ' must return a list of nodes.'
321
+ f" Return value was '{sig_prefix}'."
322
+ )
267
323
  raise TypeError(msg)
268
324
  signode += addnodes.desc_annotation(str(sig_prefix), '', *sig_prefix)
269
325
 
270
326
  if prefix:
271
327
  signode += addnodes.desc_addname(prefix, prefix)
272
- elif modname and add_module and self.env.config.add_module_names:
273
- nodetext = modname + '.'
328
+ elif modname and add_module and self.config.add_module_names:
329
+ nodetext = f'{modname}.'
274
330
  signode += addnodes.desc_addname(nodetext, nodetext)
275
331
 
276
332
  signode += addnodes.desc_name(name, name)
277
333
 
278
334
  if tp_list:
279
335
  try:
280
- signode += _parse_type_list(tp_list, self.env, multi_line_type_parameter_list)
336
+ signode += _parse_type_list(
337
+ tp_list,
338
+ self.env,
339
+ multi_line_type_parameter_list,
340
+ trailing_comma,
341
+ )
281
342
  except Exception as exc:
282
- logger.warning("could not parse tp_list (%r): %s", tp_list, exc,
283
- location=signode)
343
+ logger.warning(
344
+ 'could not parse tp_list (%r): %s', tp_list, exc, location=signode
345
+ )
284
346
 
285
347
  if arglist:
286
348
  try:
287
- signode += _parse_arglist(arglist, self.env, multi_line_parameter_list)
349
+ signode += _parse_arglist(
350
+ arglist,
351
+ self.env,
352
+ multi_line_parameter_list,
353
+ trailing_comma,
354
+ )
288
355
  except SyntaxError:
289
356
  # fallback to parse arglist original parser
290
357
  # (this may happen if the argument list is incorrectly used
291
358
  # as a list of bases when documenting a class)
292
359
  # it supports to represent optional arguments (ex. "func(foo [, bar])")
293
- _pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
360
+ _pseudo_parse_arglist(
361
+ signode,
362
+ arglist,
363
+ multi_line_parameter_list,
364
+ trailing_comma,
365
+ )
294
366
  except (NotImplementedError, ValueError) as exc:
295
367
  # duplicated parameter names raise ValueError and not a SyntaxError
296
- logger.warning("could not parse arglist (%r): %s", arglist, exc,
297
- location=signode)
298
- _pseudo_parse_arglist(signode, arglist, multi_line_parameter_list)
368
+ logger.warning(
369
+ 'could not parse arglist (%r): %s', arglist, exc, location=signode
370
+ )
371
+ _pseudo_parse_arglist(
372
+ signode,
373
+ arglist,
374
+ multi_line_parameter_list,
375
+ trailing_comma,
376
+ )
299
377
  else:
300
378
  if self.needs_arglist():
301
379
  # for callables, add an empty parameter list
@@ -307,9 +385,9 @@ class PyObject(ObjectDescription[tuple[str, str]]):
307
385
 
308
386
  anno = self.options.get('annotation')
309
387
  if anno:
310
- signode += addnodes.desc_annotation(' ' + anno, '',
311
- addnodes.desc_sig_space(),
312
- nodes.Text(anno))
388
+ signode += addnodes.desc_annotation(
389
+ f' {anno}', '', addnodes.desc_sig_space(), nodes.Text(anno)
390
+ )
313
391
 
314
392
  return fullname, prefix
315
393
 
@@ -329,10 +407,11 @@ class PyObject(ObjectDescription[tuple[str, str]]):
329
407
  msg = 'must be implemented in subclasses'
330
408
  raise NotImplementedError(msg)
331
409
 
332
- def add_target_and_index(self, name_cls: tuple[str, str], sig: str,
333
- signode: desc_signature) -> None:
334
- modname = self.options.get('module', self.env.ref_context.get('py:module'))
335
- fullname = (modname + '.' if modname else '') + name_cls[0]
410
+ def add_target_and_index(
411
+ self, name_cls: tuple[str, str], sig: str, signode: desc_signature
412
+ ) -> None:
413
+ mod_name = self.options.get('module', self.env.ref_context.get('py:module'))
414
+ fullname = (f'{mod_name}.' if mod_name else '') + name_cls[0]
336
415
  node_id = make_id(self.env, self.state.document, '', fullname)
337
416
  signode['ids'].append(node_id)
338
417
  self.state.document.note_explicit_target(signode)
@@ -342,13 +421,19 @@ class PyObject(ObjectDescription[tuple[str, str]]):
342
421
 
343
422
  canonical_name = self.options.get('canonical')
344
423
  if canonical_name:
345
- domain.note_object(canonical_name, self.objtype, node_id, aliased=True,
346
- location=signode)
424
+ domain.note_object(
425
+ canonical_name, self.objtype, node_id, aliased=True, location=signode
426
+ )
347
427
 
348
428
  if 'no-index-entry' not in self.options:
349
- indextext = self.get_index_text(modname, name_cls)
350
- if indextext:
351
- self.indexnode['entries'].append(('single', indextext, node_id, '', None))
429
+ if index_text := self.get_index_text(mod_name, name_cls):
430
+ self.indexnode['entries'].append((
431
+ 'single',
432
+ index_text,
433
+ node_id,
434
+ '',
435
+ None,
436
+ ))
352
437
 
353
438
  def before_content(self) -> None:
354
439
  """Handle object nesting before content
@@ -398,8 +483,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
398
483
  with contextlib.suppress(IndexError):
399
484
  classes.pop()
400
485
 
401
- self.env.ref_context['py:class'] = (classes[-1] if len(classes) > 0
402
- else None)
486
+ self.env.ref_context['py:class'] = classes[-1] if len(classes) > 0 else None
403
487
  if 'module' in self.options:
404
488
  modules = self.env.ref_context.setdefault('py:modules', [])
405
489
  if modules:
@@ -411,7 +495,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
411
495
  if not sig_node.get('_toc_parts'):
412
496
  return ''
413
497
 
414
- config = self.env.app.config
498
+ config = self.config
415
499
  objtype = sig_node.parent.get('objtype')
416
500
  if config.add_function_parentheses and objtype in {'function', 'method'}:
417
501
  parens = '()'
sphinx/domains/rst.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
- from typing import TYPE_CHECKING, Any, ClassVar
6
+ from typing import TYPE_CHECKING
7
7
 
8
8
  from docutils.parsers.rst import directives
9
9
 
@@ -17,7 +17,9 @@ from sphinx.util.nodes import make_id, make_refnode
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from collections.abc import Iterator, Set
20
+ from typing import Any, ClassVar
20
21
 
22
+ from docutils import nodes
21
23
  from docutils.nodes import Element
22
24
 
23
25
  from sphinx.addnodes import desc_signature, pending_xref
@@ -32,9 +34,7 @@ dir_sig_re = re.compile(r'\.\. (.+?)::(.*)$')
32
34
 
33
35
 
34
36
  class ReSTMarkup(ObjectDescription[str]):
35
- """
36
- Description of generic reST markup.
37
- """
37
+ """Description of generic reST markup."""
38
38
 
39
39
  option_spec: ClassVar[OptionSpec] = {
40
40
  'no-index': directives.flag,
@@ -46,7 +46,9 @@ class ReSTMarkup(ObjectDescription[str]):
46
46
  'nocontentsentry': directives.flag,
47
47
  }
48
48
 
49
- def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
49
+ def add_target_and_index(
50
+ self, name: str, sig: str, signode: desc_signature
51
+ ) -> None:
50
52
  node_id = make_id(self.env, self.state.document, self.objtype, name)
51
53
  signode['ids'].append(node_id)
52
54
  self.state.document.note_explicit_target(signode)
@@ -55,9 +57,14 @@ class ReSTMarkup(ObjectDescription[str]):
55
57
  domain.note_object(self.objtype, name, node_id, location=signode)
56
58
 
57
59
  if 'no-index-entry' not in self.options:
58
- indextext = self.get_index_text(self.objtype, name)
59
- if indextext:
60
- self.indexnode['entries'].append(('single', indextext, node_id, '', None))
60
+ if index_text := self.get_index_text(self.objtype, name):
61
+ self.indexnode['entries'].append((
62
+ 'single',
63
+ index_text,
64
+ node_id,
65
+ '',
66
+ None,
67
+ ))
61
68
 
62
69
  def get_index_text(self, objectname: str, name: str) -> str:
63
70
  return ''
@@ -75,12 +82,11 @@ class ReSTMarkup(ObjectDescription[str]):
75
82
  if not sig_node.get('_toc_parts'):
76
83
  return ''
77
84
 
78
- config = self.env.app.config
79
85
  objtype = sig_node.parent.get('objtype')
80
86
  *parents, name = sig_node['_toc_parts']
81
87
  if objtype == 'directive:option':
82
88
  return f':{name}:'
83
- if config.toc_object_entries_show_parents in {'domain', 'all'}:
89
+ if self.config.toc_object_entries_show_parents in {'domain', 'all'}:
84
90
  name = ':'.join(sig_node['_toc_parts'])
85
91
  if objtype == 'role':
86
92
  return f':{name}:'
@@ -98,21 +104,19 @@ def parse_directive(d: str) -> tuple[str, str]:
98
104
  dir = d.strip()
99
105
  if not dir.startswith('.'):
100
106
  # Assume it is a directive without syntax
101
- return (dir, '')
107
+ return dir, ''
102
108
  m = dir_sig_re.match(dir)
103
109
  if not m:
104
- return (dir, '')
110
+ return dir, ''
105
111
  parsed_dir, parsed_args = m.groups()
106
112
  if parsed_args.strip():
107
- return (parsed_dir.strip(), ' ' + parsed_args.strip())
113
+ return parsed_dir.strip(), ' ' + parsed_args.strip()
108
114
  else:
109
- return (parsed_dir.strip(), '')
115
+ return parsed_dir.strip(), ''
110
116
 
111
117
 
112
118
  class ReSTDirective(ReSTMarkup):
113
- """
114
- Description of a reST directive.
115
- """
119
+ """Description of a reST directive."""
116
120
 
117
121
  def handle_signature(self, sig: str, signode: desc_signature) -> str:
118
122
  name, args = parse_directive(sig)
@@ -138,9 +142,7 @@ class ReSTDirective(ReSTMarkup):
138
142
 
139
143
 
140
144
  class ReSTDirectiveOption(ReSTMarkup):
141
- """
142
- Description of an option for reST directive.
143
- """
145
+ """Description of an option for reST directive."""
144
146
 
145
147
  option_spec: ClassVar[OptionSpec] = ReSTMarkup.option_spec.copy()
146
148
  option_spec.update({
@@ -157,13 +159,16 @@ class ReSTDirectiveOption(ReSTMarkup):
157
159
  signode['fullname'] = name.strip()
158
160
  signode += addnodes.desc_name(desc_name, desc_name)
159
161
  if argument:
160
- signode += addnodes.desc_annotation(' ' + argument, ' ' + argument)
162
+ text = f' {argument}'
163
+ signode += addnodes.desc_annotation(text, text)
161
164
  if self.options.get('type'):
162
165
  text = ' (%s)' % self.options['type']
163
166
  signode += addnodes.desc_annotation(text, text)
164
167
  return name
165
168
 
166
- def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
169
+ def add_target_and_index(
170
+ self, name: str, sig: str, signode: desc_signature
171
+ ) -> None:
167
172
  domain = self.env.domains.restructuredtext_domain
168
173
 
169
174
  directive_name = self.current_directive
@@ -181,9 +186,17 @@ class ReSTDirectiveOption(ReSTMarkup):
181
186
 
182
187
  if directive_name:
183
188
  key = name[0].upper()
184
- pair = [_('%s (directive)') % directive_name,
185
- _(':%s: (directive option)') % name]
186
- self.indexnode['entries'].append(('pair', '; '.join(pair), node_id, '', key))
189
+ pair = [
190
+ _('%s (directive)') % directive_name,
191
+ _(':%s: (directive option)') % name,
192
+ ]
193
+ self.indexnode['entries'].append((
194
+ 'pair',
195
+ '; '.join(pair),
196
+ node_id,
197
+ '',
198
+ key,
199
+ ))
187
200
  else:
188
201
  key = name[0].upper()
189
202
  text = _(':%s: (directive option)') % name
@@ -199,9 +212,7 @@ class ReSTDirectiveOption(ReSTMarkup):
199
212
 
200
213
 
201
214
  class ReSTRole(ReSTMarkup):
202
- """
203
- Description of a reST role.
204
- """
215
+ """Description of a reST role."""
205
216
 
206
217
  def handle_signature(self, sig: str, signode: desc_signature) -> str:
207
218
  desc_name = f':{sig}:'
@@ -220,17 +231,17 @@ class ReSTDomain(Domain):
220
231
  label = 'reStructuredText'
221
232
 
222
233
  object_types = {
223
- 'directive': ObjType(_('directive'), 'dir'),
234
+ 'directive': ObjType(_('directive'), 'dir'),
224
235
  'directive:option': ObjType(_('directive-option'), 'dir'),
225
- 'role': ObjType(_('role'), 'role'),
236
+ 'role': ObjType(_('role'), 'role'),
226
237
  }
227
238
  directives = {
228
239
  'directive': ReSTDirective,
229
240
  'directive:option': ReSTDirectiveOption,
230
- 'role': ReSTRole,
241
+ 'role': ReSTRole,
231
242
  }
232
243
  roles = {
233
- 'dir': XRefRole(),
244
+ 'dir': XRefRole(),
234
245
  'role': XRefRole(),
235
246
  }
236
247
  initial_data: dict[str, dict[tuple[str, str], str]] = {
@@ -239,13 +250,21 @@ class ReSTDomain(Domain):
239
250
 
240
251
  @property
241
252
  def objects(self) -> dict[tuple[str, str], tuple[str, str]]:
242
- return self.data.setdefault('objects', {}) # (objtype, fullname) -> (docname, node_id)
253
+ # (objtype, fullname) -> (docname, node_id)
254
+ return self.data.setdefault('objects', {})
243
255
 
244
- def note_object(self, objtype: str, name: str, node_id: str, location: Any = None) -> None:
256
+ def note_object(
257
+ self, objtype: str, name: str, node_id: str, location: Any = None
258
+ ) -> None:
245
259
  if (objtype, name) in self.objects:
246
260
  docname, node_id = self.objects[objtype, name]
247
- logger.warning(__('duplicate description of %s %s, other instance in %s'),
248
- objtype, name, docname, location=location)
261
+ logger.warning(
262
+ __('duplicate description of %s %s, other instance in %s'),
263
+ objtype,
264
+ name,
265
+ docname,
266
+ location=location,
267
+ )
249
268
 
250
269
  self.objects[objtype, name] = (self.env.docname, node_id)
251
270
 
@@ -260,9 +279,16 @@ class ReSTDomain(Domain):
260
279
  if doc in docnames:
261
280
  self.objects[typ, name] = (doc, node_id)
262
281
 
263
- def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
264
- typ: str, target: str, node: pending_xref, contnode: Element,
265
- ) -> Element | None:
282
+ def resolve_xref(
283
+ self,
284
+ env: BuildEnvironment,
285
+ fromdocname: str,
286
+ builder: Builder,
287
+ typ: str,
288
+ target: str,
289
+ node: pending_xref,
290
+ contnode: Element,
291
+ ) -> nodes.reference | None:
266
292
  objtypes = self.objtypes_for_role(typ)
267
293
  if not objtypes:
268
294
  return None
@@ -270,22 +296,41 @@ class ReSTDomain(Domain):
270
296
  result = self.objects.get((objtype, target))
271
297
  if result:
272
298
  todocname, node_id = result
273
- return make_refnode(builder, fromdocname, todocname, node_id,
274
- contnode, target + ' ' + objtype)
299
+ return make_refnode(
300
+ builder,
301
+ fromdocname,
302
+ todocname,
303
+ node_id,
304
+ contnode,
305
+ f'{target} {objtype}',
306
+ )
275
307
  return None
276
308
 
277
- def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
278
- target: str, node: pending_xref, contnode: Element,
279
- ) -> list[tuple[str, Element]]:
280
- results: list[tuple[str, Element]] = []
309
+ def resolve_any_xref(
310
+ self,
311
+ env: BuildEnvironment,
312
+ fromdocname: str,
313
+ builder: Builder,
314
+ target: str,
315
+ node: pending_xref,
316
+ contnode: Element,
317
+ ) -> list[tuple[str, nodes.reference]]:
318
+ results: list[tuple[str, nodes.reference]] = []
281
319
  for objtype in self.object_types:
282
320
  result = self.objects.get((objtype, target))
283
321
  if result:
284
322
  todocname, node_id = result
285
- results.append(
286
- ('rst:' + self.role_for_objtype(objtype), # type: ignore[operator]
287
- make_refnode(builder, fromdocname, todocname, node_id,
288
- contnode, target + ' ' + objtype)))
323
+ results.append((
324
+ f'rst:{self.role_for_objtype(objtype)}',
325
+ make_refnode(
326
+ builder,
327
+ fromdocname,
328
+ todocname,
329
+ node_id,
330
+ contnode,
331
+ f'{target} {objtype}',
332
+ ),
333
+ ))
289
334
  return results
290
335
 
291
336
  def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]: