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
@@ -11,7 +11,7 @@ autosummary directive
11
11
  The autosummary directive has the form::
12
12
 
13
13
  .. autosummary::
14
- :nosignatures:
14
+ :signatures: none
15
15
  :toctree: generated/
16
16
 
17
17
  module.function_1
@@ -51,14 +51,13 @@ from __future__ import annotations
51
51
  import functools
52
52
  import inspect
53
53
  import operator
54
- import os
55
54
  import posixpath
56
55
  import re
57
56
  import sys
58
57
  from inspect import Parameter
59
- from os import path
58
+ from pathlib import Path
60
59
  from types import ModuleType
61
- from typing import TYPE_CHECKING, Any, ClassVar, cast
60
+ from typing import TYPE_CHECKING, cast
62
61
 
63
62
  from docutils import nodes
64
63
  from docutils.parsers.rst import directives
@@ -70,7 +69,7 @@ from sphinx import addnodes
70
69
  from sphinx.config import Config
71
70
  from sphinx.environment import BuildEnvironment
72
71
  from sphinx.errors import PycodeError
73
- from sphinx.ext.autodoc import INSTANCEATTR, Documenter, Options
72
+ from sphinx.ext.autodoc import INSTANCEATTR, Options
74
73
  from sphinx.ext.autodoc.directive import DocumenterBridge
75
74
  from sphinx.ext.autodoc.importer import import_module
76
75
  from sphinx.ext.autodoc.mock import mock
@@ -92,10 +91,12 @@ from sphinx.util.parsing import nested_parse_to_nodes
92
91
 
93
92
  if TYPE_CHECKING:
94
93
  from collections.abc import Sequence
94
+ from typing import Any, ClassVar
95
95
 
96
96
  from docutils.nodes import Node, system_message
97
97
 
98
98
  from sphinx.application import Sphinx
99
+ from sphinx.ext.autodoc import Documenter
99
100
  from sphinx.extension import Extension
100
101
  from sphinx.util.typing import ExtensionMetadata, OptionSpec
101
102
  from sphinx.writers.html5 import HTML5Translator
@@ -111,6 +112,7 @@ WELL_KNOWN_ABBREVIATIONS = ('et al.', 'e.g.', 'i.e.')
111
112
 
112
113
  # -- autosummary_toc node ------------------------------------------------------
113
114
 
115
+
114
116
  class autosummary_toc(nodes.comment):
115
117
  pass
116
118
 
@@ -126,23 +128,26 @@ def autosummary_noop(self: nodes.NodeVisitor, node: Node) -> None:
126
128
 
127
129
  # -- autosummary_table node ----------------------------------------------------
128
130
 
131
+
129
132
  class autosummary_table(nodes.comment):
130
133
  pass
131
134
 
132
135
 
133
- def autosummary_table_visit_html(self: HTML5Translator, node: autosummary_table) -> None:
136
+ def autosummary_table_visit_html(
137
+ self: HTML5Translator, node: autosummary_table
138
+ ) -> None:
134
139
  """Make the first column of the table non-breaking."""
135
140
  try:
136
- table = cast(nodes.table, node[0])
137
- tgroup = cast(nodes.tgroup, table[0])
138
- tbody = cast(nodes.tbody, tgroup[-1])
139
- rows = cast(list[nodes.row], tbody)
141
+ table = cast('nodes.table', node[0])
142
+ tgroup = cast('nodes.tgroup', table[0])
143
+ tbody = cast('nodes.tbody', tgroup[-1])
144
+ rows = cast('list[nodes.row]', tbody)
140
145
  for row in rows:
141
- col1_entry = cast(nodes.entry, row[0])
142
- par = cast(nodes.paragraph, col1_entry[0])
146
+ col1_entry = cast('nodes.entry', row[0])
147
+ par = cast('nodes.paragraph', col1_entry[0])
143
148
  for j, subnode in enumerate(list(par)):
144
149
  if isinstance(subnode, nodes.Text):
145
- new_text = subnode.astext().replace(" ", "\u00a0")
150
+ new_text = subnode.astext().replace(' ', '\u00a0')
146
151
  par[j] = nodes.Text(new_text)
147
152
  except IndexError:
148
153
  pass
@@ -150,14 +155,15 @@ def autosummary_table_visit_html(self: HTML5Translator, node: autosummary_table)
150
155
 
151
156
  # -- autodoc integration -------------------------------------------------------
152
157
 
158
+
153
159
  class FakeApplication:
154
160
  verbosity = 0
155
161
 
156
162
  def __init__(self) -> None:
157
- self.doctreedir = None
163
+ self.doctreedir = Path()
158
164
  self.events = None
159
165
  self.extensions: dict[str, Extension] = {}
160
- self.srcdir = None
166
+ self.srcdir = Path()
161
167
  self.config = Config()
162
168
  self.project = Project('', {})
163
169
  self.registry = SphinxComponentRegistry()
@@ -174,7 +180,9 @@ class FakeDirective(DocumenterBridge):
174
180
  super().__init__(env, None, Options(), 0, state)
175
181
 
176
182
 
177
- def get_documenter(app: Sphinx, obj: Any, parent: Any) -> type[Documenter]:
183
+ def _get_documenter(
184
+ obj: Any, parent: Any, *, registry: SphinxComponentRegistry
185
+ ) -> type[Documenter]:
178
186
  """Get an autodoc.Documenter class suitable for documenting the given
179
187
  object.
180
188
 
@@ -190,18 +198,21 @@ def get_documenter(app: Sphinx, obj: Any, parent: Any) -> type[Documenter]:
190
198
 
191
199
  # Construct a fake documenter for *parent*
192
200
  if parent is not None:
193
- parent_doc_cls = get_documenter(app, parent, None)
201
+ parent_doc_cls = _get_documenter(parent, None, registry=registry)
194
202
  else:
195
203
  parent_doc_cls = ModuleDocumenter
196
204
 
197
205
  if hasattr(parent, '__name__'):
198
206
  parent_doc = parent_doc_cls(FakeDirective(), parent.__name__)
199
207
  else:
200
- parent_doc = parent_doc_cls(FakeDirective(), "")
208
+ parent_doc = parent_doc_cls(FakeDirective(), '')
201
209
 
202
210
  # Get the correct documenter class for *obj*
203
- classes = [cls for cls in app.registry.documenters.values()
204
- if cls.can_document_member(obj, '', False, parent_doc)]
211
+ classes = [
212
+ cls
213
+ for cls in registry.documenters.values()
214
+ if cls.can_document_member(obj, '', False, parent_doc)
215
+ ]
205
216
  if classes:
206
217
  classes.sort(key=lambda cls: cls.priority)
207
218
  return classes[-1]
@@ -211,9 +222,9 @@ def get_documenter(app: Sphinx, obj: Any, parent: Any) -> type[Documenter]:
211
222
 
212
223
  # -- .. autosummary:: ----------------------------------------------------------
213
224
 
225
+
214
226
  class Autosummary(SphinxDirective):
215
- """
216
- Pretty table containing short signatures and summaries of functions etc.
227
+ """Pretty table containing short signatures and summaries of functions etc.
217
228
 
218
229
  autosummary can also optionally generate a hidden toctree:: node.
219
230
  """
@@ -224,18 +235,24 @@ class Autosummary(SphinxDirective):
224
235
  has_content = True
225
236
  option_spec: ClassVar[OptionSpec] = {
226
237
  'caption': directives.unchanged_required,
238
+ 'class': directives.class_option,
227
239
  'toctree': directives.unchanged,
228
240
  'nosignatures': directives.flag,
229
241
  'recursive': directives.flag,
242
+ 'signatures': directives.unchanged,
230
243
  'template': directives.unchanged,
231
244
  }
232
245
 
233
246
  def run(self) -> list[Node]:
234
- self.bridge = DocumenterBridge(self.env, self.state.document.reporter,
235
- Options(), self.lineno, self.state)
236
-
237
- names = [x.strip().split()[0] for x in self.content
238
- if x.strip() and re.search(r'^[~a-zA-Z_]', x.strip()[0])]
247
+ self.bridge = DocumenterBridge(
248
+ self.env, self.state.document.reporter, Options(), self.lineno, self.state
249
+ )
250
+
251
+ names = [
252
+ x.strip().split()[0]
253
+ for x in self.content
254
+ if x.strip() and re.search(r'^[~a-zA-Z_]', x.strip()[0])
255
+ ]
239
256
  items = self.get_items(names)
240
257
  nodes = self.get_table(items)
241
258
 
@@ -252,10 +269,14 @@ class Autosummary(SphinxDirective):
252
269
  docname = posixpath.normpath(posixpath.join(dirname, docname))
253
270
  if docname not in self.env.found_docs:
254
271
  if excluded(str(self.env.doc2path(docname, False))):
255
- msg = __('autosummary references excluded document %r. Ignored.')
272
+ msg = __(
273
+ 'autosummary references excluded document %r. Ignored.'
274
+ )
256
275
  else:
257
- msg = __('autosummary: stub file not found %r. '
258
- 'Check your autosummary_generate setting.')
276
+ msg = __(
277
+ 'autosummary: stub file not found %r. '
278
+ 'Check your autosummary_generate setting.'
279
+ )
259
280
 
260
281
  logger.warning(msg, real_name, location=self.get_location())
261
282
  continue
@@ -273,13 +294,15 @@ class Autosummary(SphinxDirective):
273
294
  nodes.append(autosummary_toc('', '', tocnode))
274
295
 
275
296
  if 'toctree' not in self.options and 'caption' in self.options:
276
- logger.warning(__('A captioned autosummary requires :toctree: option. ignored.'),
277
- location=nodes[-1])
297
+ logger.warning(
298
+ __('A captioned autosummary requires :toctree: option. ignored.'),
299
+ location=nodes[-1],
300
+ )
278
301
 
279
302
  return nodes
280
303
 
281
304
  def import_by_name(
282
- self, name: str, prefixes: list[str | None],
305
+ self, name: str, prefixes: list[str | None]
283
306
  ) -> tuple[str, Any, Any, str]:
284
307
  with mock(self.config.autosummary_mock_imports):
285
308
  try:
@@ -296,23 +319,41 @@ class Autosummary(SphinxDirective):
296
319
 
297
320
  raise ImportExceptionGroup(exc.args[0], errors) from None
298
321
 
299
- def create_documenter(self, app: Sphinx, obj: Any,
300
- parent: Any, full_name: str) -> Documenter:
322
+ def create_documenter(
323
+ self,
324
+ obj: Any,
325
+ parent: Any,
326
+ full_name: str,
327
+ *,
328
+ registry: SphinxComponentRegistry,
329
+ ) -> Documenter:
301
330
  """Get an autodoc.Documenter class suitable for documenting the given
302
331
  object.
303
332
 
304
- Wraps get_documenter and is meant as a hook for extensions.
333
+ Wraps _get_documenter and is meant as a hook for extensions.
305
334
  """
306
- doccls = get_documenter(app, obj, parent)
335
+ doccls = _get_documenter(obj, parent, registry=registry)
307
336
  return doccls(self.bridge, full_name)
308
337
 
309
- def get_items(self, names: list[str]) -> list[tuple[str, str, str, str]]:
338
+ def get_items(self, names: list[str]) -> list[tuple[str, str | None, str, str]]:
310
339
  """Try to import the given names, and return a list of
311
340
  ``[(name, signature, summary_string, real_name), ...]``.
341
+
342
+ signature is already formatted and is None if :nosignatures: option was given.
312
343
  """
313
344
  prefixes = get_import_prefixes_from_env(self.env)
314
345
 
315
- items: list[tuple[str, str, str, str]] = []
346
+ items: list[tuple[str, str | None, str, str]] = []
347
+
348
+ signatures_option = self.options.get('signatures')
349
+ if signatures_option is None:
350
+ signatures_option = 'none' if 'nosignatures' in self.options else 'long'
351
+ if signatures_option not in {'none', 'short', 'long'}:
352
+ msg = (
353
+ 'Invalid value for autosummary :signatures: option: '
354
+ f"{signatures_option!r}. Valid values are 'none', 'short', 'long'"
355
+ )
356
+ raise ValueError(msg)
316
357
 
317
358
  max_item_chars = 50
318
359
 
@@ -323,11 +364,17 @@ class Autosummary(SphinxDirective):
323
364
  display_name = name.split('.')[-1]
324
365
 
325
366
  try:
326
- real_name, obj, parent, modname = self.import_by_name(name, prefixes=prefixes)
367
+ real_name, obj, parent, modname = self.import_by_name(
368
+ name, prefixes=prefixes
369
+ )
327
370
  except ImportExceptionGroup as exc:
328
- errors = list({f"* {type(e).__name__}: {e}" for e in exc.exceptions})
329
- logger.warning(__('autosummary: failed to import %s.\nPossible hints:\n%s'),
330
- name, '\n'.join(errors), location=self.get_location())
371
+ errors = list({f'* {type(e).__name__}: {e}' for e in exc.exceptions})
372
+ logger.warning(
373
+ __('autosummary: failed to import %s.\nPossible hints:\n%s'),
374
+ name,
375
+ '\n'.join(errors),
376
+ location=self.get_location(),
377
+ )
331
378
  continue
332
379
 
333
380
  self.bridge.result = StringList() # initialize for each documenter
@@ -335,25 +382,34 @@ class Autosummary(SphinxDirective):
335
382
  if not isinstance(obj, ModuleType):
336
383
  # give explicitly separated module name, so that members
337
384
  # of inner classes can be documented
338
- full_name = modname + '::' + full_name[len(modname) + 1:]
385
+ full_name = modname + '::' + full_name[len(modname) + 1 :]
339
386
  # NB. using full_name here is important, since Documenters
340
387
  # handle module prefixes slightly differently
341
- documenter = self.create_documenter(self.env.app, obj, parent, full_name)
388
+ documenter = self.create_documenter(
389
+ obj, parent, full_name, registry=self.env._registry
390
+ )
342
391
  if not documenter.parse_name():
343
- logger.warning(__('failed to parse name %s'), real_name,
344
- location=self.get_location())
392
+ logger.warning(
393
+ __('failed to parse name %s'),
394
+ real_name,
395
+ location=self.get_location(),
396
+ )
345
397
  items.append((display_name, '', '', real_name))
346
398
  continue
347
399
  if not documenter.import_object():
348
- logger.warning(__('failed to import object %s'), real_name,
349
- location=self.get_location())
400
+ logger.warning(
401
+ __('failed to import object %s'),
402
+ real_name,
403
+ location=self.get_location(),
404
+ )
350
405
  items.append((display_name, '', '', real_name))
351
406
  continue
352
407
 
353
408
  # try to also get a source code analyzer for attribute docs
354
409
  try:
355
410
  documenter.analyzer = ModuleAnalyzer.for_module(
356
- documenter.get_real_modname())
411
+ documenter.get_real_modname()
412
+ )
357
413
  # parse right now, to get PycodeErrors on parsing (results will
358
414
  # be cached anyway)
359
415
  documenter.analyzer.find_attr_docs()
@@ -364,17 +420,22 @@ class Autosummary(SphinxDirective):
364
420
 
365
421
  # -- Grab the signature
366
422
 
367
- try:
368
- sig = documenter.format_signature(show_annotation=False)
369
- except TypeError:
370
- # the documenter does not support ``show_annotation`` option
371
- sig = documenter.format_signature()
372
-
373
- if not sig:
374
- sig = ''
423
+ if signatures_option == 'none':
424
+ sig = None
375
425
  else:
376
- max_chars = max(10, max_item_chars - len(display_name))
377
- sig = mangle_signature(sig, max_chars=max_chars)
426
+ try:
427
+ sig = documenter.format_signature(show_annotation=False)
428
+ except TypeError:
429
+ # the documenter does not support ``show_annotation`` option
430
+ sig = documenter.format_signature()
431
+ if not sig:
432
+ sig = ''
433
+ elif signatures_option == 'short':
434
+ if sig != '()':
435
+ sig = '(…)'
436
+ else: # signatures_option == 'long'
437
+ max_chars = max(10, max_item_chars - len(display_name))
438
+ sig = mangle_signature(sig, max_chars=max_chars)
378
439
 
379
440
  # -- Grab the summary
380
441
 
@@ -388,7 +449,7 @@ class Autosummary(SphinxDirective):
388
449
 
389
450
  return items
390
451
 
391
- def get_table(self, items: list[tuple[str, str, str, str]]) -> list[Node]:
452
+ def get_table(self, items: list[tuple[str, str | None, str, str]]) -> list[Node]:
392
453
  """Generate a proper list of table nodes for autosummary:: directive.
393
454
 
394
455
  *items* is a list produced by :meth:`get_items`.
@@ -397,7 +458,9 @@ class Autosummary(SphinxDirective):
397
458
  table_spec['spec'] = r'\X{1}{2}\X{1}{2}'
398
459
 
399
460
  table = autosummary_table('')
400
- real_table = nodes.table('', classes=['autosummary longtable'])
461
+ real_table = nodes.table(
462
+ '', classes=['autosummary', 'longtable', *self.options.get('class', ())]
463
+ )
401
464
  table.append(real_table)
402
465
  group = nodes.tgroup('', cols=2)
403
466
  real_table.append(group)
@@ -412,8 +475,9 @@ class Autosummary(SphinxDirective):
412
475
  for text in column_texts:
413
476
  vl = StringList([text], f'{source}:{line}:<autosummary>')
414
477
  with switch_source_input(self.state, vl):
415
- col_nodes = nested_parse_to_nodes(self.state, vl,
416
- allow_section_headings=False)
478
+ col_nodes = nested_parse_to_nodes(
479
+ self.state, vl, allow_section_headings=False
480
+ )
417
481
  if col_nodes and isinstance(col_nodes[0], nodes.paragraph):
418
482
  node = col_nodes[0]
419
483
  else:
@@ -423,10 +487,11 @@ class Autosummary(SphinxDirective):
423
487
 
424
488
  for name, sig, summary, real_name in items:
425
489
  qualifier = 'obj'
426
- if 'nosignatures' not in self.options:
427
- col1 = f':py:{qualifier}:`{name} <{real_name}>`\\ {rst.escape(sig)}'
428
- else:
490
+ if sig is None:
429
491
  col1 = f':py:{qualifier}:`{name} <{real_name}>`'
492
+ else:
493
+ col1 = f':py:{qualifier}:`{name} <{real_name}>`\\ {rst.escape(sig)}'
494
+
430
495
  col2 = summary
431
496
  append_row(col1, col2)
432
497
 
@@ -463,31 +528,33 @@ def mangle_signature(sig: str, max_chars: int = 30) -> str:
463
528
  s = _cleanup_signature(sig)
464
529
 
465
530
  # Strip return type annotation
466
- s = re.sub(r"\)\s*->\s.*$", ")", s)
531
+ s = re.sub(r'\)\s*->\s.*$', ')', s)
467
532
 
468
533
  # Remove parenthesis
469
- s = re.sub(r"^\((.*)\)$", r"\1", s).strip()
534
+ s = re.sub(r'^\((.*)\)$', r'\1', s).strip()
470
535
 
471
536
  # Strip literals (which can contain things that confuse the code below)
472
- s = re.sub(r"\\\\", "", s) # escaped backslash (maybe inside string)
473
- s = re.sub(r"\\'", "", s) # escaped single quote
474
- s = re.sub(r'\\"', "", s) # escaped double quote
475
- s = re.sub(r"'[^']*'", "", s) # string literal (w/ single quote)
476
- s = re.sub(r'"[^"]*"', "", s) # string literal (w/ double quote)
537
+ s = re.sub(r'\\\\', '', s) # escaped backslash (maybe inside string)
538
+ s = re.sub(r"\\'", '', s) # escaped single quote
539
+ s = re.sub(r'\\"', '', s) # escaped double quote
540
+ s = re.sub(r"'[^']*'", '', s) # string literal (w/ single quote)
541
+ s = re.sub(r'"[^"]*"', '', s) # string literal (w/ double quote)
477
542
 
478
543
  # Strip complex objects (maybe default value of arguments)
479
- while re.search(r'\([^)]*\)', s): # contents of parenthesis (ex. NamedTuple(attr=...))
544
+ while re.search(
545
+ r'\([^)]*\)', s
546
+ ): # contents of parenthesis (ex. NamedTuple(attr=...))
480
547
  s = re.sub(r'\([^)]*\)', '', s)
481
- while re.search(r'<[^>]*>', s): # contents of angle brackets (ex. <object>)
548
+ while re.search(r'<[^>]*>', s): # contents of angle brackets (ex. <object>)
482
549
  s = re.sub(r'<[^>]*>', '', s)
483
- while re.search(r'{[^}]*}', s): # contents of curly brackets (ex. dict)
550
+ while re.search(r'{[^}]*}', s): # contents of curly brackets (ex. dict)
484
551
  s = re.sub(r'{[^}]*}', '', s)
485
552
 
486
553
  # Parse the signature to arguments + options
487
554
  args: list[str] = []
488
555
  opts: list[str] = []
489
556
 
490
- opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)\s*=\s*")
557
+ opt_re = re.compile(r'^(.*, |)([a-zA-Z0-9_*]+)\s*=\s*')
491
558
  while s:
492
559
  m = opt_re.search(s)
493
560
  if not m:
@@ -506,19 +573,21 @@ def mangle_signature(sig: str, max_chars: int = 30) -> str:
506
573
  opts[i] = strip_arg_typehint(opt)
507
574
 
508
575
  # Produce a more compact signature
509
- sig = limited_join(", ", args, max_chars=max_chars - 2)
576
+ sig = limited_join(', ', args, max_chars=max_chars - 2)
510
577
  if opts:
511
578
  if not sig:
512
- sig = "[%s]" % limited_join(", ", opts, max_chars=max_chars - 4)
579
+ sig = '[%s]' % limited_join(', ', opts, max_chars=max_chars - 4)
513
580
  elif len(sig) < max_chars - 4 - 2 - 3:
514
- sig += "[, %s]" % limited_join(", ", opts,
515
- max_chars=max_chars - len(sig) - 4 - 2)
581
+ sig += '[, %s]' % limited_join(
582
+ ', ', opts, max_chars=max_chars - len(sig) - 4 - 2
583
+ )
516
584
 
517
- return "(%s)" % sig
585
+ return '(%s)' % sig
518
586
 
519
587
 
520
588
  def extract_summary(doc: list[str], document: Any) -> str:
521
589
  """Extract summary from docstring."""
590
+
522
591
  def parse(doc: list[str], settings: Any) -> nodes.document:
523
592
  state_machine = RSTStateMachine(state_classes, 'Body')
524
593
  node = new_document('', settings)
@@ -552,13 +621,13 @@ def extract_summary(doc: list[str], document: Any) -> str:
552
621
  summary = doc[0].strip()
553
622
  else:
554
623
  # Try to find the "first sentence", which may span multiple lines
555
- sentences = periods_re.split(" ".join(doc))
624
+ sentences = periods_re.split(' '.join(doc))
556
625
  if len(sentences) == 1:
557
626
  summary = sentences[0].strip()
558
627
  else:
559
628
  summary = ''
560
629
  for i in range(len(sentences)):
561
- summary = ". ".join(sentences[:i + 1]).rstrip(".") + "."
630
+ summary = '. '.join(sentences[: i + 1]).rstrip('.') + '.'
562
631
  node[:] = []
563
632
  node = parse(doc, document.settings)
564
633
  if summary.endswith(WELL_KNOWN_ABBREVIATIONS):
@@ -573,8 +642,9 @@ def extract_summary(doc: list[str], document: Any) -> str:
573
642
  return summary
574
643
 
575
644
 
576
- def limited_join(sep: str, items: list[str], max_chars: int = 30,
577
- overflow_marker: str = "...") -> str:
645
+ def limited_join(
646
+ sep: str, items: list[str], max_chars: int = 30, overflow_marker: str = '...'
647
+ ) -> str:
578
648
  """Join a number of strings into one, limiting the length to *max_chars*.
579
649
 
580
650
  If the string overflows this limit, replace the last fitting item by
@@ -607,14 +677,15 @@ class ImportExceptionGroup(Exception):
607
677
  It contains an error messages and a list of exceptions as its arguments.
608
678
  """
609
679
 
610
- def __init__(self, message: str | None, exceptions: Sequence[BaseException]) -> None:
680
+ def __init__(
681
+ self, message: str | None, exceptions: Sequence[BaseException]
682
+ ) -> None:
611
683
  super().__init__(message)
612
684
  self.exceptions = list(exceptions)
613
685
 
614
686
 
615
687
  def get_import_prefixes_from_env(env: BuildEnvironment) -> list[str | None]:
616
- """
617
- Obtain current Python import prefixes (for `import_by_name`)
688
+ """Obtain current Python import prefixes (for `import_by_name`)
618
689
  from ``document.env``
619
690
  """
620
691
  prefixes: list[str | None] = [None]
@@ -626,7 +697,7 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> list[str | None]:
626
697
  currclass = env.ref_context.get('py:class')
627
698
  if currclass:
628
699
  if currmodule:
629
- prefixes.insert(0, currmodule + "." + currclass)
700
+ prefixes.insert(0, f'{currmodule}.{currclass}')
630
701
  else:
631
702
  prefixes.insert(0, currclass)
632
703
 
@@ -634,7 +705,7 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> list[str | None]:
634
705
 
635
706
 
636
707
  def import_by_name(
637
- name: str, prefixes: Sequence[str | None] = (None,),
708
+ name: str, prefixes: Sequence[str | None] = (None,)
638
709
  ) -> tuple[str, Any, Any, str]:
639
710
  """Import a Python object that has the given *name*, under one of the
640
711
  *prefixes*. The first name that succeeds is used.
@@ -644,17 +715,26 @@ def import_by_name(
644
715
  for prefix in prefixes:
645
716
  if prefix is not None and name.startswith(f'{prefix}.'):
646
717
  # Catch and avoid module cycles (e.g., sphinx.ext.sphinx.ext...)
647
- msg = __('Summarised items should not include the current module. '
648
- 'Replace %r with %r.')
649
- logger.warning(msg, name, name.removeprefix(f'{prefix}.'),
650
- type='autosummary', subtype='import_cycle')
718
+ msg = __(
719
+ 'Summarised items should not include the current module. '
720
+ 'Replace %r with %r.'
721
+ )
722
+ logger.warning(
723
+ msg,
724
+ name,
725
+ name.removeprefix(f'{prefix}.'),
726
+ type='autosummary',
727
+ subtype='import_cycle',
728
+ )
651
729
  continue
652
730
  try:
653
731
  if prefix:
654
732
  prefixed_name = f'{prefix}.{name}'
655
733
  else:
656
734
  prefixed_name = name
657
- obj, parent, modname = _import_by_name(prefixed_name, grouped_exception=True)
735
+ obj, parent, modname = _import_by_name(
736
+ prefixed_name, grouped_exception=True
737
+ )
658
738
  return prefixed_name, obj, parent, modname
659
739
  except ImportError:
660
740
  tried.append(prefixed_name)
@@ -663,7 +743,8 @@ def import_by_name(
663
743
  errors.append(exc)
664
744
 
665
745
  exceptions: list[BaseException] = functools.reduce(
666
- operator.iadd, (e.exceptions for e in errors), [])
746
+ operator.iadd, (e.exceptions for e in errors), []
747
+ )
667
748
  raise ImportExceptionGroup('no module named %s' % ' or '.join(tried), exceptions)
668
749
 
669
750
 
@@ -714,13 +795,14 @@ def _import_by_name(name: str, grouped_exception: bool = True) -> tuple[Any, Any
714
795
  raise ImportError(*exc.args) from exc
715
796
 
716
797
 
717
- def import_ivar_by_name(name: str, prefixes: Sequence[str | None] = (None,),
718
- grouped_exception: bool = True) -> tuple[str, Any, Any, str]:
798
+ def import_ivar_by_name(
799
+ name: str, prefixes: Sequence[str | None] = (None,), grouped_exception: bool = True
800
+ ) -> tuple[str, Any, Any, str]:
719
801
  """Import an instance variable that has the given *name*, under one of the
720
802
  *prefixes*. The first name that succeeds is used.
721
803
  """
722
804
  try:
723
- name, attr = name.rsplit(".", 1)
805
+ name, attr = name.rsplit('.', 1)
724
806
  real_name, obj, parent, modname = import_by_name(name, prefixes)
725
807
 
726
808
  # Get ancestors of the object (class.__mro__ includes the class itself as
@@ -730,14 +812,16 @@ def import_ivar_by_name(name: str, prefixes: Sequence[str | None] = (None,),
730
812
  candidate_objects = (obj,)
731
813
 
732
814
  for candidate_obj in candidate_objects:
733
- analyzer = ModuleAnalyzer.for_module(getattr(candidate_obj, '__module__', modname))
815
+ analyzer = ModuleAnalyzer.for_module(
816
+ getattr(candidate_obj, '__module__', modname)
817
+ )
734
818
  analyzer.analyze()
735
819
  # check for presence in `annotations` to include dataclass attributes
736
820
  found_attrs = set()
737
821
  found_attrs |= {attr for (qualname, attr) in analyzer.attr_docs}
738
822
  found_attrs |= {attr for (qualname, attr) in analyzer.annotations}
739
823
  if attr in found_attrs:
740
- return real_name + "." + attr, INSTANCEATTR, obj, modname
824
+ return f'{real_name}.{attr}', INSTANCEATTR, obj, modname
741
825
  except (ImportError, ValueError, PycodeError) as exc:
742
826
  raise ImportError from exc
743
827
  except ImportExceptionGroup:
@@ -748,6 +832,7 @@ def import_ivar_by_name(name: str, prefixes: Sequence[str | None] = (None,),
748
832
 
749
833
  # -- :autolink: (smart default role) -------------------------------------------
750
834
 
835
+
751
836
  class AutoLink(SphinxRole):
752
837
  """Smart linking role.
753
838
 
@@ -758,13 +843,20 @@ class AutoLink(SphinxRole):
758
843
  def run(self) -> tuple[list[Node], list[system_message]]:
759
844
  pyobj_role = self.env.domains.python_domain.role('obj')
760
845
  assert pyobj_role is not None
761
- objects, errors = pyobj_role('obj', self.rawtext, self.text, self.lineno,
762
- self.inliner, self.options, self.content)
846
+ objects, errors = pyobj_role(
847
+ 'obj',
848
+ self.rawtext,
849
+ self.text,
850
+ self.lineno,
851
+ self.inliner,
852
+ self.options,
853
+ self.content,
854
+ )
763
855
  if errors:
764
856
  return objects, errors
765
857
 
766
858
  assert len(objects) == 1
767
- pending_xref = cast(addnodes.pending_xref, objects[0])
859
+ pending_xref = cast('addnodes.pending_xref', objects[0])
768
860
  try:
769
861
  # try to import object by name
770
862
  prefixes = get_import_prefixes_from_env(self.env)
@@ -777,9 +869,10 @@ class AutoLink(SphinxRole):
777
869
  ]
778
870
  import_by_name(name, prefixes)
779
871
  except ImportExceptionGroup:
780
- literal = cast(nodes.literal, pending_xref[0])
781
- objects[0] = nodes.emphasis(self.rawtext, literal.astext(),
782
- classes=literal['classes'])
872
+ literal = cast('nodes.literal', pending_xref[0])
873
+ objects[0] = nodes.emphasis(
874
+ self.rawtext, literal.astext(), classes=literal['classes']
875
+ )
783
876
 
784
877
  return objects, errors
785
878
 
@@ -803,18 +896,23 @@ def process_generate_options(app: Sphinx) -> None:
803
896
  genfiles = app.config.autosummary_generate
804
897
 
805
898
  if genfiles is True:
806
- env = app.builder.env
807
- genfiles = [str(env.doc2path(x, base=False)) for x in env.found_docs
808
- if os.path.isfile(env.doc2path(x))]
899
+ env = app.env
900
+ genfiles = [
901
+ str(env.doc2path(x, base=False))
902
+ for x in env.found_docs
903
+ if env.doc2path(x).is_file()
904
+ ]
809
905
  elif genfiles is False:
810
906
  pass
811
907
  else:
812
908
  ext = list(app.config.source_suffix)
813
- genfiles = [genfile + (ext[0] if not genfile.endswith(tuple(ext)) else '')
814
- for genfile in genfiles]
909
+ genfiles = [
910
+ genfile + (ext[0] if not genfile.endswith(tuple(ext)) else '')
911
+ for genfile in genfiles
912
+ ]
815
913
 
816
914
  for entry in genfiles[:]:
817
- if not path.isfile(path.join(app.srcdir, entry)):
915
+ if not (app.srcdir / entry).is_file():
818
916
  logger.warning(__('autosummary_generate: file not found: %s'), entry)
819
917
  genfiles.remove(entry)
820
918
 
@@ -823,45 +921,75 @@ def process_generate_options(app: Sphinx) -> None:
823
921
 
824
922
  suffix = get_rst_suffix(app)
825
923
  if suffix is None:
826
- logger.warning(__('autosummary generates .rst files internally. '
827
- 'But your source_suffix does not contain .rst. Skipped.'))
924
+ logger.warning(
925
+ __(
926
+ 'autosummary generates .rst files internally. '
927
+ 'But your source_suffix does not contain .rst. Skipped.'
928
+ )
929
+ )
828
930
  return
829
931
 
830
932
  from sphinx.ext.autosummary.generate import generate_autosummary_docs
831
933
 
832
934
  imported_members = app.config.autosummary_imported_members
833
935
  with mock(app.config.autosummary_mock_imports):
834
- generate_autosummary_docs(genfiles, suffix=suffix, base_path=app.srcdir,
835
- app=app, imported_members=imported_members,
836
- overwrite=app.config.autosummary_generate_overwrite,
837
- encoding=app.config.source_encoding)
936
+ generate_autosummary_docs(
937
+ genfiles,
938
+ suffix=suffix,
939
+ base_path=app.srcdir,
940
+ app=app,
941
+ imported_members=imported_members,
942
+ overwrite=app.config.autosummary_generate_overwrite,
943
+ encoding=app.config.source_encoding,
944
+ )
838
945
 
839
946
 
840
947
  def setup(app: Sphinx) -> ExtensionMetadata:
841
948
  # I need autodoc
842
949
  app.setup_extension('sphinx.ext.autodoc')
843
- app.add_node(autosummary_toc,
844
- html=(autosummary_toc_visit_html, autosummary_noop),
845
- latex=(autosummary_noop, autosummary_noop),
846
- text=(autosummary_noop, autosummary_noop),
847
- man=(autosummary_noop, autosummary_noop),
848
- texinfo=(autosummary_noop, autosummary_noop))
849
- app.add_node(autosummary_table,
850
- html=(autosummary_table_visit_html, autosummary_noop),
851
- latex=(autosummary_noop, autosummary_noop),
852
- text=(autosummary_noop, autosummary_noop),
853
- man=(autosummary_noop, autosummary_noop),
854
- texinfo=(autosummary_noop, autosummary_noop))
950
+ app.add_node(
951
+ autosummary_toc,
952
+ html=(autosummary_toc_visit_html, autosummary_noop),
953
+ latex=(autosummary_noop, autosummary_noop),
954
+ text=(autosummary_noop, autosummary_noop),
955
+ man=(autosummary_noop, autosummary_noop),
956
+ texinfo=(autosummary_noop, autosummary_noop),
957
+ )
958
+ app.add_node(
959
+ autosummary_table,
960
+ html=(autosummary_table_visit_html, autosummary_noop),
961
+ latex=(autosummary_noop, autosummary_noop),
962
+ text=(autosummary_noop, autosummary_noop),
963
+ man=(autosummary_noop, autosummary_noop),
964
+ texinfo=(autosummary_noop, autosummary_noop),
965
+ )
855
966
  app.add_directive('autosummary', Autosummary)
856
967
  app.add_role('autolink', AutoLink())
857
968
  app.connect('builder-inited', process_generate_options)
858
- app.add_config_value('autosummary_context', {}, 'env')
859
- app.add_config_value('autosummary_filename_map', {}, 'html')
860
- app.add_config_value('autosummary_generate', True, 'env', {bool, list})
861
- app.add_config_value('autosummary_generate_overwrite', True, '')
862
- app.add_config_value('autosummary_mock_imports',
863
- lambda config: config.autodoc_mock_imports, 'env')
864
- app.add_config_value('autosummary_imported_members', [], '', bool)
865
- app.add_config_value('autosummary_ignore_module_all', True, 'env', bool)
866
-
867
- return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
969
+ app.add_config_value('autosummary_context', {}, 'env', types=frozenset({dict}))
970
+ app.add_config_value(
971
+ 'autosummary_filename_map', {}, 'html', types=frozenset({dict})
972
+ )
973
+ app.add_config_value(
974
+ 'autosummary_generate', True, 'env', types=frozenset({bool, list})
975
+ )
976
+ app.add_config_value(
977
+ 'autosummary_generate_overwrite', True, '', types=frozenset({bool})
978
+ )
979
+ app.add_config_value(
980
+ 'autosummary_mock_imports',
981
+ lambda config: config.autodoc_mock_imports,
982
+ 'env',
983
+ types=frozenset({list, tuple}),
984
+ )
985
+ app.add_config_value(
986
+ 'autosummary_imported_members', False, '', types=frozenset({bool})
987
+ )
988
+ app.add_config_value(
989
+ 'autosummary_ignore_module_all', True, 'env', types=frozenset({bool})
990
+ )
991
+
992
+ return {
993
+ 'version': sphinx.__display_version__,
994
+ 'parallel_read_safe': True,
995
+ }