Sphinx 7.1.1__py3-none-any.whl → 7.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 (313) hide show
  1. sphinx/__init__.py +6 -6
  2. sphinx/__main__.py +3 -1
  3. sphinx/addnodes.py +35 -22
  4. sphinx/application.py +40 -38
  5. sphinx/builders/__init__.py +16 -12
  6. sphinx/builders/_epub_base.py +15 -11
  7. sphinx/builders/changes.py +6 -4
  8. sphinx/builders/dirhtml.py +4 -2
  9. sphinx/builders/dummy.py +6 -4
  10. sphinx/builders/epub3.py +16 -8
  11. sphinx/builders/gettext.py +40 -43
  12. sphinx/builders/html/__init__.py +166 -196
  13. sphinx/builders/html/_assets.py +116 -0
  14. sphinx/builders/html/transforms.py +4 -2
  15. sphinx/builders/latex/__init__.py +12 -7
  16. sphinx/builders/latex/theming.py +5 -2
  17. sphinx/builders/latex/transforms.py +6 -3
  18. sphinx/builders/linkcheck.py +21 -13
  19. sphinx/builders/manpage.py +6 -4
  20. sphinx/builders/singlehtml.py +16 -9
  21. sphinx/builders/texinfo.py +11 -6
  22. sphinx/builders/text.py +8 -3
  23. sphinx/builders/xml.py +9 -4
  24. sphinx/cmd/build.py +27 -14
  25. sphinx/cmd/make_mode.py +13 -4
  26. sphinx/cmd/quickstart.py +13 -4
  27. sphinx/config.py +17 -14
  28. sphinx/deprecation.py +4 -2
  29. sphinx/directives/__init__.py +44 -12
  30. sphinx/directives/code.py +5 -4
  31. sphinx/directives/other.py +92 -44
  32. sphinx/directives/patches.py +1 -1
  33. sphinx/domains/__init__.py +11 -8
  34. sphinx/domains/c.py +67 -57
  35. sphinx/domains/changeset.py +3 -2
  36. sphinx/domains/citation.py +2 -1
  37. sphinx/domains/cpp.py +136 -93
  38. sphinx/domains/index.py +9 -5
  39. sphinx/domains/javascript.py +32 -19
  40. sphinx/domains/math.py +5 -3
  41. sphinx/domains/python.py +69 -57
  42. sphinx/domains/rst.py +20 -11
  43. sphinx/domains/std.py +21 -15
  44. sphinx/environment/__init__.py +97 -65
  45. sphinx/environment/adapters/indexentries.py +13 -10
  46. sphinx/environment/adapters/toctree.py +485 -308
  47. sphinx/environment/collectors/__init__.py +3 -4
  48. sphinx/environment/collectors/asset.py +10 -4
  49. sphinx/environment/collectors/dependencies.py +7 -4
  50. sphinx/environment/collectors/metadata.py +7 -5
  51. sphinx/environment/collectors/title.py +5 -3
  52. sphinx/environment/collectors/toctree.py +13 -8
  53. sphinx/errors.py +1 -1
  54. sphinx/events.py +5 -5
  55. sphinx/ext/apidoc.py +49 -27
  56. sphinx/ext/autodoc/__init__.py +179 -161
  57. sphinx/ext/autodoc/directive.py +10 -6
  58. sphinx/ext/autodoc/importer.py +22 -13
  59. sphinx/ext/autodoc/mock.py +4 -1
  60. sphinx/ext/autodoc/preserve_defaults.py +80 -12
  61. sphinx/ext/autodoc/type_comment.py +14 -10
  62. sphinx/ext/autodoc/typehints.py +7 -3
  63. sphinx/ext/autosectionlabel.py +6 -3
  64. sphinx/ext/autosummary/__init__.py +21 -15
  65. sphinx/ext/autosummary/generate.py +176 -126
  66. sphinx/ext/coverage.py +93 -8
  67. sphinx/ext/doctest.py +28 -17
  68. sphinx/ext/duration.py +19 -17
  69. sphinx/ext/extlinks.py +11 -6
  70. sphinx/ext/githubpages.py +8 -7
  71. sphinx/ext/graphviz.py +61 -17
  72. sphinx/ext/ifconfig.py +7 -4
  73. sphinx/ext/imgconverter.py +4 -2
  74. sphinx/ext/imgmath.py +29 -23
  75. sphinx/ext/inheritance_diagram.py +41 -27
  76. sphinx/ext/intersphinx.py +45 -38
  77. sphinx/ext/linkcode.py +8 -5
  78. sphinx/ext/mathjax.py +13 -9
  79. sphinx/ext/napoleon/__init__.py +3 -3
  80. sphinx/ext/napoleon/docstring.py +40 -31
  81. sphinx/ext/todo.py +10 -7
  82. sphinx/ext/viewcode.py +46 -25
  83. sphinx/extension.py +1 -1
  84. sphinx/highlighting.py +20 -12
  85. sphinx/io.py +5 -4
  86. sphinx/jinja2glue.py +24 -19
  87. sphinx/locale/__init__.py +8 -2
  88. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  89. sphinx/locale/ar/LC_MESSAGES/sphinx.po +756 -740
  90. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  91. sphinx/locale/bg/LC_MESSAGES/sphinx.po +754 -738
  92. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  93. sphinx/locale/bn/LC_MESSAGES/sphinx.po +755 -739
  94. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  95. sphinx/locale/ca/LC_MESSAGES/sphinx.po +768 -752
  96. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  97. sphinx/locale/cak/LC_MESSAGES/sphinx.po +754 -738
  98. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/cs/LC_MESSAGES/sphinx.po +758 -742
  100. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/cy/LC_MESSAGES/sphinx.po +759 -743
  102. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/da/LC_MESSAGES/sphinx.po +760 -744
  104. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/de/LC_MESSAGES/sphinx.po +759 -743
  106. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +754 -738
  108. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/el/LC_MESSAGES/sphinx.po +763 -747
  110. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +754 -738
  112. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +754 -738
  114. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +768 -752
  116. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +754 -738
  118. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/eo/LC_MESSAGES/sphinx.po +754 -738
  120. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/es/LC_MESSAGES/sphinx.po +767 -751
  122. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +754 -738
  124. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/et/LC_MESSAGES/sphinx.po +762 -746
  126. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/eu/LC_MESSAGES/sphinx.po +755 -739
  128. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/fa/LC_MESSAGES/sphinx.po +766 -750
  130. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/fi/LC_MESSAGES/sphinx.po +754 -738
  132. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/fr/LC_MESSAGES/sphinx.po +768 -752
  134. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +754 -738
  136. sphinx/locale/gl/LC_MESSAGES/sphinx.js +60 -0
  137. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  138. sphinx/locale/gl/LC_MESSAGES/sphinx.po +3695 -0
  139. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  140. sphinx/locale/he/LC_MESSAGES/sphinx.po +755 -739
  141. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  142. sphinx/locale/hi/LC_MESSAGES/sphinx.po +763 -747
  143. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +754 -738
  145. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/hr/LC_MESSAGES/sphinx.po +760 -744
  147. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  148. sphinx/locale/hu/LC_MESSAGES/sphinx.po +759 -743
  149. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  150. sphinx/locale/id/LC_MESSAGES/sphinx.po +765 -749
  151. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/is/LC_MESSAGES/sphinx.po +760 -744
  153. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/it/LC_MESSAGES/sphinx.po +760 -744
  155. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/ja/LC_MESSAGES/sphinx.po +767 -751
  157. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/ka/LC_MESSAGES/sphinx.po +759 -743
  159. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/ko/LC_MESSAGES/sphinx.po +767 -751
  161. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/lt/LC_MESSAGES/sphinx.po +755 -739
  163. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/lv/LC_MESSAGES/sphinx.po +755 -739
  165. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/mk/LC_MESSAGES/sphinx.po +754 -738
  167. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +755 -739
  169. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ne/LC_MESSAGES/sphinx.po +755 -739
  171. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/nl/LC_MESSAGES/sphinx.po +760 -744
  173. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/pl/LC_MESSAGES/sphinx.po +762 -745
  175. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/pt/LC_MESSAGES/sphinx.po +754 -738
  177. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +768 -752
  179. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  180. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +755 -739
  181. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  182. sphinx/locale/ro/LC_MESSAGES/sphinx.po +759 -743
  183. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  184. sphinx/locale/ru/LC_MESSAGES/sphinx.po +760 -744
  185. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  186. sphinx/locale/si/LC_MESSAGES/sphinx.po +754 -738
  187. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  188. sphinx/locale/sk/LC_MESSAGES/sphinx.po +765 -749
  189. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  190. sphinx/locale/sl/LC_MESSAGES/sphinx.po +755 -739
  191. sphinx/locale/sphinx.pot +748 -740
  192. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/sq/LC_MESSAGES/sphinx.po +768 -752
  194. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/sr/LC_MESSAGES/sphinx.po +754 -738
  196. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.po +754 -738
  198. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.po +754 -738
  200. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sv/LC_MESSAGES/sphinx.po +755 -739
  202. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/ta/LC_MESSAGES/sphinx.po +754 -738
  204. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  205. sphinx/locale/te/LC_MESSAGES/sphinx.po +754 -738
  206. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/tr/LC_MESSAGES/sphinx.po +763 -747
  208. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +760 -749
  210. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/ur/LC_MESSAGES/sphinx.po +759 -748
  212. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/vi/LC_MESSAGES/sphinx.po +754 -738
  214. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  215. sphinx/locale/yue/LC_MESSAGES/sphinx.po +754 -738
  216. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  217. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +768 -752
  218. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  219. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +754 -738
  220. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  221. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +767 -751
  222. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  223. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +754 -738
  224. sphinx/parsers.py +5 -4
  225. sphinx/project.py +52 -34
  226. sphinx/pycode/__init__.py +2 -1
  227. sphinx/pycode/ast.py +7 -13
  228. sphinx/pycode/parser.py +42 -38
  229. sphinx/registry.py +35 -29
  230. sphinx/roles.py +9 -4
  231. sphinx/search/__init__.py +5 -17
  232. sphinx/search/da.py +1 -1
  233. sphinx/search/de.py +1 -1
  234. sphinx/search/en.py +1 -1
  235. sphinx/search/es.py +1 -1
  236. sphinx/search/fi.py +1 -1
  237. sphinx/search/fr.py +1 -1
  238. sphinx/search/hu.py +1 -1
  239. sphinx/search/it.py +1 -1
  240. sphinx/search/ja.py +1 -1
  241. sphinx/search/nl.py +1 -1
  242. sphinx/search/no.py +1 -1
  243. sphinx/search/pt.py +1 -1
  244. sphinx/search/ro.py +1 -1
  245. sphinx/search/ru.py +1 -1
  246. sphinx/search/sv.py +1 -1
  247. sphinx/search/tr.py +1 -1
  248. sphinx/search/zh.py +1 -1
  249. sphinx/testing/fixtures.py +23 -30
  250. sphinx/testing/path.py +9 -0
  251. sphinx/testing/restructuredtext.py +13 -5
  252. sphinx/testing/util.py +20 -63
  253. sphinx/texinputs/sphinxlatexobjects.sty +15 -15
  254. sphinx/themes/agogo/static/agogo.css_t +10 -4
  255. sphinx/themes/basic/layout.html +1 -1
  256. sphinx/themes/basic/static/basic.css_t +4 -0
  257. sphinx/themes/basic/static/documentation_options.js_t +1 -2
  258. sphinx/themes/basic/static/searchtools.js +17 -9
  259. sphinx/themes/basic/static/sphinx_highlight.js +13 -3
  260. sphinx/themes/bizstyle/static/bizstyle.css_t +4 -0
  261. sphinx/themes/classic/theme.conf +1 -1
  262. sphinx/themes/epub/static/epub.css_t +6 -1
  263. sphinx/themes/haiku/theme.conf +1 -1
  264. sphinx/themes/nature/static/nature.css_t +4 -0
  265. sphinx/themes/nonav/static/nonav.css_t +6 -1
  266. sphinx/themes/pyramid/static/pyramid.css_t +4 -0
  267. sphinx/themes/scrolls/static/scrolls.css_t +4 -0
  268. sphinx/themes/scrolls/theme.conf +1 -1
  269. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +4 -0
  270. sphinx/theming.py +9 -7
  271. sphinx/transforms/__init__.py +79 -3
  272. sphinx/transforms/compact_bullet_list.py +6 -3
  273. sphinx/transforms/i18n.py +26 -10
  274. sphinx/transforms/post_transforms/__init__.py +21 -8
  275. sphinx/transforms/post_transforms/code.py +6 -3
  276. sphinx/transforms/post_transforms/images.py +13 -9
  277. sphinx/util/__init__.py +21 -92
  278. sphinx/util/cfamily.py +7 -4
  279. sphinx/util/display.py +3 -2
  280. sphinx/util/docfields.py +7 -6
  281. sphinx/util/docstrings.py +1 -1
  282. sphinx/util/docutils.py +41 -31
  283. sphinx/util/fileutil.py +9 -6
  284. sphinx/util/i18n.py +21 -18
  285. sphinx/util/images.py +2 -1
  286. sphinx/util/index_entries.py +27 -0
  287. sphinx/util/inspect.py +83 -67
  288. sphinx/util/inventory.py +4 -2
  289. sphinx/util/logging.py +9 -6
  290. sphinx/util/matching.py +5 -2
  291. sphinx/util/math.py +6 -3
  292. sphinx/util/nodes.py +70 -31
  293. sphinx/util/osutil.py +22 -40
  294. sphinx/util/parallel.py +4 -1
  295. sphinx/util/rst.py +7 -3
  296. sphinx/util/tags.py +11 -4
  297. sphinx/util/template.py +17 -14
  298. sphinx/util/typing.py +61 -20
  299. sphinx/versioning.py +6 -4
  300. sphinx/writers/html.py +1 -1
  301. sphinx/writers/html5.py +32 -24
  302. sphinx/writers/latex.py +67 -53
  303. sphinx/writers/manpage.py +9 -5
  304. sphinx/writers/texinfo.py +11 -9
  305. sphinx/writers/text.py +14 -9
  306. sphinx/writers/xml.py +3 -2
  307. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/METADATA +7 -5
  308. sphinx-7.2.0.dist-info/RECORD +568 -0
  309. sphinx/testing/comparer.py +0 -97
  310. sphinx-7.1.1.dist-info/RECORD +0 -564
  311. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/LICENSE +0 -0
  312. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/WHEEL +0 -0
  313. {sphinx-7.1.1.dist-info → sphinx-7.2.0.dist-info}/entry_points.txt +0 -0
sphinx/builders/epub3.py CHANGED
@@ -6,28 +6,30 @@ Originally derived from epub.py.
6
6
  from __future__ import annotations
7
7
 
8
8
  import html
9
+ import os
9
10
  import re
11
+ import time
10
12
  from os import path
11
- from typing import Any, NamedTuple
13
+ from typing import TYPE_CHECKING, Any, NamedTuple
12
14
 
13
15
  from sphinx import package_dir
14
- from sphinx.application import Sphinx
15
16
  from sphinx.builders import _epub_base
16
17
  from sphinx.config import ENUM, Config
17
18
  from sphinx.locale import __
18
19
  from sphinx.util import logging
19
20
  from sphinx.util.fileutil import copy_asset_file
20
- from sphinx.util.i18n import format_date
21
21
  from sphinx.util.osutil import make_filename
22
22
 
23
+ if TYPE_CHECKING:
24
+ from sphinx.application import Sphinx
25
+
23
26
  logger = logging.getLogger(__name__)
24
27
 
25
28
 
26
29
  class NavPoint(NamedTuple):
27
30
  text: str
28
31
  refuri: str
29
- children: list[Any] # mypy does not support recursive types
30
- # https://github.com/python/mypy/issues/7069
32
+ children: list[NavPoint]
31
33
 
32
34
 
33
35
  # writing modes
@@ -99,12 +101,17 @@ class Epub3Builder(_epub_base.EpubBuilder):
99
101
  """
100
102
  writing_mode = self.config.epub_writing_mode
101
103
 
104
+ if (source_date_epoch := os.getenv('SOURCE_DATE_EPOCH')) is not None:
105
+ time_tuple = time.gmtime(int(source_date_epoch))
106
+ else:
107
+ time_tuple = time.gmtime()
108
+
102
109
  metadata = super().content_metadata()
103
110
  metadata['description'] = html.escape(self.config.epub_description)
104
111
  metadata['contributor'] = html.escape(self.config.epub_contributor)
105
112
  metadata['page_progression_direction'] = PAGE_PROGRESSION_DIRECTIONS.get(writing_mode)
106
113
  metadata['ibook_scroll_axis'] = IBOOK_SCROLL_AXIS.get(writing_mode)
107
- metadata['date'] = html.escape(format_date("%Y-%m-%dT%H:%M:%SZ", language='en'))
114
+ metadata['date'] = html.escape(time.strftime("%Y-%m-%dT%H:%M:%SZ", time_tuple))
108
115
  metadata['version'] = html.escape(self.config.version)
109
116
  metadata['epub_version'] = self.config.epub_version
110
117
  return metadata
@@ -156,7 +163,8 @@ class Epub3Builder(_epub_base.EpubBuilder):
156
163
  navstack[-1].children.append(navpoint)
157
164
  navstack.append(navpoint)
158
165
  else:
159
- raise RuntimeError('Should never reach here. It might be a bug.')
166
+ unreachable = 'Should never reach here. It might be a bug.'
167
+ raise RuntimeError(unreachable)
160
168
 
161
169
  return navstack[0].children
162
170
 
@@ -245,7 +253,7 @@ def convert_epub_css_files(app: Sphinx, config: Config) -> None:
245
253
  logger.warning(__('invalid css_file: %r, ignored'), entry)
246
254
  continue
247
255
 
248
- config.epub_css_files = epub_css_files # type: ignore
256
+ config.epub_css_files = epub_css_files # type: ignore[attr-defined]
249
257
 
250
258
 
251
259
  def setup(app: Sphinx) -> dict[str, Any]:
@@ -2,31 +2,37 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import time
5
6
  from codecs import open
6
7
  from collections import defaultdict
7
- from datetime import datetime, timedelta, timezone, tzinfo
8
8
  from os import getenv, path, walk
9
- from time import time
10
- from typing import Any, Generator, Iterable
9
+ from typing import TYPE_CHECKING, Any
11
10
  from uuid import uuid4
12
11
 
13
12
  from docutils import nodes
14
- from docutils.nodes import Element
15
13
 
16
14
  from sphinx import addnodes, package_dir
17
- from sphinx.application import Sphinx
18
15
  from sphinx.builders import Builder
19
16
  from sphinx.errors import ThemeError
20
17
  from sphinx.locale import __
21
- from sphinx.util import logging, split_index_msg
22
- from sphinx.util.console import bold # type: ignore
18
+ from sphinx.util import logging
19
+ from sphinx.util.console import bold # type: ignore[attr-defined]
23
20
  from sphinx.util.display import status_iterator
24
21
  from sphinx.util.i18n import CatalogInfo, docname_to_domain
22
+ from sphinx.util.index_entries import split_index_msg
25
23
  from sphinx.util.nodes import extract_messages, traverse_translatable_index
26
24
  from sphinx.util.osutil import canon_path, ensuredir, relpath
27
25
  from sphinx.util.tags import Tags
28
26
  from sphinx.util.template import SphinxRenderer
29
27
 
28
+ if TYPE_CHECKING:
29
+ import os
30
+ from collections.abc import Generator, Iterable
31
+
32
+ from docutils.nodes import Element
33
+
34
+ from sphinx.application import Sphinx
35
+
30
36
  logger = logging.getLogger(__name__)
31
37
 
32
38
 
@@ -81,11 +87,12 @@ class MsgOrigin:
81
87
 
82
88
  class GettextRenderer(SphinxRenderer):
83
89
  def __init__(
84
- self, template_path: str | None = None, outdir: str | None = None,
90
+ self, template_path: list[str | os.PathLike[str]] | None = None,
91
+ outdir: str | os.PathLike[str] | None = None,
85
92
  ) -> None:
86
93
  self.outdir = outdir
87
94
  if template_path is None:
88
- template_path = path.join(package_dir, 'templates', 'gettext')
95
+ template_path = [path.join(package_dir, 'templates', 'gettext')]
89
96
  super().__init__(template_path)
90
97
 
91
98
  def escape(s: str) -> str:
@@ -147,11 +154,13 @@ class I18nBuilder(Builder):
147
154
 
148
155
  for toctree in self.env.tocs[docname].findall(addnodes.toctree):
149
156
  for node, msg in extract_messages(toctree):
150
- node.uid = '' # type: ignore # Hack UUID model
157
+ node.uid = '' # type: ignore[attr-defined] # Hack UUID model
151
158
  catalog.add(msg, node)
152
159
 
153
160
  for node, msg in extract_messages(doctree):
154
- catalog.add(msg, node)
161
+ # Do not extract messages from within substitution definitions.
162
+ if not _is_node_in_substitution_definition(node):
163
+ catalog.add(msg, node)
155
164
 
156
165
  if 'index' in self.env.config.gettext_additional_targets:
157
166
  # Extract translatable messages from index entries.
@@ -161,36 +170,14 @@ class I18nBuilder(Builder):
161
170
  catalog.add(m, node)
162
171
 
163
172
 
164
- # determine tzoffset once to remain unaffected by DST change during build
165
- timestamp = time()
166
- local_time = datetime.fromtimestamp(timestamp)
167
- utc_time = datetime.fromtimestamp(timestamp, tz=timezone.utc)
168
- tzdelta = local_time - utc_time.replace(tzinfo=None)
169
-
170
- # set timestamp from SOURCE_DATE_EPOCH if set
171
- # see https://reproducible-builds.org/specs/source-date-epoch/
172
- source_date_epoch = getenv('SOURCE_DATE_EPOCH')
173
- if source_date_epoch is not None:
174
- timestamp = float(source_date_epoch)
175
- tzdelta = timedelta(0)
176
-
177
-
178
- class LocalTimeZone(tzinfo):
179
- def __init__(self, *args: Any, **kwargs: Any) -> None:
180
- super().__init__(*args, **kwargs)
181
- self.tzdelta = tzdelta
182
-
183
- def tzname(self, dt: datetime | None) -> str: # purely to satisfy mypy
184
- return "local"
185
-
186
- def utcoffset(self, dt: datetime | None) -> timedelta:
187
- return self.tzdelta
188
-
189
- def dst(self, dt: datetime | None) -> timedelta:
190
- return timedelta(0)
191
-
192
-
193
- ltz = LocalTimeZone()
173
+ # If set, use the timestamp from SOURCE_DATE_EPOCH
174
+ # https://reproducible-builds.org/specs/source-date-epoch/
175
+ if (source_date_epoch := getenv('SOURCE_DATE_EPOCH')) is not None:
176
+ timestamp = time.gmtime(float(source_date_epoch))
177
+ else:
178
+ # determine timestamp once to remain unaffected by DST changes during build
179
+ timestamp = time.localtime()
180
+ ctime = time.strftime('%Y-%m-%d %H:%M%z', timestamp)
194
181
 
195
182
 
196
183
  def should_write(filepath: str, new_content: str) -> bool:
@@ -211,6 +198,15 @@ def should_write(filepath: str, new_content: str) -> bool:
211
198
  return True
212
199
 
213
200
 
201
+ def _is_node_in_substitution_definition(node: nodes.Node) -> bool:
202
+ """Check "node" to test if it is in a substitution definition."""
203
+ while node.parent:
204
+ if isinstance(node, nodes.substitution_definition):
205
+ return True
206
+ node = node.parent
207
+ return False
208
+
209
+
214
210
  class MessageCatalogBuilder(I18nBuilder):
215
211
  """
216
212
  Builds gettext-style message catalogs (.pot files).
@@ -251,7 +247,8 @@ class MessageCatalogBuilder(I18nBuilder):
251
247
  origin = MsgOrigin(template, line)
252
248
  self.catalogs['sphinx'].add(msg, origin)
253
249
  except Exception as exc:
254
- raise ThemeError(f'{template}: {exc!r}') from exc
250
+ msg = f'{template}: {exc!r}'
251
+ raise ThemeError(msg) from exc
255
252
 
256
253
  def build(
257
254
  self,
@@ -270,7 +267,7 @@ class MessageCatalogBuilder(I18nBuilder):
270
267
  'project': self.config.project,
271
268
  'last_translator': self.config.gettext_last_translator,
272
269
  'language_team': self.config.gettext_language_team,
273
- 'ctime': datetime.fromtimestamp(timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'),
270
+ 'ctime': ctime,
274
271
  'display_location': self.config.gettext_location,
275
272
  'display_uuid': self.config.gettext_uuid,
276
273
  }