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
sphinx/util/docutils.py CHANGED
@@ -4,22 +4,20 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import re
7
- from collections.abc import Sequence # NoQA: TCH003
8
7
  from contextlib import contextmanager
9
8
  from copy import copy
10
- from os import path
11
- from typing import IO, TYPE_CHECKING, Any, cast
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING
12
11
 
13
12
  import docutils
14
13
  from docutils import nodes
15
14
  from docutils.io import FileOutput
16
15
  from docutils.parsers.rst import Directive, directives, roles
17
- from docutils.parsers.rst.states import Inliner # NoQA: TCH002
18
- from docutils.statemachine import State, StateMachine, StringList
16
+ from docutils.statemachine import StateMachine
19
17
  from docutils.utils import Reporter, unescape
20
18
 
21
19
  from sphinx.errors import SphinxError
22
- from sphinx.locale import _, __
20
+ from sphinx.locale import __
23
21
  from sphinx.util import logging
24
22
  from sphinx.util.parsing import nested_parse_to_nodes
25
23
 
@@ -29,17 +27,44 @@ report_re = re.compile(
29
27
  )
30
28
 
31
29
  if TYPE_CHECKING:
32
- from collections.abc import Callable, Iterator # NoQA: TCH003
33
- from types import ModuleType
30
+ from collections.abc import Iterator, Sequence
31
+ from types import ModuleType, TracebackType
32
+ from typing import Any, Protocol
34
33
 
35
34
  from docutils.frontend import Values
36
35
  from docutils.nodes import Element, Node, system_message
36
+ from docutils.parsers.rst.states import Inliner
37
+ from docutils.statemachine import State, StringList
37
38
 
38
39
  from sphinx.builders import Builder
39
40
  from sphinx.config import Config
40
41
  from sphinx.environment import BuildEnvironment
41
42
  from sphinx.util.typing import RoleFunction
42
43
 
44
+ class _LanguageModule(Protocol):
45
+ labels: dict[str, str]
46
+ author_separators: list[str]
47
+ bibliographic_fields: list[str]
48
+
49
+ class _DirectivesDispatcher(Protocol):
50
+ def __call__(
51
+ self,
52
+ directive_name: str,
53
+ language_module: _LanguageModule,
54
+ document: nodes.document,
55
+ /,
56
+ ) -> tuple[type[Directive] | None, list[system_message]]: ...
57
+
58
+ class _RolesDispatcher(Protocol):
59
+ def __call__(
60
+ self,
61
+ role_name: str,
62
+ language_module: _LanguageModule,
63
+ lineno: int,
64
+ reporter: Reporter,
65
+ /,
66
+ ) -> tuple[RoleFunction | None, list[system_message]]: ...
67
+
43
68
 
44
69
  additional_nodes: set[type[Element]] = set()
45
70
 
@@ -127,7 +152,7 @@ def patched_get_language() -> Iterator[None]:
127
152
  """Patch docutils.languages.get_language() temporarily.
128
153
 
129
154
  This ignores the second argument ``reporter`` to suppress warnings.
130
- refs: https://github.com/sphinx-doc/sphinx/issues/3788
155
+ See: https://github.com/sphinx-doc/sphinx/issues/3788
131
156
  """
132
157
  from docutils.languages import get_language
133
158
 
@@ -153,7 +178,7 @@ def patched_rst_get_language() -> Iterator[None]:
153
178
  This should also work for old versions of docutils,
154
179
  because reporter is none by default.
155
180
 
156
- refs: https://github.com/sphinx-doc/sphinx/issues/10179
181
+ See: https://github.com/sphinx-doc/sphinx/issues/10179
157
182
  """
158
183
  from docutils.parsers.rst.languages import get_language
159
184
 
@@ -171,25 +196,23 @@ def patched_rst_get_language() -> Iterator[None]:
171
196
 
172
197
 
173
198
  @contextmanager
174
- def using_user_docutils_conf(confdir: str | None) -> Iterator[None]:
199
+ def using_user_docutils_conf(confdir: str | os.PathLike[str] | None) -> Iterator[None]:
175
200
  """Let docutils know the location of ``docutils.conf`` for Sphinx."""
176
201
  try:
177
- docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
202
+ docutils_config = os.environ.get('DOCUTILSCONFIG', None)
178
203
  if confdir:
179
- os.environ['DOCUTILSCONFIG'] = path.join(
180
- path.abspath(confdir), 'docutils.conf'
181
- )
182
-
204
+ docutils_conf_path = Path(confdir, 'docutils.conf').resolve()
205
+ os.environ['DOCUTILSCONFIG'] = str(docutils_conf_path)
183
206
  yield
184
207
  finally:
185
- if docutilsconfig is None:
208
+ if docutils_config is None:
186
209
  os.environ.pop('DOCUTILSCONFIG', None)
187
210
  else:
188
- os.environ['DOCUTILSCONFIG'] = docutilsconfig
211
+ os.environ['DOCUTILSCONFIG'] = docutils_config
189
212
 
190
213
 
191
214
  @contextmanager
192
- def patch_docutils(confdir: str | None = None) -> Iterator[None]:
215
+ def patch_docutils(confdir: str | os.PathLike[str] | None = None) -> Iterator[None]:
193
216
  """Patch to docutils temporarily."""
194
217
  with (
195
218
  patched_get_language(),
@@ -207,14 +230,17 @@ class CustomReSTDispatcher:
207
230
  """
208
231
 
209
232
  def __init__(self) -> None:
210
- self.directive_func: Callable = lambda *args: (None, [])
211
- self.roles_func: Callable = lambda *args: (None, [])
233
+ self.directive_func: _DirectivesDispatcher = lambda *args: (None, [])
234
+ self.roles_func: _RolesDispatcher = lambda *args: (None, [])
212
235
 
213
236
  def __enter__(self) -> None:
214
237
  self.enable()
215
238
 
216
239
  def __exit__(
217
- self, exc_type: type[Exception], exc_value: Exception, traceback: Any
240
+ self,
241
+ exc_type: type[BaseException] | None,
242
+ exc_value: BaseException | None,
243
+ traceback: TracebackType | None,
218
244
  ) -> None:
219
245
  self.disable()
220
246
 
@@ -226,7 +252,7 @@ class CustomReSTDispatcher:
226
252
  roles.role = self.role # type: ignore[assignment]
227
253
 
228
254
  def disable(self) -> None:
229
- directives.directive = self.directive_func
255
+ directives.directive = self.directive_func # type: ignore[assignment]
230
256
  roles.role = self.role_func
231
257
 
232
258
  def directive(
@@ -262,51 +288,44 @@ class sphinx_domains(CustomReSTDispatcher):
262
288
  """
263
289
 
264
290
  def __init__(self, env: BuildEnvironment) -> None:
265
- self.env = env
291
+ self.domains = env.domains
292
+ self.current_document = env.current_document
266
293
  super().__init__()
267
294
 
268
- def lookup_domain_element(self, type: str, name: str) -> Any:
269
- """Lookup a markup element (directive or role), given its name which can
270
- be a full name (with domain).
271
- """
272
- name = name.lower()
295
+ def directive(
296
+ self,
297
+ directive_name: str,
298
+ language_module: ModuleType,
299
+ document: nodes.document,
300
+ ) -> tuple[type[Directive] | None, list[system_message]]:
301
+ """Lookup a directive, given its name which can include a domain."""
302
+ directive_name = directive_name.lower()
273
303
  # explicit domain given?
274
- if ':' in name:
275
- domain_name, name = name.split(':', 1)
276
- if domain_name in self.env.domains:
277
- domain = self.env.get_domain(domain_name)
278
- element = getattr(domain, type)(name)
304
+ if ':' in directive_name:
305
+ domain_name, _, name = directive_name.partition(':')
306
+ try:
307
+ domain = self.domains[domain_name]
308
+ except KeyError:
309
+ logger.warning(__('unknown directive name: %s'), directive_name)
310
+ else:
311
+ element = domain.directive(name)
279
312
  if element is not None:
280
313
  return element, []
281
- else:
282
- logger.warning(
283
- _('unknown directive or role name: %s:%s'), domain_name, name
284
- )
285
314
  # else look in the default domain
286
315
  else:
287
- def_domain = self.env.temp_data.get('default_domain')
288
- if def_domain is not None:
289
- element = getattr(def_domain, type)(name)
316
+ name = directive_name
317
+ default_domain = self.current_document.default_domain
318
+ if default_domain is not None:
319
+ element = default_domain.directive(name)
290
320
  if element is not None:
291
321
  return element, []
292
322
 
293
323
  # always look in the std domain
294
- element = getattr(self.env.domains.standard_domain, type)(name)
324
+ element = self.domains.standard_domain.directive(name)
295
325
  if element is not None:
296
326
  return element, []
297
327
 
298
- raise ElementLookupError
299
-
300
- def directive(
301
- self,
302
- directive_name: str,
303
- language_module: ModuleType,
304
- document: nodes.document,
305
- ) -> tuple[type[Directive] | None, list[system_message]]:
306
- try:
307
- return self.lookup_domain_element('directive', directive_name)
308
- except ElementLookupError:
309
- return super().directive(directive_name, language_module, document)
328
+ return super().directive(directive_name, language_module, document)
310
329
 
311
330
  def role(
312
331
  self,
@@ -315,10 +334,34 @@ class sphinx_domains(CustomReSTDispatcher):
315
334
  lineno: int,
316
335
  reporter: Reporter,
317
336
  ) -> tuple[RoleFunction, list[system_message]]:
318
- try:
319
- return self.lookup_domain_element('role', role_name)
320
- except ElementLookupError:
321
- return super().role(role_name, language_module, lineno, reporter)
337
+ """Lookup a role, given its name which can include a domain."""
338
+ role_name = role_name.lower()
339
+ # explicit domain given?
340
+ if ':' in role_name:
341
+ domain_name, _, name = role_name.partition(':')
342
+ try:
343
+ domain = self.domains[domain_name]
344
+ except KeyError:
345
+ logger.warning(__('unknown role name: %s'), role_name)
346
+ else:
347
+ element = domain.role(name)
348
+ if element is not None:
349
+ return element, []
350
+ # else look in the default domain
351
+ else:
352
+ name = role_name
353
+ default_domain = self.current_document.default_domain
354
+ if default_domain is not None:
355
+ element = default_domain.role(name)
356
+ if element is not None:
357
+ return element, []
358
+
359
+ # always look in the std domain
360
+ element = self.domains.standard_domain.role(name)
361
+ if element is not None:
362
+ return element, []
363
+
364
+ return super().role(role_name, language_module, lineno, reporter)
322
365
 
323
366
 
324
367
  class WarningStream:
@@ -354,7 +397,7 @@ class LoggingReporter(Reporter):
354
397
  debug: bool = False,
355
398
  error_handler: str = 'backslashreplace',
356
399
  ) -> None:
357
- stream = cast(IO, WarningStream())
400
+ stream = WarningStream()
358
401
  super().__init__(
359
402
  source, report_level, halt_level, stream, debug, error_handler=error_handler
360
403
  )
@@ -368,7 +411,7 @@ class NullReporter(Reporter):
368
411
 
369
412
 
370
413
  @contextmanager
371
- def switch_source_input(state: State, content: StringList) -> Iterator[None]:
414
+ def switch_source_input(state: State[list[str]], content: StringList) -> Iterator[None]:
372
415
  """Switch current source input of state temporarily."""
373
416
  try:
374
417
  # remember the original ``get_source_and_line()`` method
@@ -377,7 +420,7 @@ def switch_source_input(state: State, content: StringList) -> Iterator[None]:
377
420
  # replace it by new one
378
421
  state_machine: StateMachine[None] = StateMachine([], None) # type: ignore[arg-type]
379
422
  state_machine.input_lines = content
380
- state.memo.reporter.get_source_and_line = state_machine.get_source_and_line # type: ignore[attr-defined] # NoQA: E501
423
+ state.memo.reporter.get_source_and_line = state_machine.get_source_and_line # type: ignore[attr-defined]
381
424
 
382
425
  yield
383
426
  finally:
@@ -402,9 +445,10 @@ class SphinxFileOutput(FileOutput):
402
445
  and os.path.exists(self.destination_path)
403
446
  ):
404
447
  with open(self.destination_path, encoding=self.encoding) as f:
405
- # skip writing: content not changed
406
- if f.read() == data:
407
- return data
448
+ on_disk = f.read()
449
+ # skip writing: content not changed
450
+ if on_disk == data:
451
+ return data
408
452
 
409
453
  return super().write(data)
410
454
 
@@ -572,7 +616,7 @@ class SphinxRole:
572
616
  text: str,
573
617
  lineno: int,
574
618
  inliner: Inliner,
575
- options: dict | None = None,
619
+ options: dict[str, Any] | None = None,
576
620
  content: Sequence[str] = (),
577
621
  ) -> tuple[list[Node], list[system_message]]:
578
622
  self.rawtext = rawtext
@@ -586,7 +630,7 @@ class SphinxRole:
586
630
  if name:
587
631
  self.name = name.lower()
588
632
  else:
589
- self.name = self.env.temp_data.get('default_role', '')
633
+ self.name = self.env.current_document.default_role
590
634
  if not self.name:
591
635
  self.name = self.env.config.default_role
592
636
  if not self.name:
@@ -666,7 +710,7 @@ class ReferenceRole(SphinxRole):
666
710
  text: str,
667
711
  lineno: int,
668
712
  inliner: Inliner,
669
- options: dict | None = None,
713
+ options: dict[str, Any] | None = None,
670
714
  content: Sequence[str] = (),
671
715
  ) -> tuple[list[Node], list[system_message]]:
672
716
  if options is None:
@@ -707,10 +751,10 @@ class SphinxTranslator(nodes.NodeVisitor):
707
751
  self.builder = builder
708
752
  self.config = builder.config
709
753
  self.settings = document.settings
754
+ self._domains = builder.env.domains
710
755
 
711
756
  def dispatch_visit(self, node: Node) -> None:
712
- """
713
- Dispatch node to appropriate visitor method.
757
+ """Dispatch node to appropriate visitor method.
714
758
  The priority of visitor method is:
715
759
 
716
760
  1. ``self.visit_{node_class}()``
@@ -718,7 +762,7 @@ class SphinxTranslator(nodes.NodeVisitor):
718
762
  3. ``self.unknown_visit()``
719
763
  """
720
764
  for node_class in node.__class__.__mro__:
721
- method = getattr(self, 'visit_%s' % (node_class.__name__), None)
765
+ method = getattr(self, 'visit_%s' % node_class.__name__, None)
722
766
  if method:
723
767
  method(node)
724
768
  break
@@ -726,8 +770,7 @@ class SphinxTranslator(nodes.NodeVisitor):
726
770
  super().dispatch_visit(node)
727
771
 
728
772
  def dispatch_departure(self, node: Node) -> None:
729
- """
730
- Dispatch node to appropriate departure method.
773
+ """Dispatch node to appropriate departure method.
731
774
  The priority of departure method is:
732
775
 
733
776
  1. ``self.depart_{node_class}()``
@@ -735,7 +778,7 @@ class SphinxTranslator(nodes.NodeVisitor):
735
778
  3. ``self.unknown_departure()``
736
779
  """
737
780
  for node_class in node.__class__.__mro__:
738
- method = getattr(self, 'depart_%s' % (node_class.__name__), None)
781
+ method = getattr(self, 'depart_%s' % node_class.__name__, None)
739
782
  if method:
740
783
  method(node)
741
784
  break
@@ -758,7 +801,7 @@ def new_document(source_path: str, settings: Any = None) -> nodes.document:
758
801
  caches the result of docutils' and use it on second call for instantiation.
759
802
  This makes an instantiation of document nodes much faster.
760
803
  """
761
- global __document_cache__
804
+ global __document_cache__ # NoQA: PLW0603
762
805
  try:
763
806
  cached_settings, reporter = __document_cache__
764
807
  except NameError:
@@ -770,8 +813,6 @@ def new_document(source_path: str, settings: Any = None) -> nodes.document:
770
813
  settings = copy(cached_settings)
771
814
 
772
815
  # Create a new instance of nodes.document using cached reporter
773
- from sphinx import addnodes
774
-
775
- document = addnodes.document(settings, reporter, source=source_path)
816
+ document = nodes.document(settings, reporter, source=source_path)
776
817
  document.note_source(source_path, -1)
777
818
  return document
sphinx/util/fileutil.py CHANGED
@@ -5,16 +5,15 @@ from __future__ import annotations
5
5
  import os
6
6
  import posixpath
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING, Any
9
-
10
- from docutils.utils import relative_path
8
+ from typing import TYPE_CHECKING
11
9
 
12
10
  from sphinx.locale import __
13
11
  from sphinx.util import logging
14
- from sphinx.util.osutil import copyfile, ensuredir
12
+ from sphinx.util.osutil import _relative_path, copyfile, ensuredir
15
13
 
16
14
  if TYPE_CHECKING:
17
15
  from collections.abc import Callable
16
+ from typing import Any
18
17
 
19
18
  from sphinx.util.template import BaseRenderer
20
19
  from sphinx.util.typing import PathMatcher
@@ -22,16 +21,16 @@ if TYPE_CHECKING:
22
21
  logger = logging.getLogger(__name__)
23
22
 
24
23
 
25
- def _template_basename(filename: str | os.PathLike[str]) -> str | None:
24
+ def _template_basename(filename: Path) -> Path | None:
26
25
  """Given an input filename:
27
26
  If the input looks like a template, then return the filename output should
28
27
  be written to. Otherwise, return no result (None).
29
28
  """
30
- basename = os.path.basename(filename)
31
- if basename.lower().endswith('_t'):
32
- return str(filename)[:-2]
33
- elif basename.lower().endswith('.jinja'):
34
- return str(filename)[:-6]
29
+ basename = filename.name.lower()
30
+ if basename.endswith('_t'):
31
+ return filename.with_name(filename.name[:-2])
32
+ elif basename.endswith('.jinja'):
33
+ return filename.with_name(filename.name[:-6])
35
34
  return None
36
35
 
37
36
 
@@ -54,13 +53,14 @@ def copy_asset_file(
54
53
  :param renderer: The template engine. If not given, SphinxRenderer is used by default
55
54
  :param bool force: Overwrite the destination file even if it exists.
56
55
  """
57
- if not os.path.exists(source):
56
+ source = Path(source)
57
+ if not source.exists():
58
58
  return
59
59
 
60
60
  destination = Path(destination)
61
61
  if destination.is_dir():
62
62
  # Use source filename if destination points a directory
63
- destination /= os.path.basename(source)
63
+ destination /= source.name
64
64
 
65
65
  if _template_basename(source) and context is not None:
66
66
  if renderer is None:
@@ -68,8 +68,7 @@ def copy_asset_file(
68
68
 
69
69
  renderer = SphinxRenderer()
70
70
 
71
- with open(source, encoding='utf-8') as fsrc:
72
- template_content = fsrc.read()
71
+ template_content = source.read_text(encoding='utf-8')
73
72
  rendered_template = renderer.render_string(template_content, context)
74
73
 
75
74
  if not force and destination.exists() and template_content != rendered_template:
@@ -87,15 +86,14 @@ def copy_asset_file(
87
86
  return
88
87
 
89
88
  destination = _template_basename(destination) or destination
90
- with open(destination, 'w', encoding='utf-8') as fdst:
91
- msg = __('Writing evaluated template result to %s')
92
- logger.info(
93
- msg,
94
- os.fsdecode(destination),
95
- type='misc',
96
- subtype='template_evaluation',
97
- )
98
- fdst.write(rendered_template)
89
+ msg = __('Writing evaluated template result to %s')
90
+ logger.info(
91
+ msg,
92
+ os.fsdecode(destination),
93
+ type='misc',
94
+ subtype='template_evaluation',
95
+ )
96
+ destination.write_text(rendered_template, encoding='utf-8')
99
97
  else:
100
98
  copyfile(source, destination, force=force)
101
99
 
@@ -125,7 +123,8 @@ def copy_asset(
125
123
  :param onerror: The error handler.
126
124
  :param bool force: Overwrite the destination file even if it exists.
127
125
  """
128
- if not os.path.exists(source):
126
+ source = Path(source)
127
+ if not source.exists():
129
128
  return
130
129
 
131
130
  if renderer is None:
@@ -134,14 +133,14 @@ def copy_asset(
134
133
  renderer = SphinxRenderer()
135
134
 
136
135
  ensuredir(destination)
137
- if os.path.isfile(source):
136
+ if source.is_file():
138
137
  copy_asset_file(
139
138
  source, destination, context=context, renderer=renderer, force=force
140
139
  )
141
140
  return
142
141
 
143
142
  for root, dirs, files in os.walk(source, followlinks=True):
144
- reldir = relative_path(source, root)
143
+ reldir = _relative_path(Path(root), source).as_posix()
145
144
  for dir in dirs.copy():
146
145
  if excluded(posixpath.join(reldir, dir)):
147
146
  dirs.remove(dir)
sphinx/util/http_date.py CHANGED
@@ -3,6 +3,8 @@
3
3
  Reference: https://www.rfc-editor.org/rfc/rfc7231#section-7.1.1.1
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  import time
7
9
  import warnings
8
10
  from email.utils import parsedate_tz