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
@@ -6,25 +6,28 @@ import operator
6
6
  import token
7
7
  from collections import deque
8
8
  from inspect import Parameter
9
- from typing import TYPE_CHECKING, Any
9
+ from typing import TYPE_CHECKING
10
10
 
11
11
  from docutils import nodes
12
12
 
13
13
  from sphinx import addnodes
14
- from sphinx.addnodes import desc_signature, pending_xref, pending_xref_condition
14
+ from sphinx.addnodes import pending_xref, pending_xref_condition
15
15
  from sphinx.pycode.parser import Token, TokenProcessor
16
16
  from sphinx.util.inspect import signature_from_str
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from collections.abc import Iterable, Iterator
20
+ from typing import Any
20
21
 
21
22
  from docutils.nodes import Element, Node
22
23
 
24
+ from sphinx.addnodes import desc_signature
23
25
  from sphinx.environment import BuildEnvironment
24
26
 
25
27
 
26
- def parse_reftarget(reftarget: str, suppress_prefix: bool = False,
27
- ) -> tuple[str, str, str, bool]:
28
+ def parse_reftarget(
29
+ reftarget: str, suppress_prefix: bool = False
30
+ ) -> tuple[str, str, str, bool]:
28
31
  """Parse a type string and return (reftype, reftarget, title, refspecific flag)"""
29
32
  refspecific = False
30
33
  if reftarget.startswith('.'):
@@ -50,12 +53,15 @@ def parse_reftarget(reftarget: str, suppress_prefix: bool = False,
50
53
  return reftype, reftarget, title, refspecific
51
54
 
52
55
 
53
- def type_to_xref(target: str, env: BuildEnvironment, *,
54
- suppress_prefix: bool = False) -> addnodes.pending_xref:
56
+ def type_to_xref(
57
+ target: str, env: BuildEnvironment, *, suppress_prefix: bool = False
58
+ ) -> addnodes.pending_xref:
55
59
  """Convert a type string to a cross reference node."""
56
60
  if env:
57
- kwargs = {'py:module': env.ref_context.get('py:module'),
58
- 'py:class': env.ref_context.get('py:class')}
61
+ kwargs = {
62
+ 'py:module': env.ref_context.get('py:module'),
63
+ 'py:class': env.ref_context.get('py:class'),
64
+ }
59
65
  else:
60
66
  kwargs = {}
61
67
 
@@ -66,14 +72,22 @@ def type_to_xref(target: str, env: BuildEnvironment, *,
66
72
  # nested classes. But python domain can't access the real python object because this
67
73
  # module should work not-dynamically.
68
74
  shortname = title.split('.')[-1]
69
- contnodes: list[Node] = [pending_xref_condition('', shortname, condition='resolved'),
70
- pending_xref_condition('', title, condition='*')]
75
+ contnodes: list[Node] = [
76
+ pending_xref_condition('', shortname, condition='resolved'),
77
+ pending_xref_condition('', title, condition='*'),
78
+ ]
71
79
  else:
72
80
  contnodes = [nodes.Text(title)]
73
81
 
74
- return pending_xref('', *contnodes,
75
- refdomain='py', reftype=reftype, reftarget=target,
76
- refspecific=refspecific, **kwargs)
82
+ return pending_xref(
83
+ '',
84
+ *contnodes,
85
+ refdomain='py',
86
+ reftype=reftype,
87
+ reftarget=target,
88
+ refspecific=refspecific,
89
+ **kwargs,
90
+ )
77
91
 
78
92
 
79
93
  def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
@@ -82,19 +96,21 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
82
96
 
83
97
  def unparse(node: ast.AST) -> list[Node]:
84
98
  if isinstance(node, ast.Attribute):
85
- return [nodes.Text(f"{unparse(node.value)[0]}.{node.attr}")]
99
+ return [nodes.Text(f'{unparse(node.value)[0]}.{node.attr}')]
86
100
  if isinstance(node, ast.BinOp):
87
101
  result: list[Node] = unparse(node.left)
88
102
  result.extend(unparse(node.op))
89
103
  result.extend(unparse(node.right))
90
104
  return result
91
105
  if isinstance(node, ast.BitOr):
92
- return [addnodes.desc_sig_space(),
93
- addnodes.desc_sig_punctuation('', '|'),
94
- addnodes.desc_sig_space()]
106
+ return [
107
+ addnodes.desc_sig_space(),
108
+ addnodes.desc_sig_punctuation('', '|'),
109
+ addnodes.desc_sig_space(),
110
+ ]
95
111
  if isinstance(node, ast.Constant):
96
112
  if node.value is Ellipsis:
97
- return [addnodes.desc_sig_punctuation('', "...")]
113
+ return [addnodes.desc_sig_punctuation('', '...')]
98
114
  if isinstance(node.value, bool):
99
115
  return [addnodes.desc_sig_keyword('', repr(node.value))]
100
116
  if isinstance(node.value, int):
@@ -140,7 +156,7 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
140
156
  result.append(addnodes.desc_sig_punctuation('', ']'))
141
157
 
142
158
  # Wrap the Text nodes inside brackets by literal node if the subscript is a Literal
143
- if result[0] in ('Literal', 'typing.Literal'):
159
+ if result[0] in {'Literal', 'typing.Literal'}:
144
160
  for i, subnode in enumerate(result[1:], start=1):
145
161
  if isinstance(subnode, nodes.Text):
146
162
  result[i] = nodes.literal('', '', subnode)
@@ -157,8 +173,10 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
157
173
  result.pop()
158
174
  result.pop()
159
175
  else:
160
- result = [addnodes.desc_sig_punctuation('', '('),
161
- addnodes.desc_sig_punctuation('', ')')]
176
+ result = [
177
+ addnodes.desc_sig_punctuation('', '('),
178
+ addnodes.desc_sig_punctuation('', ')'),
179
+ ]
162
180
 
163
181
  return result
164
182
  if isinstance(node, ast.Call):
@@ -211,8 +229,11 @@ def _parse_annotation(annotation: str, env: BuildEnvironment) -> list[Node]:
211
229
  if isinstance(node, nodes.literal):
212
230
  result.append(node[0])
213
231
  elif isinstance(node, nodes.Text) and node.strip():
214
- if (result and isinstance(result[-1], addnodes.desc_sig_punctuation) and
215
- result[-1].astext() == '~'):
232
+ if (
233
+ result
234
+ and isinstance(result[-1], addnodes.desc_sig_punctuation)
235
+ and result[-1].astext() == '~'
236
+ ):
216
237
  result.pop()
217
238
  result.append(type_to_xref(str(node), env, suppress_prefix=True))
218
239
  else:
@@ -244,8 +265,7 @@ class _TypeParameterListParser(TokenProcessor):
244
265
  else:
245
266
  if current == token.INDENT:
246
267
  tokens += self.fetch_until(token.DEDENT)
247
- elif current.match(
248
- [token.OP, ':'], [token.OP, '='], [token.OP, ',']):
268
+ elif current.match([token.OP, ':'], [token.OP, '='], [token.OP, ',']):
249
269
  tokens.pop()
250
270
  break
251
271
  return tokens
@@ -254,7 +274,9 @@ class _TypeParameterListParser(TokenProcessor):
254
274
  while current := self.fetch_token():
255
275
  if current == token.NAME:
256
276
  tp_name = current.value.strip()
257
- if self.previous and self.previous.match([token.OP, '*'], [token.OP, '**']):
277
+ if self.previous and self.previous.match(
278
+ [token.OP, '*'], [token.OP, '**']
279
+ ):
258
280
  if self.previous == [token.OP, '*']:
259
281
  tp_kind = Parameter.VAR_POSITIONAL
260
282
  else:
@@ -275,9 +297,14 @@ class _TypeParameterListParser(TokenProcessor):
275
297
  tokens = self.fetch_type_param_spec()
276
298
  tp_default = self._build_identifier(tokens)
277
299
 
278
- if tp_kind != Parameter.POSITIONAL_OR_KEYWORD and tp_ann != Parameter.empty:
279
- msg = ('type parameter bound or constraint is not allowed '
280
- f'for {tp_kind.description} parameters')
300
+ if (
301
+ tp_kind != Parameter.POSITIONAL_OR_KEYWORD
302
+ and tp_ann != Parameter.empty
303
+ ):
304
+ msg = (
305
+ 'type parameter bound or constraint is not allowed '
306
+ f'for {tp_kind.description} parameters'
307
+ )
281
308
  raise SyntaxError(msg)
282
309
 
283
310
  type_param = (tp_name, tp_kind, tp_default, tp_ann)
@@ -315,12 +342,22 @@ class _TypeParameterListParser(TokenProcessor):
315
342
  idents.append(ident)
316
343
  # determine if the next token is an unpack operator depending
317
344
  # on the left and right hand side of the operator symbol
318
- is_unpack_operator = (
319
- op.match([token.OP, '*'], [token.OP, '**']) and not (
320
- tok.match(token.NAME, token.NUMBER, token.STRING,
321
- [token.OP, ')'], [token.OP, ']'], [token.OP, '}'])
322
- and after.match(token.NAME, token.NUMBER, token.STRING,
323
- [token.OP, '('], [token.OP, '['], [token.OP, '{'])
345
+ is_unpack_operator = op.match([token.OP, '*'], [token.OP, '**']) and not (
346
+ tok.match(
347
+ token.NAME,
348
+ token.NUMBER,
349
+ token.STRING,
350
+ [token.OP, ')'],
351
+ [token.OP, ']'],
352
+ [token.OP, '}'],
353
+ )
354
+ and after.match(
355
+ token.NAME,
356
+ token.NUMBER,
357
+ token.STRING,
358
+ [token.OP, '('],
359
+ [token.OP, '['],
360
+ [token.OP, '{'],
324
361
  )
325
362
  )
326
363
 
@@ -356,28 +393,33 @@ class _TypeParameterListParser(TokenProcessor):
356
393
  [token.OP, '@'], [token.OP, '/'], [token.OP, '//'], [token.OP, '%'],
357
394
  [token.OP, '<<'], [token.OP, '>>'], [token.OP, '>>>'],
358
395
  [token.OP, '<='], [token.OP, '>='], [token.OP, '=='], [token.OP, '!='],
359
- ):
396
+ ): # fmt: skip
360
397
  return f' {tok.value} '
361
398
 
362
399
  return tok.value
363
400
 
364
401
 
365
402
  def _parse_type_list(
366
- tp_list: str, env: BuildEnvironment,
403
+ tp_list: str,
404
+ env: BuildEnvironment,
367
405
  multi_line_parameter_list: bool = False,
406
+ trailing_comma: bool = True,
368
407
  ) -> addnodes.desc_type_parameter_list:
369
408
  """Parse a list of type parameters according to PEP 695."""
370
409
  type_params = addnodes.desc_type_parameter_list(tp_list)
371
410
  type_params['multi_line_parameter_list'] = multi_line_parameter_list
411
+ type_params['multi_line_trailing_comma'] = trailing_comma
372
412
  # formal parameter names are interpreted as type parameter names and
373
413
  # type annotations are interpreted as type parameter bound or constraints
374
414
  parser = _TypeParameterListParser(tp_list)
375
415
  parser.parse()
376
- for (tp_name, tp_kind, tp_default, tp_ann) in parser.type_params:
416
+ for tp_name, tp_kind, tp_default, tp_ann in parser.type_params:
377
417
  # no positional-only or keyword-only allowed in a type parameters list
378
418
  if tp_kind in {Parameter.POSITIONAL_ONLY, Parameter.KEYWORD_ONLY}:
379
- msg = ('positional-only or keyword-only parameters '
380
- 'are prohibited in type parameter lists')
419
+ msg = (
420
+ 'positional-only or keyword-only parameters '
421
+ 'are prohibited in type parameter lists'
422
+ )
381
423
  raise SyntaxError(msg)
382
424
 
383
425
  node = addnodes.desc_type_parameter()
@@ -395,8 +437,7 @@ def _parse_type_list(
395
437
  node += addnodes.desc_sig_punctuation('', ':')
396
438
  node += addnodes.desc_sig_space()
397
439
 
398
- type_ann_expr = addnodes.desc_sig_name('', '',
399
- *annotation) # type: ignore[arg-type]
440
+ type_ann_expr = addnodes.desc_sig_name('', '', *annotation) # type: ignore[arg-type]
400
441
  # a type bound is ``T: U`` whereas type constraints
401
442
  # must be enclosed with parentheses. ``T: (U, V)``
402
443
  if tp_ann.startswith('(') and tp_ann.endswith(')'):
@@ -416,31 +457,41 @@ def _parse_type_list(
416
457
  node += addnodes.desc_sig_space()
417
458
  node += addnodes.desc_sig_operator('', '=')
418
459
  node += addnodes.desc_sig_space()
419
- node += nodes.inline('', tp_default,
420
- classes=['default_value'],
421
- support_smartquotes=False)
460
+ node += nodes.inline(
461
+ '', tp_default, classes=['default_value'], support_smartquotes=False
462
+ )
422
463
 
423
464
  type_params += node
424
465
  return type_params
425
466
 
426
467
 
427
468
  def _parse_arglist(
428
- arglist: str, env: BuildEnvironment, multi_line_parameter_list: bool = False,
469
+ arglist: str,
470
+ env: BuildEnvironment,
471
+ multi_line_parameter_list: bool = False,
472
+ trailing_comma: bool = True,
429
473
  ) -> addnodes.desc_parameterlist:
430
474
  """Parse a list of arguments using AST parser"""
431
475
  params = addnodes.desc_parameterlist(arglist)
432
476
  params['multi_line_parameter_list'] = multi_line_parameter_list
477
+ params['multi_line_trailing_comma'] = trailing_comma
433
478
  sig = signature_from_str('(%s)' % arglist)
434
479
  last_kind = None
435
480
  for param in sig.parameters.values():
436
481
  if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY:
437
482
  # PEP-570: Separator for Positional Only Parameter: /
438
- params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/'))
439
- if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD,
440
- param.POSITIONAL_ONLY,
441
- None):
483
+ params += addnodes.desc_parameter(
484
+ '', '', addnodes.desc_sig_operator('', '/')
485
+ )
486
+ if param.kind == param.KEYWORD_ONLY and last_kind in {
487
+ param.POSITIONAL_OR_KEYWORD,
488
+ param.POSITIONAL_ONLY,
489
+ None,
490
+ }:
442
491
  # PEP-3102: Separator for Keyword Only Parameter: *
443
- params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*'))
492
+ params += addnodes.desc_parameter(
493
+ '', '', addnodes.desc_sig_operator('', '*')
494
+ )
444
495
 
445
496
  node = addnodes.desc_parameter()
446
497
  if param.kind == param.VAR_POSITIONAL:
@@ -464,8 +515,9 @@ def _parse_arglist(
464
515
  node += addnodes.desc_sig_space()
465
516
  else:
466
517
  node += addnodes.desc_sig_operator('', '=')
467
- node += nodes.inline('', param.default, classes=['default_value'],
468
- support_smartquotes=False)
518
+ node += nodes.inline(
519
+ '', param.default, classes=['default_value'], support_smartquotes=False
520
+ )
469
521
 
470
522
  params += node
471
523
  last_kind = param.kind
@@ -478,9 +530,12 @@ def _parse_arglist(
478
530
 
479
531
 
480
532
  def _pseudo_parse_arglist(
481
- signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False,
533
+ signode: desc_signature,
534
+ arglist: str,
535
+ multi_line_parameter_list: bool = False,
536
+ trailing_comma: bool = True,
482
537
  ) -> None:
483
- """"Parse" a list of arguments separated by commas.
538
+ """'Parse' a list of arguments separated by commas.
484
539
 
485
540
  Arguments can have "optional" annotations given by enclosing them in
486
541
  brackets. Currently, this will split at any comma, even if it's inside a
@@ -488,6 +543,7 @@ def _pseudo_parse_arglist(
488
543
  """
489
544
  paramlist = addnodes.desc_parameterlist()
490
545
  paramlist['multi_line_parameter_list'] = multi_line_parameter_list
546
+ paramlist['multi_line_trailing_comma'] = trailing_comma
491
547
  stack: list[Element] = [paramlist]
492
548
  try:
493
549
  for argument in arglist.split(','):
@@ -508,7 +564,8 @@ def _pseudo_parse_arglist(
508
564
  argument = argument[:-1].strip()
509
565
  if argument:
510
566
  stack[-1] += addnodes.desc_parameter(
511
- '', '', addnodes.desc_sig_name(argument, argument))
567
+ '', '', addnodes.desc_sig_name(argument, argument)
568
+ )
512
569
  while ends_open:
513
570
  stack.append(addnodes.desc_optional())
514
571
  stack[-2] += stack[-1]
@@ -517,7 +574,7 @@ def _pseudo_parse_arglist(
517
574
  stack.pop()
518
575
  ends_close -= 1
519
576
  if len(stack) != 1:
520
- raise IndexError
577
+ raise IndexError # NoQA: TRY301
521
578
  except IndexError:
522
579
  # if there are too few or too many elements on the stack, just give up
523
580
  # and treat the whole argument list as one argument, discarding the