Sphinx 7.4.7__py3-none-any.whl → 8.0.0__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 (234) hide show
  1. sphinx/__init__.py +2 -2
  2. sphinx/_cli/__init__.py +4 -4
  3. sphinx/application.py +2 -2
  4. sphinx/builders/__init__.py +2 -3
  5. sphinx/builders/_epub_base.py +33 -12
  6. sphinx/builders/changes.py +13 -5
  7. sphinx/builders/epub3.py +6 -2
  8. sphinx/builders/html/__init__.py +88 -58
  9. sphinx/builders/latex/__init__.py +38 -12
  10. sphinx/builders/latex/transforms.py +1 -1
  11. sphinx/builders/linkcheck.py +8 -49
  12. sphinx/builders/texinfo.py +12 -6
  13. sphinx/builders/text.py +7 -3
  14. sphinx/builders/xml.py +7 -3
  15. sphinx/cmd/quickstart.py +10 -20
  16. sphinx/config.py +13 -13
  17. sphinx/deprecation.py +8 -8
  18. sphinx/directives/other.py +2 -3
  19. sphinx/directives/patches.py +2 -2
  20. sphinx/domains/__init__.py +4 -2
  21. sphinx/domains/c/__init__.py +2 -2
  22. sphinx/domains/c/_ast.py +3 -2
  23. sphinx/domains/c/_parser.py +4 -3
  24. sphinx/domains/cpp/__init__.py +2 -2
  25. sphinx/domains/cpp/_ast.py +1 -2
  26. sphinx/domains/cpp/_parser.py +2 -2
  27. sphinx/domains/cpp/_symbol.py +2 -2
  28. sphinx/domains/math.py +1 -1
  29. sphinx/domains/python/_object.py +0 -1
  30. sphinx/domains/std/__init__.py +7 -8
  31. sphinx/environment/__init__.py +15 -32
  32. sphinx/environment/adapters/indexentries.py +4 -6
  33. sphinx/environment/adapters/toctree.py +4 -4
  34. sphinx/environment/collectors/title.py +1 -1
  35. sphinx/environment/collectors/toctree.py +1 -1
  36. sphinx/events.py +3 -1
  37. sphinx/ext/autodoc/__init__.py +17 -63
  38. sphinx/ext/autodoc/directive.py +7 -5
  39. sphinx/ext/autodoc/importer.py +2 -1
  40. sphinx/ext/autodoc/preserve_defaults.py +2 -2
  41. sphinx/ext/autosummary/__init__.py +7 -6
  42. sphinx/ext/autosummary/generate.py +5 -4
  43. sphinx/ext/doctest.py +5 -5
  44. sphinx/ext/graphviz.py +1 -1
  45. sphinx/ext/imgmath.py +1 -1
  46. sphinx/ext/inheritance_diagram.py +1 -1
  47. sphinx/ext/intersphinx/__init__.py +25 -5
  48. sphinx/ext/intersphinx/_cli.py +7 -6
  49. sphinx/ext/intersphinx/_load.py +240 -115
  50. sphinx/ext/intersphinx/_resolve.py +12 -11
  51. sphinx/ext/intersphinx/_shared.py +102 -9
  52. sphinx/ext/mathjax.py +1 -1
  53. sphinx/ext/napoleon/docstring.py +2 -2
  54. sphinx/ext/todo.py +2 -2
  55. sphinx/ext/viewcode.py +2 -1
  56. sphinx/highlighting.py +3 -3
  57. sphinx/io.py +2 -2
  58. sphinx/jinja2glue.py +13 -6
  59. sphinx/locale/__init__.py +4 -3
  60. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  61. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2383 -2186
  62. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  63. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2249 -2052
  64. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  65. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2412 -2215
  66. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  67. sphinx/locale/ca/LC_MESSAGES/sphinx.po +3029 -2832
  68. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  69. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2308 -2111
  70. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  71. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2469 -2272
  72. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  73. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2393 -2196
  74. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  75. sphinx/locale/da/LC_MESSAGES/sphinx.po +2532 -2335
  76. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  77. sphinx/locale/de/LC_MESSAGES/sphinx.po +2492 -2295
  78. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  79. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2250 -2053
  80. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  81. sphinx/locale/el/LC_MESSAGES/sphinx.po +2879 -2682
  82. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  83. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2250 -2053
  84. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  85. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2250 -2053
  86. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  87. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2989 -2792
  88. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  89. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2250 -2053
  90. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  91. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2297 -2100
  92. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  93. sphinx/locale/es/LC_MESSAGES/sphinx.po +3017 -2820
  94. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  95. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2250 -2053
  96. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  97. sphinx/locale/et/LC_MESSAGES/sphinx.po +2748 -2551
  98. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2459 -2262
  100. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/fa/LC_MESSAGES/sphinx.po +2957 -2760
  102. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/fi/LC_MESSAGES/sphinx.po +2321 -2124
  104. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/fr/LC_MESSAGES/sphinx.po +2977 -2780
  106. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +2250 -2053
  108. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2992 -2795
  110. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/he/LC_MESSAGES/sphinx.po +2375 -2178
  112. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/hi/LC_MESSAGES/sphinx.po +2937 -2740
  114. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +2250 -2053
  116. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/hr/LC_MESSAGES/sphinx.po +2532 -2335
  118. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/hu/LC_MESSAGES/sphinx.po +2505 -2308
  120. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/id/LC_MESSAGES/sphinx.po +2925 -2728
  122. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/is/LC_MESSAGES/sphinx.po +2307 -2110
  124. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/it/LC_MESSAGES/sphinx.po +2514 -2317
  126. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2970 -2773
  128. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2868 -2671
  130. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/ko/LC_MESSAGES/sphinx.po +3016 -2819
  132. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2476 -2279
  134. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2477 -2280
  136. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  137. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2292 -2095
  138. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  139. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2479 -2282
  140. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  141. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2481 -2284
  142. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  143. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2557 -2360
  144. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  145. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2696 -2499
  146. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  147. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2250 -2053
  148. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  149. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2979 -2782
  150. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  151. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2469 -2272
  152. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  153. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2473 -2276
  154. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  155. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2746 -2549
  156. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  157. sphinx/locale/si/LC_MESSAGES/sphinx.po +2331 -2134
  158. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  159. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2966 -2769
  160. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  161. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2404 -2207
  162. sphinx/locale/sphinx.pot +2262 -2065
  163. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2972 -2775
  165. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2440 -2243
  167. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2483 -2286
  169. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/te/LC_MESSAGES/sphinx.po +2250 -2053
  171. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2892 -2695
  173. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2400 -2203
  175. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2250 -2053
  177. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2422 -2225
  179. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2250 -2053
  181. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2250 -2053
  183. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +3028 -2831
  185. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2250 -2053
  187. sphinx/project.py +25 -20
  188. sphinx/pycode/ast.py +2 -2
  189. sphinx/pycode/parser.py +2 -2
  190. sphinx/pygments_styles.py +3 -3
  191. sphinx/registry.py +3 -8
  192. sphinx/search/__init__.py +1 -1
  193. sphinx/testing/path.py +2 -1
  194. sphinx/testing/util.py +1 -1
  195. sphinx/texinputs/Makefile.jinja +2 -1
  196. sphinx/texinputs_win/Makefile.jinja +2 -1
  197. sphinx/theming.py +3 -12
  198. sphinx/transforms/__init__.py +5 -5
  199. sphinx/transforms/references.py +1 -1
  200. sphinx/util/__init__.py +11 -35
  201. sphinx/util/_pathlib.py +31 -19
  202. sphinx/util/_timestamps.py +12 -0
  203. sphinx/util/cfamily.py +5 -5
  204. sphinx/util/console.py +4 -3
  205. sphinx/util/display.py +3 -3
  206. sphinx/util/docfields.py +1 -1
  207. sphinx/util/docutils.py +44 -10
  208. sphinx/util/fileutil.py +25 -20
  209. sphinx/util/i18n.py +9 -4
  210. sphinx/util/images.py +3 -2
  211. sphinx/util/inspect.py +28 -43
  212. sphinx/util/inventory.py +2 -2
  213. sphinx/util/matching.py +2 -2
  214. sphinx/util/math.py +1 -1
  215. sphinx/util/nodes.py +8 -8
  216. sphinx/util/osutil.py +36 -32
  217. sphinx/util/parallel.py +2 -2
  218. sphinx/util/requests.py +1 -1
  219. sphinx/util/template.py +3 -3
  220. sphinx/util/typing.py +36 -72
  221. sphinx/writers/html.py +1 -1
  222. sphinx/writers/html5.py +1 -1
  223. sphinx/writers/latex.py +4 -4
  224. sphinx/writers/manpage.py +2 -2
  225. sphinx/writers/texinfo.py +5 -5
  226. sphinx/writers/text.py +4 -4
  227. sphinx/writers/xml.py +2 -2
  228. {sphinx-7.4.7.dist-info → sphinx-8.0.0.dist-info}/METADATA +11 -10
  229. {sphinx-7.4.7.dist-info → sphinx-8.0.0.dist-info}/RECORD +232 -233
  230. sphinx/templates/quickstart/Makefile.jinja +0 -98
  231. sphinx/templates/quickstart/make.bat.jinja +0 -110
  232. {sphinx-7.4.7.dist-info → sphinx-8.0.0.dist-info}/LICENSE.rst +0 -0
  233. {sphinx-7.4.7.dist-info → sphinx-8.0.0.dist-info}/WHEEL +0 -0
  234. {sphinx-7.4.7.dist-info → sphinx-8.0.0.dist-info}/entry_points.txt +0 -0
sphinx/util/docfields.py CHANGED
@@ -356,7 +356,7 @@ class DocFieldTransformer:
356
356
  if is_typefield:
357
357
  # filter out only inline nodes; others will result in invalid
358
358
  # markup being written out
359
- content = [n for n in content if isinstance(n, (nodes.Inline, nodes.Text))]
359
+ content = [n for n in content if isinstance(n, nodes.Inline | nodes.Text)]
360
360
  if content:
361
361
  types.setdefault(typename, {})[fieldarg] = content
362
362
  continue
sphinx/util/docutils.py CHANGED
@@ -8,7 +8,7 @@ from collections.abc import Sequence # NoQA: TCH003
8
8
  from contextlib import contextmanager
9
9
  from copy import copy
10
10
  from os import path
11
- from typing import IO, TYPE_CHECKING, Any, Callable, cast
11
+ from typing import IO, TYPE_CHECKING, Any, cast
12
12
 
13
13
  import docutils
14
14
  from docutils import nodes
@@ -27,7 +27,7 @@ logger = logging.getLogger(__name__)
27
27
  report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) ')
28
28
 
29
29
  if TYPE_CHECKING:
30
- from collections.abc import Iterator
30
+ from collections.abc import Callable, Iterator # NoQA: TCH003
31
31
  from types import ModuleType
32
32
 
33
33
  from docutils.frontend import Values
@@ -366,30 +366,47 @@ class SphinxDirective(Directive):
366
366
 
367
367
  This class provides helper methods for Sphinx directives.
368
368
 
369
+ .. versionadded:: 1.8
370
+
369
371
  .. note:: The subclasses of this class might not work with docutils.
370
372
  This class is strongly coupled with Sphinx.
371
373
  """
372
374
 
373
375
  @property
374
376
  def env(self) -> BuildEnvironment:
375
- """Reference to the :class:`.BuildEnvironment` object."""
377
+ """Reference to the :class:`.BuildEnvironment` object.
378
+
379
+ .. versionadded:: 1.8
380
+ """
376
381
  return self.state.document.settings.env
377
382
 
378
383
  @property
379
384
  def config(self) -> Config:
380
- """Reference to the :class:`.Config` object."""
385
+ """Reference to the :class:`.Config` object.
386
+
387
+ .. versionadded:: 1.8
388
+ """
381
389
  return self.env.config
382
390
 
383
391
  def get_source_info(self) -> tuple[str, int]:
384
- """Get source and line number."""
392
+ """Get source and line number.
393
+
394
+ .. versionadded:: 3.0
395
+ """
385
396
  return self.state_machine.get_source_and_line(self.lineno)
386
397
 
387
398
  def set_source_info(self, node: Node) -> None:
388
- """Set source and line number to the node."""
399
+ """Set source and line number to the node.
400
+
401
+ .. versionadded:: 2.1
402
+ """
389
403
  node.source, node.line = self.get_source_info()
390
404
 
391
405
  def get_location(self) -> str:
392
- """Get current location info for logging."""
406
+ """Get current location info for logging.
407
+
408
+ .. versionadded:: 4.2
409
+ """
393
410
  source, line = self.get_source_info()
394
411
  if source and line:
395
412
  return f'{source}:{line}'
@@ -473,6 +490,8 @@ class SphinxRole:
473
490
 
474
491
  This class provides helper methods for Sphinx roles.
475
492
 
493
+ .. versionadded:: 2.0
494
+
476
495
  .. note:: The subclasses of this class might not work with docutils.
477
496
  This class is strongly coupled with Sphinx.
478
497
  """
@@ -517,24 +536,35 @@ class SphinxRole:
517
536
 
518
537
  @property
519
538
  def env(self) -> BuildEnvironment:
520
- """Reference to the :class:`.BuildEnvironment` object."""
539
+ """Reference to the :class:`.BuildEnvironment` object.
540
+
541
+ .. versionadded:: 2.0
542
+ """
521
543
  return self.inliner.document.settings.env
522
544
 
523
545
  @property
524
546
  def config(self) -> Config:
525
- """Reference to the :class:`.Config` object."""
547
+ """Reference to the :class:`.Config` object.
548
+
549
+ .. versionadded:: 2.0
550
+ """
526
551
  return self.env.config
527
552
 
528
553
  def get_source_info(self, lineno: int | None = None) -> tuple[str, int]:
554
+ # .. versionadded:: 3.0
529
555
  if lineno is None:
530
556
  lineno = self.lineno
531
557
  return self.inliner.reporter.get_source_and_line(lineno) # type: ignore[attr-defined]
532
558
 
533
559
  def set_source_info(self, node: Node, lineno: int | None = None) -> None:
560
+ # .. versionadded:: 2.0
534
561
  node.source, node.line = self.get_source_info(lineno)
535
562
 
536
563
  def get_location(self) -> str:
537
- """Get current location info for logging."""
564
+ """Get current location info for logging.
565
+
566
+ .. versionadded:: 4.2
567
+ """
538
568
  source, line = self.get_source_info()
539
569
  if source and line:
540
570
  return f'{source}:{line}'
@@ -551,6 +581,8 @@ class ReferenceRole(SphinxRole):
551
581
  The reference roles can accept ``link title <target>`` style as a text for
552
582
  the role. The parsed result; link title and target will be stored to
553
583
  ``self.title`` and ``self.target``.
584
+
585
+ .. versionadded:: 2.0
554
586
  """
555
587
 
556
588
  has_explicit_title: bool #: A boolean indicates the role has explicit title or not.
@@ -591,6 +623,8 @@ class SphinxTranslator(nodes.NodeVisitor):
591
623
 
592
624
  It also provides helper methods for Sphinx translators.
593
625
 
626
+ .. versionadded:: 2.0
627
+
594
628
  .. note:: The subclasses of this class might not work with docutils.
595
629
  This class is strongly coupled with Sphinx.
596
630
  """
sphinx/util/fileutil.py CHANGED
@@ -4,14 +4,17 @@ from __future__ import annotations
4
4
 
5
5
  import os
6
6
  import posixpath
7
- from typing import TYPE_CHECKING, Any, Callable
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from docutils.utils import relative_path
10
10
 
11
+ from sphinx.locale import __
11
12
  from sphinx.util import logging
12
13
  from sphinx.util.osutil import copyfile, ensuredir
13
14
 
14
15
  if TYPE_CHECKING:
16
+ from collections.abc import Callable
17
+
15
18
  from sphinx.util.template import BaseRenderer
16
19
  from sphinx.util.typing import PathMatcher
17
20
 
@@ -34,7 +37,8 @@ def _template_basename(filename: str | os.PathLike[str]) -> str | None:
34
37
  def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLike[str],
35
38
  context: dict[str, Any] | None = None,
36
39
  renderer: BaseRenderer | None = None,
37
- *, __overwrite_warning__: bool = True) -> None:
40
+ *,
41
+ force: bool = False) -> None:
38
42
  """Copy an asset file to destination.
39
43
 
40
44
  On copying, it expands the template variables if context argument is given and
@@ -44,6 +48,7 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
44
48
  :param destination: The path to destination file or directory
45
49
  :param context: The template variables. If not given, template files are simply copied
46
50
  :param renderer: The template engine. If not given, SphinxRenderer is used by default
51
+ :param bool force: Overwrite the destination file even if it exists.
47
52
  """
48
53
  if not os.path.exists(source):
49
54
  return
@@ -64,45 +69,42 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
64
69
  rendered_template = renderer.render_string(template_content, context)
65
70
 
66
71
  if (
67
- __overwrite_warning__
72
+ not force
68
73
  and os.path.exists(destination)
69
74
  and template_content != rendered_template
70
75
  ):
71
- # Consider raising an error in Sphinx 8.
72
- # Certainly make overwriting user content opt-in.
73
- # xref: RemovedInSphinx80Warning
74
- # xref: https://github.com/sphinx-doc/sphinx/issues/12096
75
- msg = ('Copying the rendered template %s to %s will overwrite data, '
76
- 'as a file already exists at the destination path '
77
- 'and the content does not match.')
78
- # See https://github.com/sphinx-doc/sphinx/pull/12627#issuecomment-2241144330
79
- # for the rationale for logger.info().
80
- logger.info(msg, os.fsdecode(source), os.fsdecode(destination),
81
- type='misc', subtype='copy_overwrite')
76
+ msg = __('Aborted attempted copy from rendered template %s to %s '
77
+ '(the destination path has existing data).')
78
+ logger.warning(msg, os.fsdecode(source), os.fsdecode(destination),
79
+ type='misc', subtype='copy_overwrite')
80
+ return
82
81
 
83
82
  destination = _template_basename(destination) or destination
84
83
  with open(destination, 'w', encoding='utf-8') as fdst:
85
84
  fdst.write(rendered_template)
86
85
  else:
87
- copyfile(source, destination, __overwrite_warning__=__overwrite_warning__)
86
+ copyfile(source, destination, force=force)
88
87
 
89
88
 
90
89
  def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[str],
91
90
  excluded: PathMatcher = lambda path: False,
92
91
  context: dict[str, Any] | None = None, renderer: BaseRenderer | None = None,
93
92
  onerror: Callable[[str, Exception], None] | None = None,
94
- *, __overwrite_warning__: bool = True) -> None:
93
+ *, force: bool = False) -> None:
95
94
  """Copy asset files to destination recursively.
96
95
 
97
96
  On copying, it expands the template variables if context argument is given and
98
97
  the asset is a template file.
99
98
 
99
+ Use ``copy_asset_file`` instead to copy a single file.
100
+
100
101
  :param source: The path to source file or directory
101
102
  :param destination: The path to destination directory
102
103
  :param excluded: The matcher to determine the given path should be copied or not
103
104
  :param context: The template variables. If not given, template files are simply copied
104
105
  :param renderer: The template engine. If not given, SphinxRenderer is used by default
105
106
  :param onerror: The error handler.
107
+ :param bool force: Overwrite the destination file even if it exists.
106
108
  """
107
109
  if not os.path.exists(source):
108
110
  return
@@ -113,8 +115,10 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
113
115
 
114
116
  ensuredir(destination)
115
117
  if os.path.isfile(source):
116
- copy_asset_file(source, destination, context, renderer,
117
- __overwrite_warning__=__overwrite_warning__)
118
+ copy_asset_file(source, destination,
119
+ context=context,
120
+ renderer=renderer,
121
+ force=force)
118
122
  return
119
123
 
120
124
  for root, dirs, files in os.walk(source, followlinks=True):
@@ -130,8 +134,9 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
130
134
  try:
131
135
  copy_asset_file(posixpath.join(root, filename),
132
136
  posixpath.join(destination, reldir),
133
- context, renderer,
134
- __overwrite_warning__=__overwrite_warning__)
137
+ context=context,
138
+ renderer=renderer,
139
+ force=force)
135
140
  except Exception as exc:
136
141
  if onerror:
137
142
  onerror(posixpath.join(root, filename), exc)
sphinx/util/i18n.py CHANGED
@@ -15,12 +15,17 @@ from babel.messages.pofile import read_po
15
15
  from sphinx.errors import SphinxError
16
16
  from sphinx.locale import __
17
17
  from sphinx.util import logging
18
- from sphinx.util.osutil import SEP, canon_path, relpath
18
+ from sphinx.util.osutil import (
19
+ SEP,
20
+ _last_modified_time,
21
+ canon_path,
22
+ relpath,
23
+ )
19
24
 
20
25
  if TYPE_CHECKING:
21
26
  import datetime as dt
22
27
  from collections.abc import Iterator
23
- from typing import Protocol, Union
28
+ from typing import Protocol, TypeAlias
24
29
 
25
30
  from babel.core import Locale
26
31
 
@@ -52,7 +57,7 @@ if TYPE_CHECKING:
52
57
  locale: str | Locale | None = ...,
53
58
  ) -> str: ...
54
59
 
55
- Formatter = Union[DateFormatter, TimeFormatter, DatetimeFormatter]
60
+ Formatter: TypeAlias = DateFormatter | TimeFormatter | DatetimeFormatter
56
61
 
57
62
  logger = logging.getLogger(__name__)
58
63
 
@@ -84,7 +89,7 @@ class CatalogInfo(LocaleFileInfoBase):
84
89
  def is_outdated(self) -> bool:
85
90
  return (
86
91
  not path.exists(self.mo_path) or
87
- path.getmtime(self.mo_path) < path.getmtime(self.po_path))
92
+ _last_modified_time(self.mo_path) < _last_modified_time(self.po_path))
88
93
 
89
94
  def write_mo(self, locale: str, use_fuzzy: bool = False) -> None:
90
95
  with open(self.po_path, encoding=self.charset) as file_po:
sphinx/util/images.py CHANGED
@@ -13,8 +13,9 @@ if TYPE_CHECKING:
13
13
 
14
14
  try:
15
15
  from PIL import Image
16
+ PILLOW_AVAILABLE = True
16
17
  except ImportError:
17
- Image = None
18
+ PILLOW_AVAILABLE = False
18
19
 
19
20
  mime_suffixes = {
20
21
  '.gif': 'image/gif',
@@ -43,7 +44,7 @@ def get_image_size(filename: str) -> tuple[int, int] | None:
43
44
  elif isinstance(size[0], float) or isinstance(size[1], float):
44
45
  size = (int(size[0]), int(size[1]))
45
46
 
46
- if size is None and Image: # fallback to Pillow
47
+ if size is None and PILLOW_AVAILABLE: # fallback to Pillow
47
48
  with Image.open(filename) as im:
48
49
  size = im.size
49
50
 
sphinx/util/inspect.py CHANGED
@@ -17,19 +17,19 @@ from importlib import import_module
17
17
  from inspect import Parameter, Signature
18
18
  from io import StringIO
19
19
  from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType
20
- from typing import TYPE_CHECKING, Any
20
+ from typing import TYPE_CHECKING, Any, ForwardRef
21
21
 
22
22
  from sphinx.pycode.ast import unparse as ast_unparse
23
23
  from sphinx.util import logging
24
- from sphinx.util.typing import ForwardRef, stringify_annotation
24
+ from sphinx.util.typing import stringify_annotation
25
25
 
26
26
  if TYPE_CHECKING:
27
27
  from collections.abc import Callable, Sequence
28
28
  from inspect import _ParameterKind
29
29
  from types import MethodType, ModuleType
30
- from typing import Final, Protocol, Union
30
+ from typing import Final, Protocol, TypeAlias
31
31
 
32
- from typing_extensions import TypeAlias, TypeIs
32
+ from typing_extensions import TypeIs
33
33
 
34
34
  class _SupportsGet(Protocol):
35
35
  def __get__(self, __instance: Any, __owner: type | None = ...) -> Any: ... # NoQA: E704
@@ -42,21 +42,21 @@ if TYPE_CHECKING:
42
42
  # instance is contravariant but we do not need that precision
43
43
  def __delete__(self, __instance: Any) -> None: ... # NoQA: E704
44
44
 
45
- _RoutineType: TypeAlias = Union[
46
- types.FunctionType,
47
- types.LambdaType,
48
- types.MethodType,
49
- types.BuiltinFunctionType,
50
- types.BuiltinMethodType,
51
- types.WrapperDescriptorType,
52
- types.MethodDescriptorType,
53
- types.ClassMethodDescriptorType,
54
- ]
55
- _SignatureType: TypeAlias = Union[
56
- Callable[..., Any],
57
- staticmethod,
58
- classmethod,
59
- ]
45
+ _RoutineType: TypeAlias = (
46
+ types.FunctionType
47
+ | types.LambdaType
48
+ | types.MethodType
49
+ | types.BuiltinFunctionType
50
+ | types.BuiltinMethodType
51
+ | types.WrapperDescriptorType
52
+ | types.MethodDescriptorType
53
+ | types.ClassMethodDescriptorType
54
+ )
55
+ _SignatureType: TypeAlias = (
56
+ Callable[..., Any]
57
+ | staticmethod
58
+ | classmethod
59
+ )
60
60
 
61
61
  logger = logging.getLogger(__name__)
62
62
 
@@ -128,20 +128,14 @@ def getall(obj: Any) -> Sequence[str] | None:
128
128
  __all__ = safe_getattr(obj, '__all__', None)
129
129
  if __all__ is None:
130
130
  return None
131
- if isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__):
131
+ if isinstance(__all__, list | tuple) and all(isinstance(e, str) for e in __all__):
132
132
  return __all__
133
133
  raise ValueError(__all__)
134
134
 
135
135
 
136
136
  def getannotations(obj: Any) -> Mapping[str, Any]:
137
137
  """Safely get the ``__annotations__`` attribute of an object."""
138
- if sys.version_info >= (3, 10, 0) or not isinstance(obj, type):
139
- __annotations__ = safe_getattr(obj, '__annotations__', None)
140
- else:
141
- # Workaround for bugfix not available until python 3.10 as recommended by docs
142
- # https://docs.python.org/3.10/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
143
- __dict__ = safe_getattr(obj, '__dict__', {})
144
- __annotations__ = __dict__.get('__annotations__', None)
138
+ __annotations__ = safe_getattr(obj, '__annotations__', None)
145
139
  if isinstance(__annotations__, Mapping):
146
140
  return __annotations__
147
141
  return {}
@@ -198,21 +192,12 @@ def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None:
198
192
  return __slots__
199
193
  elif isinstance(__slots__, str):
200
194
  return {__slots__: None}
201
- elif isinstance(__slots__, (list, tuple)):
195
+ elif isinstance(__slots__, list | tuple):
202
196
  return dict.fromkeys(__slots__)
203
197
  else:
204
198
  raise ValueError
205
199
 
206
200
 
207
- def isNewType(obj: Any) -> bool:
208
- """Check the if object is a kind of :class:`~typing.NewType`."""
209
- if sys.version_info[:2] >= (3, 10):
210
- return isinstance(obj, typing.NewType)
211
- __module__ = safe_getattr(obj, '__module__', None)
212
- __qualname__ = safe_getattr(obj, '__qualname__', None)
213
- return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type'
214
-
215
-
216
201
  def isenumclass(x: Any) -> TypeIs[type[enum.Enum]]:
217
202
  """Check if the object is an :class:`enumeration class <enum.Enum>`."""
218
203
  return isclass(x) and issubclass(x, enum.Enum)
@@ -237,7 +222,7 @@ def unpartial(obj: Any) -> Any:
237
222
 
238
223
  def ispartial(obj: Any) -> TypeIs[partial | partialmethod]:
239
224
  """Check if the object is a partial function or method."""
240
- return isinstance(obj, (partial, partialmethod))
225
+ return isinstance(obj, partial | partialmethod)
241
226
 
242
227
 
243
228
  def isclassmethod(
@@ -397,12 +382,12 @@ def _is_wrapped_coroutine(obj: Any) -> bool:
397
382
 
398
383
  def isproperty(obj: Any) -> TypeIs[property | cached_property]:
399
384
  """Check if the object is property (possibly cached)."""
400
- return isinstance(obj, (property, cached_property))
385
+ return isinstance(obj, property | cached_property)
401
386
 
402
387
 
403
388
  def isgenericalias(obj: Any) -> TypeIs[types.GenericAlias]:
404
389
  """Check if the object is a generic alias."""
405
- return isinstance(obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
390
+ return isinstance(obj, types.GenericAlias | typing._BaseGenericAlias) # type: ignore[attr-defined]
406
391
 
407
392
 
408
393
  def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
@@ -852,11 +837,11 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
852
837
  params: list[Parameter] = []
853
838
 
854
839
  # positional-only arguments (introduced in Python 3.8)
855
- for arg, defexpr in zip(args.posonlyargs, defaults):
840
+ for arg, defexpr in zip(args.posonlyargs, defaults, strict=False):
856
841
  params.append(_define(Parameter.POSITIONAL_ONLY, arg, code, defexpr=defexpr))
857
842
 
858
843
  # normal arguments
859
- for arg, defexpr in zip(args.args, defaults[pos_only_offset:]):
844
+ for arg, defexpr in zip(args.args, defaults[pos_only_offset:], strict=False):
860
845
  params.append(_define(Parameter.POSITIONAL_OR_KEYWORD, arg, code, defexpr=defexpr))
861
846
 
862
847
  # variadic positional argument (no possible default expression)
@@ -864,7 +849,7 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
864
849
  params.append(_define(Parameter.VAR_POSITIONAL, args.vararg, code, defexpr=None))
865
850
 
866
851
  # keyword-only arguments
867
- for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults):
852
+ for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults, strict=False):
868
853
  params.append(_define(Parameter.KEYWORD_ONLY, arg, code, defexpr=defexpr))
869
854
 
870
855
  # variadic keyword argument (no possible default expression)
sphinx/util/inventory.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
  import os
5
5
  import re
6
6
  import zlib
7
- from typing import IO, TYPE_CHECKING, Callable
7
+ from typing import IO, TYPE_CHECKING
8
8
 
9
9
  from sphinx.locale import __
10
10
  from sphinx.util import logging
@@ -13,7 +13,7 @@ BUFSIZE = 16 * 1024
13
13
  logger = logging.getLogger(__name__)
14
14
 
15
15
  if TYPE_CHECKING:
16
- from collections.abc import Iterator
16
+ from collections.abc import Callable, Iterator
17
17
 
18
18
  from sphinx.builders import Builder
19
19
  from sphinx.environment import BuildEnvironment
sphinx/util/matching.py CHANGED
@@ -4,12 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import os.path
6
6
  import re
7
- from typing import TYPE_CHECKING, Callable
7
+ from typing import TYPE_CHECKING
8
8
 
9
9
  from sphinx.util.osutil import canon_path, path_stabilize
10
10
 
11
11
  if TYPE_CHECKING:
12
- from collections.abc import Iterable, Iterator
12
+ from collections.abc import Callable, Iterable, Iterator
13
13
 
14
14
 
15
15
  def _translate_pattern(pat: str) -> str:
sphinx/util/math.py CHANGED
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
  if TYPE_CHECKING:
8
8
  from docutils import nodes
9
9
 
10
- from sphinx.builders.html import HTML5Translator
10
+ from sphinx.writers.html5 import HTML5Translator
11
11
 
12
12
 
13
13
  def get_node_equation_number(writer: HTML5Translator, node: nodes.math_block) -> str:
sphinx/util/nodes.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import contextlib
6
6
  import re
7
7
  import unicodedata
8
- from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, cast
8
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
9
9
 
10
10
  from docutils import nodes
11
11
  from docutils.nodes import Node
@@ -16,7 +16,7 @@ from sphinx.util import logging
16
16
  from sphinx.util.parsing import _fresh_title_style_context
17
17
 
18
18
  if TYPE_CHECKING:
19
- from collections.abc import Iterable, Iterator
19
+ from collections.abc import Callable, Iterable, Iterator
20
20
 
21
21
  from docutils.nodes import Element
22
22
  from docutils.parsers.rst import Directive
@@ -178,12 +178,12 @@ def apply_source_workaround(node: Element) -> None:
178
178
  return
179
179
 
180
180
  # workaround: some docutils nodes doesn't have source, line.
181
- if (isinstance(node, (
182
- nodes.rubric, # #1305 rubric directive
183
- nodes.line, # #1477 line node
184
- nodes.image, # #3093 image directive in substitution
185
- nodes.field_name, # #3335 field list syntax
186
- ))):
181
+ if isinstance(node, (
182
+ nodes.rubric # #1305 rubric directive
183
+ | nodes.line # #1477 line node
184
+ | nodes.image # #3093 image directive in substitution
185
+ | nodes.field_name # #3335 field list syntax
186
+ )):
187
187
  logger.debug('[i18n] PATCH: %r to have source and line: %s',
188
188
  get_full_module_name(node), repr_domxml(node))
189
189
  try: