Sphinx 8.1.2__py3-none-any.whl → 8.2.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 (328) 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 +50 -40
  55. sphinx/domains/python/__init__.py +402 -211
  56. sphinx/domains/python/_annotations.py +134 -61
  57. sphinx/domains/python/_object.py +155 -68
  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 +66 -0
  73. sphinx/ext/apidoc/__main__.py +9 -0
  74. sphinx/ext/apidoc/_cli.py +356 -0
  75. sphinx/ext/apidoc/_extension.py +262 -0
  76. sphinx/ext/apidoc/_generate.py +356 -0
  77. sphinx/ext/apidoc/_shared.py +99 -0
  78. sphinx/ext/autodoc/__init__.py +837 -483
  79. sphinx/ext/autodoc/directive.py +57 -21
  80. sphinx/ext/autodoc/importer.py +184 -67
  81. sphinx/ext/autodoc/mock.py +25 -10
  82. sphinx/ext/autodoc/preserve_defaults.py +17 -9
  83. sphinx/ext/autodoc/type_comment.py +56 -29
  84. sphinx/ext/autodoc/typehints.py +49 -26
  85. sphinx/ext/autosectionlabel.py +28 -11
  86. sphinx/ext/autosummary/__init__.py +281 -142
  87. sphinx/ext/autosummary/generate.py +121 -51
  88. sphinx/ext/coverage.py +152 -91
  89. sphinx/ext/doctest.py +169 -101
  90. sphinx/ext/duration.py +12 -6
  91. sphinx/ext/extlinks.py +33 -21
  92. sphinx/ext/githubpages.py +8 -8
  93. sphinx/ext/graphviz.py +175 -109
  94. sphinx/ext/ifconfig.py +11 -6
  95. sphinx/ext/imgconverter.py +48 -25
  96. sphinx/ext/imgmath.py +127 -97
  97. sphinx/ext/inheritance_diagram.py +177 -103
  98. sphinx/ext/intersphinx/__init__.py +22 -13
  99. sphinx/ext/intersphinx/__main__.py +3 -1
  100. sphinx/ext/intersphinx/_cli.py +18 -14
  101. sphinx/ext/intersphinx/_load.py +91 -82
  102. sphinx/ext/intersphinx/_resolve.py +108 -74
  103. sphinx/ext/intersphinx/_shared.py +2 -2
  104. sphinx/ext/linkcode.py +28 -12
  105. sphinx/ext/mathjax.py +60 -29
  106. sphinx/ext/napoleon/__init__.py +19 -7
  107. sphinx/ext/napoleon/docstring.py +229 -231
  108. sphinx/ext/todo.py +44 -49
  109. sphinx/ext/viewcode.py +105 -57
  110. sphinx/extension.py +3 -1
  111. sphinx/highlighting.py +13 -7
  112. sphinx/io.py +9 -13
  113. sphinx/jinja2glue.py +29 -26
  114. sphinx/locale/__init__.py +8 -9
  115. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  116. sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
  117. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  118. sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
  119. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  120. sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
  121. sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
  122. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
  124. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
  125. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
  126. sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
  127. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  128. sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
  129. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  130. sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
  131. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  132. sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
  133. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  134. sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
  135. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  136. sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
  137. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
  139. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
  141. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
  143. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
  145. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
  147. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
  149. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
  151. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
  153. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
  155. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
  157. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
  159. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
  161. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
  163. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
  165. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
  167. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
  169. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
  171. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
  173. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
  175. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
  177. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
  179. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
  181. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
  183. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
  185. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
  187. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
  189. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
  191. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  192. sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
  193. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  194. sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
  195. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  196. sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
  197. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  198. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
  199. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  200. sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
  201. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  202. sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
  203. sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
  204. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
  206. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
  208. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
  210. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
  212. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
  214. sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
  215. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
  217. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
  219. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
  221. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
  223. sphinx/locale/sphinx.pot +2069 -1964
  224. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  225. sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
  226. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  227. sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
  228. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  229. sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
  230. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  231. sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
  232. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  233. sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
  234. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
  236. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
  238. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  239. sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
  240. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  241. sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
  242. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  243. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
  244. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  245. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
  246. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  247. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
  248. sphinx/parsers.py +8 -7
  249. sphinx/project.py +2 -2
  250. sphinx/pycode/__init__.py +31 -21
  251. sphinx/pycode/ast.py +6 -3
  252. sphinx/pycode/parser.py +14 -8
  253. sphinx/pygments_styles.py +4 -5
  254. sphinx/registry.py +192 -92
  255. sphinx/roles.py +58 -7
  256. sphinx/search/__init__.py +75 -54
  257. sphinx/search/en.py +11 -13
  258. sphinx/search/fi.py +1 -1
  259. sphinx/search/ja.py +8 -6
  260. sphinx/search/nl.py +1 -1
  261. sphinx/search/zh.py +19 -21
  262. sphinx/testing/fixtures.py +26 -29
  263. sphinx/testing/path.py +26 -62
  264. sphinx/testing/restructuredtext.py +14 -8
  265. sphinx/testing/util.py +21 -19
  266. sphinx/texinputs/make.bat.jinja +50 -50
  267. sphinx/texinputs/sphinx.sty +4 -3
  268. sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
  269. sphinx/texinputs/sphinxlatexobjects.sty +29 -10
  270. sphinx/themes/basic/static/searchtools.js +8 -5
  271. sphinx/theming.py +49 -61
  272. sphinx/transforms/__init__.py +17 -38
  273. sphinx/transforms/compact_bullet_list.py +5 -3
  274. sphinx/transforms/i18n.py +8 -21
  275. sphinx/transforms/post_transforms/__init__.py +142 -93
  276. sphinx/transforms/post_transforms/code.py +5 -5
  277. sphinx/transforms/post_transforms/images.py +28 -24
  278. sphinx/transforms/references.py +3 -1
  279. sphinx/util/__init__.py +109 -60
  280. sphinx/util/_files.py +39 -23
  281. sphinx/util/_importer.py +4 -1
  282. sphinx/util/_inventory_file_reader.py +76 -0
  283. sphinx/util/_io.py +2 -2
  284. sphinx/util/_lines.py +6 -3
  285. sphinx/util/_pathlib.py +40 -2
  286. sphinx/util/build_phase.py +2 -0
  287. sphinx/util/cfamily.py +19 -14
  288. sphinx/util/console.py +44 -179
  289. sphinx/util/display.py +9 -10
  290. sphinx/util/docfields.py +140 -122
  291. sphinx/util/docstrings.py +1 -1
  292. sphinx/util/docutils.py +118 -77
  293. sphinx/util/fileutil.py +25 -26
  294. sphinx/util/http_date.py +2 -0
  295. sphinx/util/i18n.py +77 -64
  296. sphinx/util/images.py +8 -6
  297. sphinx/util/inspect.py +147 -38
  298. sphinx/util/inventory.py +215 -116
  299. sphinx/util/logging.py +33 -33
  300. sphinx/util/matching.py +12 -4
  301. sphinx/util/nodes.py +18 -13
  302. sphinx/util/osutil.py +38 -39
  303. sphinx/util/parallel.py +22 -13
  304. sphinx/util/parsing.py +2 -1
  305. sphinx/util/png.py +6 -2
  306. sphinx/util/requests.py +33 -2
  307. sphinx/util/rst.py +3 -2
  308. sphinx/util/tags.py +1 -1
  309. sphinx/util/template.py +18 -10
  310. sphinx/util/texescape.py +8 -6
  311. sphinx/util/typing.py +148 -122
  312. sphinx/versioning.py +3 -3
  313. sphinx/writers/html.py +3 -1
  314. sphinx/writers/html5.py +63 -52
  315. sphinx/writers/latex.py +83 -67
  316. sphinx/writers/manpage.py +19 -38
  317. sphinx/writers/texinfo.py +47 -47
  318. sphinx/writers/text.py +50 -32
  319. sphinx/writers/xml.py +11 -8
  320. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
  321. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
  322. sphinx-8.2.0.dist-info/RECORD +606 -0
  323. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
  324. sphinx/builders/html/transforms.py +0 -90
  325. sphinx/ext/apidoc.py +0 -721
  326. sphinx/util/exceptions.py +0 -74
  327. sphinx-8.1.2.dist-info/RECORD +0 -598
  328. {sphinx-8.1.2.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
@@ -2,25 +2,27 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import codecs
5
6
  import operator
7
+ import os
8
+ import os.path
6
9
  import time
7
- from codecs import open
8
10
  from collections import defaultdict
9
- from os import getenv, path, walk
11
+ from os import getenv, walk
10
12
  from pathlib import Path
11
- from typing import TYPE_CHECKING, Any, Literal
13
+ from typing import TYPE_CHECKING
12
14
  from uuid import uuid4
13
15
 
14
16
  from docutils import nodes
15
17
 
16
18
  from sphinx import addnodes, package_dir
19
+ from sphinx._cli.util.colour import bold
17
20
  from sphinx.builders import Builder
18
21
  from sphinx.errors import ThemeError
19
22
  from sphinx.locale import __
20
23
  from sphinx.util import logging
21
- from sphinx.util.console import bold
22
24
  from sphinx.util.display import status_iterator
23
- from sphinx.util.i18n import CatalogInfo, docname_to_domain
25
+ from sphinx.util.i18n import docname_to_domain
24
26
  from sphinx.util.index_entries import split_index_msg
25
27
  from sphinx.util.nodes import extract_messages, traverse_translatable_index
26
28
  from sphinx.util.osutil import canon_path, ensuredir, relpath
@@ -28,16 +30,16 @@ from sphinx.util.tags import Tags
28
30
  from sphinx.util.template import SphinxRenderer
29
31
 
30
32
  if TYPE_CHECKING:
31
- import os
32
33
  from collections.abc import Iterable, Iterator, Sequence
34
+ from typing import Any, Literal
33
35
 
34
36
  from docutils.nodes import Element
35
37
 
36
38
  from sphinx.application import Sphinx
37
- from sphinx.config import Config
39
+ from sphinx.util.i18n import CatalogInfo
38
40
  from sphinx.util.typing import ExtensionMetadata
39
41
 
40
- DEFAULT_TEMPLATE_PATH = Path(package_dir, 'templates', 'gettext')
42
+ DEFAULT_TEMPLATE_PATH = package_dir.joinpath('templates', 'gettext')
41
43
 
42
44
  logger = logging.getLogger(__name__)
43
45
 
@@ -45,6 +47,12 @@ logger = logging.getLogger(__name__)
45
47
  class Message:
46
48
  """An entry of translatable message."""
47
49
 
50
+ __slots__ = 'text', 'locations', 'uuids'
51
+
52
+ text: str
53
+ locations: list[tuple[str, int]]
54
+ uuids: list[str]
55
+
48
56
  def __init__(
49
57
  self, text: str, locations: list[tuple[str, int]], uuids: list[str]
50
58
  ) -> None:
@@ -52,48 +60,60 @@ class Message:
52
60
  self.locations = locations
53
61
  self.uuids = uuids
54
62
 
63
+ def __repr__(self) -> str:
64
+ return (
65
+ 'Message('
66
+ f'text={self.text!r}, locations={self.locations!r}, uuids={self.uuids!r}'
67
+ ')'
68
+ )
69
+
55
70
 
56
71
  class Catalog:
57
72
  """Catalog of translatable messages."""
58
73
 
59
- def __init__(self) -> None:
60
- self.messages: list[str] = [] # retain insertion order
74
+ __slots__ = ('metadata',)
61
75
 
76
+ def __init__(self) -> None:
62
77
  # msgid -> file, line, uid
63
78
  self.metadata: dict[str, list[tuple[str, int, str]]] = {}
64
79
 
65
80
  def add(self, msg: str, origin: Element | MsgOrigin) -> None:
66
81
  if not hasattr(origin, 'uid'):
67
82
  # Nodes that are replicated like todo don't have a uid,
68
- # however i18n is also unnecessary.
83
+ # however translation is also unnecessary.
69
84
  return
70
- if msg not in self.metadata: # faster lookup in hash
71
- self.messages.append(msg)
72
- self.metadata[msg] = []
73
- line = origin.line
74
- if line is None:
75
- line = -1
76
- self.metadata[msg].append((origin.source, line, origin.uid)) # type: ignore[arg-type]
85
+ msg_metadata = self.metadata.setdefault(msg, [])
86
+ line = line if (line := origin.line) is not None else -1
87
+ msg_metadata.append((origin.source or '', line, origin.uid))
77
88
 
78
89
  def __iter__(self) -> Iterator[Message]:
79
- for message in self.messages:
80
- positions = sorted({
81
- (source, line) for source, line, uuid in self.metadata[message]
82
- })
83
- uuids = [uuid for source, line, uuid in self.metadata[message]]
84
- yield Message(message, positions, uuids)
90
+ for message, msg_metadata in self.metadata.items():
91
+ positions = sorted(set(map(operator.itemgetter(0, 1), msg_metadata)))
92
+ uuids = list(map(operator.itemgetter(2), msg_metadata))
93
+ yield Message(text=message, locations=positions, uuids=uuids)
94
+
95
+ @property
96
+ def messages(self) -> list[str]:
97
+ return list(self.metadata)
85
98
 
86
99
 
87
100
  class MsgOrigin:
88
- """
89
- Origin holder for Catalog message origin.
90
- """
101
+ """Origin holder for Catalog message origin."""
102
+
103
+ __slots__ = 'source', 'line', 'uid'
104
+
105
+ source: str
106
+ line: int
107
+ uid: str
91
108
 
92
109
  def __init__(self, source: str, line: int) -> None:
93
110
  self.source = source
94
111
  self.line = line
95
112
  self.uid = uuid4().hex
96
113
 
114
+ def __repr__(self) -> str:
115
+ return f'<MsgOrigin {self.source}:{self.line}; uid={self.uid!r}>'
116
+
97
117
 
98
118
  class GettextRenderer(SphinxRenderer):
99
119
  def __init__(
@@ -136,9 +156,7 @@ class I18nTags(Tags):
136
156
 
137
157
 
138
158
  class I18nBuilder(Builder):
139
- """
140
- General i18n builder.
141
- """
159
+ """General i18n builder."""
142
160
 
143
161
  name = 'i18n'
144
162
  versioning_method = 'text'
@@ -146,10 +164,8 @@ class I18nBuilder(Builder):
146
164
 
147
165
  def init(self) -> None:
148
166
  super().init()
149
- self.env.set_versioning_method(
150
- self.versioning_method, self.env.config.gettext_uuid
151
- )
152
- self.tags = I18nTags()
167
+ self.env.set_versioning_method(self.versioning_method, self.config.gettext_uuid)
168
+ self.tags = self.app.tags = I18nTags()
153
169
  self.catalogs: defaultdict[str, Catalog] = defaultdict(Catalog)
154
170
 
155
171
  def get_target_uri(self, docname: str, typ: str | None = None) -> str:
@@ -174,7 +190,7 @@ class I18nBuilder(Builder):
174
190
  if not _is_node_in_substitution_definition(node):
175
191
  catalog.add(msg, node)
176
192
 
177
- if 'index' in self.env.config.gettext_additional_targets:
193
+ if 'index' in self.config.gettext_additional_targets:
178
194
  # Extract translatable messages from index entries.
179
195
  for node, entries in traverse_translatable_index(doctree):
180
196
  for entry_type, value, _target_id, _main, _category_key in entries:
@@ -192,20 +208,20 @@ else:
192
208
  ctime = time.strftime('%Y-%m-%d %H:%M%z', timestamp)
193
209
 
194
210
 
195
- def should_write(filepath: str, new_content: str) -> bool:
196
- if not path.exists(filepath):
211
+ def should_write(filepath: Path, new_content: str) -> bool:
212
+ if not filepath.exists():
197
213
  return True
198
214
  try:
199
- with open(filepath, encoding='utf-8') as oldpot:
215
+ with codecs.open(str(filepath), encoding='utf-8') as oldpot:
200
216
  old_content = oldpot.read()
201
- old_header_index = old_content.index('"POT-Creation-Date:')
202
- new_header_index = new_content.index('"POT-Creation-Date:')
203
- old_body_index = old_content.index('"PO-Revision-Date:')
204
- new_body_index = new_content.index('"PO-Revision-Date:')
205
- return (
206
- old_content[:old_header_index] != new_content[:new_header_index]
207
- or new_content[new_body_index:] != old_content[old_body_index:]
208
- )
217
+ old_header_index = old_content.index('"POT-Creation-Date:')
218
+ new_header_index = new_content.index('"POT-Creation-Date:')
219
+ old_body_index = old_content.index('"PO-Revision-Date:')
220
+ new_body_index = new_content.index('"PO-Revision-Date:')
221
+ return (
222
+ old_content[:old_header_index] != new_content[:new_header_index]
223
+ or new_content[new_body_index:] != old_content[old_body_index:]
224
+ )
209
225
  except ValueError:
210
226
  pass
211
227
 
@@ -222,9 +238,7 @@ def _is_node_in_substitution_definition(node: nodes.Node) -> bool:
222
238
 
223
239
 
224
240
  class MessageCatalogBuilder(I18nBuilder):
225
- """
226
- Builds gettext-style message catalogs (.pot files).
227
- """
241
+ """Builds gettext-style message catalogs (.pot files)."""
228
242
 
229
243
  name = 'gettext'
230
244
  epilog = __('The message catalogs are in %(outdir)s.')
@@ -237,11 +251,11 @@ class MessageCatalogBuilder(I18nBuilder):
237
251
  def _collect_templates(self) -> set[str]:
238
252
  template_files = set()
239
253
  for template_path in self.config.templates_path:
240
- tmpl_abs_path = path.join(self.app.srcdir, template_path)
254
+ tmpl_abs_path = self.app.srcdir / template_path
241
255
  for dirpath, _dirs, files in walk(tmpl_abs_path):
242
256
  for fn in files:
243
257
  if fn.endswith('.html'):
244
- filename = canon_path(path.join(dirpath, fn))
258
+ filename = Path(dirpath, fn).as_posix()
245
259
  template_files.add(filename)
246
260
  return template_files
247
261
 
@@ -257,10 +271,10 @@ class MessageCatalogBuilder(I18nBuilder):
257
271
  files, __('reading templates... '), 'purple', len(files), self.app.verbosity
258
272
  ):
259
273
  try:
260
- with open(template, encoding='utf-8') as f:
274
+ with codecs.open(template, encoding='utf-8') as f:
261
275
  context = f.read()
262
276
  for line, _meth, msg in extract_translations(context):
263
- origin = MsgOrigin(template, line)
277
+ origin = MsgOrigin(source=template, line=line)
264
278
  self.catalogs['sphinx'].add(msg, origin)
265
279
  except Exception as exc:
266
280
  msg = f'{template}: {exc!r}'
@@ -287,6 +301,7 @@ class MessageCatalogBuilder(I18nBuilder):
287
301
  'display_location': self.config.gettext_location,
288
302
  'display_uuid': self.config.gettext_uuid,
289
303
  }
304
+ catalog: Catalog
290
305
  for textdomain, catalog in status_iterator(
291
306
  self.catalogs.items(),
292
307
  __('writing message catalogs... '),
@@ -296,7 +311,7 @@ class MessageCatalogBuilder(I18nBuilder):
296
311
  operator.itemgetter(0),
297
312
  ):
298
313
  # noop if config.gettext_compact is set
299
- ensuredir(path.join(self.outdir, path.dirname(textdomain)))
314
+ ensuredir(self.outdir / os.path.dirname(textdomain))
300
315
 
301
316
  context['messages'] = list(catalog)
302
317
  template_path = [
@@ -305,34 +320,39 @@ class MessageCatalogBuilder(I18nBuilder):
305
320
  renderer = GettextRenderer(template_path, outdir=self.outdir)
306
321
  content = renderer.render('message.pot.jinja', context)
307
322
 
308
- pofn = path.join(self.outdir, textdomain + '.pot')
323
+ pofn = self.outdir / f'{textdomain}.pot'
309
324
  if should_write(pofn, content):
310
- with open(pofn, 'w', encoding='utf-8') as pofile:
325
+ with codecs.open(str(pofn), 'w', encoding='utf-8') as pofile:
311
326
  pofile.write(content)
312
327
 
313
328
 
314
- def _gettext_compact_validator(app: Sphinx, config: Config) -> None:
315
- gettext_compact = config.gettext_compact
316
- # Convert 0/1 from the command line to ``bool`` types
317
- if gettext_compact == '0':
318
- config.gettext_compact = False
319
- elif gettext_compact == '1':
320
- config.gettext_compact = True
321
-
322
-
323
329
  def setup(app: Sphinx) -> ExtensionMetadata:
324
330
  app.add_builder(MessageCatalogBuilder)
325
331
 
326
- app.add_config_value('gettext_compact', True, 'gettext', {bool, str})
327
- app.add_config_value('gettext_location', True, 'gettext')
328
- app.add_config_value('gettext_uuid', False, 'gettext')
329
- app.add_config_value('gettext_auto_build', True, 'env')
330
- app.add_config_value('gettext_additional_targets', [], 'env', types={set, list})
331
332
  app.add_config_value(
332
- 'gettext_last_translator', 'FULL NAME <EMAIL@ADDRESS>', 'gettext'
333
+ 'gettext_compact', True, 'gettext', types=frozenset({bool, str})
334
+ )
335
+ app.add_config_value('gettext_location', True, 'gettext', types=frozenset({bool}))
336
+ app.add_config_value('gettext_uuid', False, 'gettext', types=frozenset({bool}))
337
+ app.add_config_value('gettext_auto_build', True, 'env', types=frozenset({bool}))
338
+ app.add_config_value(
339
+ 'gettext_additional_targets',
340
+ [],
341
+ 'env',
342
+ types=frozenset({frozenset, list, set, tuple}),
343
+ )
344
+ app.add_config_value(
345
+ 'gettext_last_translator',
346
+ 'FULL NAME <EMAIL@ADDRESS>',
347
+ 'gettext',
348
+ types=frozenset({str}),
349
+ )
350
+ app.add_config_value(
351
+ 'gettext_language_team',
352
+ 'LANGUAGE <LL@li.org>',
353
+ 'gettext',
354
+ types=frozenset({str}),
333
355
  )
334
- app.add_config_value('gettext_language_team', 'LANGUAGE <LL@li.org>', 'gettext')
335
- app.connect('config-inited', _gettext_compact_validator, priority=800)
336
356
 
337
357
  return {
338
358
  'version': 'builtin',