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
sphinx/util/typing.py CHANGED
@@ -7,18 +7,7 @@ import sys
7
7
  import types
8
8
  import typing
9
9
  from collections.abc import Callable, Sequence
10
- from contextvars import Context, ContextVar, Token
11
- from struct import Struct
12
- from typing import (
13
- TYPE_CHECKING,
14
- Annotated,
15
- Any,
16
- ForwardRef,
17
- NewType,
18
- TypedDict,
19
- TypeVar,
20
- Union,
21
- )
10
+ from typing import TYPE_CHECKING
22
11
 
23
12
  from docutils import nodes
24
13
  from docutils.parsers.rst.states import Inliner
@@ -27,11 +16,12 @@ from sphinx.util import logging
27
16
 
28
17
  if TYPE_CHECKING:
29
18
  from collections.abc import Mapping
30
- from typing import Final, Literal, Protocol, TypeAlias
19
+ from typing import Annotated, Any, Final, Literal, Protocol, TypeAlias
31
20
 
32
21
  from typing_extensions import TypeIs
33
22
 
34
23
  from sphinx.application import Sphinx
24
+ from sphinx.util.inventory import _InventoryItem
35
25
 
36
26
  _RestifyMode: TypeAlias = Literal[
37
27
  'fully-qualified-except-typing',
@@ -47,41 +37,82 @@ logger = logging.getLogger(__name__)
47
37
 
48
38
 
49
39
  # classes that have an incorrect .__module__ attribute
50
- _INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = {
51
- Context: 'contextvars.Context', # Context.__module__ == '_contextvars'
52
- ContextVar: 'contextvars.ContextVar', # ContextVar.__module__ == '_contextvars'
53
- Token: 'contextvars.Token', # Token.__module__ == '_contextvars'
54
- Struct: 'struct.Struct', # Struct.__module__ == '_struct'
55
- # types in 'types' with <type>.__module__ == 'builtins':
56
- types.AsyncGeneratorType: 'types.AsyncGeneratorType',
57
- types.BuiltinFunctionType: 'types.BuiltinFunctionType',
58
- types.BuiltinMethodType: 'types.BuiltinMethodType',
59
- types.CellType: 'types.CellType',
60
- types.ClassMethodDescriptorType: 'types.ClassMethodDescriptorType',
61
- types.CodeType: 'types.CodeType',
62
- types.CoroutineType: 'types.CoroutineType',
63
- types.FrameType: 'types.FrameType',
64
- types.FunctionType: 'types.FunctionType',
65
- types.GeneratorType: 'types.GeneratorType',
66
- types.GetSetDescriptorType: 'types.GetSetDescriptorType',
67
- types.LambdaType: 'types.LambdaType',
68
- types.MappingProxyType: 'types.MappingProxyType',
69
- types.MemberDescriptorType: 'types.MemberDescriptorType',
70
- types.MethodDescriptorType: 'types.MethodDescriptorType',
71
- types.MethodType: 'types.MethodType',
72
- types.MethodWrapperType: 'types.MethodWrapperType',
73
- types.ModuleType: 'types.ModuleType',
74
- types.TracebackType: 'types.TracebackType',
75
- types.WrapperDescriptorType: 'types.WrapperDescriptorType',
40
+ # Map of (__module__, __qualname__) to the correct fully-qualified name
41
+ _INVALID_BUILTIN_CLASSES: Final[Mapping[tuple[str, str], str]] = {
42
+ # types from 'contextvars'
43
+ ('_contextvars', 'Context'): 'contextvars.Context',
44
+ ('_contextvars', 'ContextVar'): 'contextvars.ContextVar',
45
+ ('_contextvars', 'Token'): 'contextvars.Token',
46
+ # types from 'ctypes':
47
+ ('_ctypes', 'Array'): 'ctypes.Array',
48
+ ('_ctypes', 'Structure'): 'ctypes.Structure',
49
+ ('_ctypes', 'Union'): 'ctypes.Union',
50
+ # types from 'io':
51
+ ('_io', 'BufferedRandom'): 'io.BufferedRandom',
52
+ ('_io', 'BufferedReader'): 'io.BufferedReader',
53
+ ('_io', 'BufferedRWPair'): 'io.BufferedRWPair',
54
+ ('_io', 'BufferedWriter'): 'io.BufferedWriter',
55
+ ('_io', 'BytesIO'): 'io.BytesIO',
56
+ ('_io', 'FileIO'): 'io.FileIO',
57
+ ('_io', 'StringIO'): 'io.StringIO',
58
+ ('_io', 'TextIOWrapper'): 'io.TextIOWrapper',
59
+ # types from 'json':
60
+ ('json.decoder', 'JSONDecoder'): 'json.JSONDecoder',
61
+ ('json.encoder', 'JSONEncoder'): 'json.JSONEncoder',
62
+ # types from 'lzma':
63
+ ('_lzma', 'LZMACompressor'): 'lzma.LZMACompressor',
64
+ ('_lzma', 'LZMADecompressor'): 'lzma.LZMADecompressor',
65
+ # types from 'multiprocessing':
66
+ ('multiprocessing.context', 'Process'): 'multiprocessing.Process',
67
+ # types from 'pathlib':
68
+ ('pathlib._local', 'Path'): 'pathlib.Path',
69
+ ('pathlib._local', 'PosixPath'): 'pathlib.PosixPath',
70
+ ('pathlib._local', 'PurePath'): 'pathlib.PurePath',
71
+ ('pathlib._local', 'PurePosixPath'): 'pathlib.PurePosixPath',
72
+ ('pathlib._local', 'PureWindowsPath'): 'pathlib.PureWindowsPath',
73
+ ('pathlib._local', 'WindowsPath'): 'pathlib.WindowsPath',
74
+ # types from 'pickle':
75
+ ('_pickle', 'Pickler'): 'pickle.Pickler',
76
+ ('_pickle', 'Unpickler'): 'pickle.Unpickler',
77
+ # types from 'struct':
78
+ ('_struct', 'Struct'): 'struct.Struct',
79
+ # types from 'types':
80
+ ('builtins', 'async_generator'): 'types.AsyncGeneratorType',
81
+ ('builtins', 'builtin_function_or_method'): 'types.BuiltinMethodType',
82
+ ('builtins', 'cell'): 'types.CellType',
83
+ ('builtins', 'classmethod_descriptor'): 'types.ClassMethodDescriptorType',
84
+ ('builtins', 'code'): 'types.CodeType',
85
+ ('builtins', 'coroutine'): 'types.CoroutineType',
86
+ ('builtins', 'ellipsis'): 'types.EllipsisType',
87
+ ('builtins', 'frame'): 'types.FrameType',
88
+ ('builtins', 'function'): 'types.LambdaType',
89
+ ('builtins', 'generator'): 'types.GeneratorType',
90
+ ('builtins', 'getset_descriptor'): 'types.GetSetDescriptorType',
91
+ ('builtins', 'mappingproxy'): 'types.MappingProxyType',
92
+ ('builtins', 'member_descriptor'): 'types.MemberDescriptorType',
93
+ ('builtins', 'method'): 'types.MethodType',
94
+ ('builtins', 'method-wrapper'): 'types.MethodWrapperType',
95
+ ('builtins', 'method_descriptor'): 'types.MethodDescriptorType',
96
+ ('builtins', 'module'): 'types.ModuleType',
97
+ ('builtins', 'NoneType'): 'types.NoneType',
98
+ ('builtins', 'NotImplementedType'): 'types.NotImplementedType',
99
+ ('builtins', 'traceback'): 'types.TracebackType',
100
+ ('builtins', 'wrapper_descriptor'): 'types.WrapperDescriptorType',
101
+ # types from 'weakref':
102
+ ('_weakrefset', 'WeakSet'): 'weakref.WeakSet',
103
+ # types from 'zipfile':
104
+ ('zipfile._path', 'CompleteDirs'): 'zipfile.CompleteDirs',
105
+ ('zipfile._path', 'Path'): 'zipfile.Path',
76
106
  }
77
107
 
78
108
 
79
- def is_invalid_builtin_class(obj: Any) -> bool:
109
+ def is_invalid_builtin_class(obj: Any) -> str:
80
110
  """Check *obj* is an invalid built-in class."""
81
111
  try:
82
- return obj in _INVALID_BUILTIN_CLASSES
83
- except TypeError: # unhashable type
84
- return False
112
+ key = obj.__module__, obj.__qualname__
113
+ except AttributeError: # non-standard type
114
+ return ''
115
+ return _INVALID_BUILTIN_CLASSES.get(key, '')
85
116
 
86
117
 
87
118
  # Text like nodes which are initialized with text and rawsource
@@ -94,7 +125,7 @@ PathMatcher: TypeAlias = Callable[[str], bool]
94
125
  if TYPE_CHECKING:
95
126
 
96
127
  class RoleFunction(Protocol):
97
- def __call__( # NoQA: E704
128
+ def __call__(
98
129
  self,
99
130
  name: str,
100
131
  rawtext: str,
@@ -108,48 +139,21 @@ if TYPE_CHECKING:
108
139
 
109
140
  else:
110
141
  RoleFunction: TypeAlias = Callable[
111
- [str, str, str, int, Inliner, dict[str, Any], Sequence[str]],
142
+ [str, str, str, int, Inliner, dict[str, typing.Any], Sequence[str]],
112
143
  tuple[list[nodes.Node], list[nodes.system_message]],
113
144
  ]
114
145
 
115
146
  # A option spec for directive
116
- OptionSpec: TypeAlias = dict[str, Callable[[str], Any]]
147
+ OptionSpec: TypeAlias = dict[str, Callable[[str], typing.Any]]
117
148
 
118
149
  # title getter functions for enumerable nodes (see sphinx.domains.std)
119
150
  TitleGetter: TypeAlias = Callable[[nodes.Node], str]
120
151
 
121
- # Readable file stream for inventory loading
122
- if TYPE_CHECKING:
123
- from types import TracebackType
124
-
125
- from typing_extensions import Self
126
-
127
- _T_co = TypeVar('_T_co', str, bytes, covariant=True)
128
-
129
- class _ReadableStream(Protocol[_T_co]):
130
- def read(self, size: int = ...) -> _T_co: ... # NoQA: E704
131
-
132
- def __enter__(self) -> Self: ... # NoQA: E704
133
-
134
- def __exit__( # NoQA: E704
135
- self,
136
- exc_type: type[BaseException] | None,
137
- exc_val: BaseException | None,
138
- exc_tb: TracebackType | None,
139
- ) -> None: ...
140
-
141
-
142
152
  # inventory data on memory
143
- InventoryItem: TypeAlias = tuple[
144
- str, # project name
145
- str, # project version
146
- str, # URL
147
- str, # display name
148
- ]
149
- Inventory: TypeAlias = dict[str, dict[str, InventoryItem]]
153
+ Inventory: TypeAlias = dict[str, dict[str, '_InventoryItem']]
150
154
 
151
155
 
152
- class ExtensionMetadata(TypedDict, total=False):
156
+ class ExtensionMetadata(typing.TypedDict, total=False):
153
157
  """The metadata returned by an extension's ``setup()`` function.
154
158
 
155
159
  See :ref:`ext-metadata`.
@@ -170,7 +174,7 @@ class ExtensionMetadata(TypedDict, total=False):
170
174
 
171
175
 
172
176
  if TYPE_CHECKING:
173
- _ExtensionSetupFunc: TypeAlias = Callable[[Sphinx], ExtensionMetadata]
177
+ _ExtensionSetupFunc: TypeAlias = Callable[[Sphinx], ExtensionMetadata] # NoQA: PYI047 (false positive)
174
178
 
175
179
 
176
180
  def get_type_hints(
@@ -201,39 +205,28 @@ def get_type_hints(
201
205
  # Invalid object is given. But try to get __annotations__ as a fallback.
202
206
  return safe_getattr(obj, '__annotations__', {})
203
207
  except KeyError:
204
- # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
208
+ # a broken class found
209
+ # See: https://github.com/sphinx-doc/sphinx/issues/8084
205
210
  return {}
206
211
 
207
212
 
208
213
  def is_system_TypeVar(typ: Any) -> bool:
209
214
  """Check *typ* is system defined TypeVar."""
210
215
  modname = getattr(typ, '__module__', '')
211
- return modname == 'typing' and isinstance(typ, TypeVar)
216
+ return modname == 'typing' and isinstance(typ, typing.TypeVar)
212
217
 
213
218
 
214
219
  def _is_annotated_form(obj: Any) -> TypeIs[Annotated[Any, ...]]:
215
220
  """Check if *obj* is an annotated type."""
216
221
  return (
217
- typing.get_origin(obj) is Annotated
222
+ typing.get_origin(obj) is typing.Annotated
218
223
  or str(obj).startswith('typing.Annotated')
219
224
  ) # fmt: skip
220
225
 
221
226
 
222
227
  def _is_unpack_form(obj: Any) -> bool:
223
228
  """Check if the object is :class:`typing.Unpack` or equivalent."""
224
- if sys.version_info >= (3, 11):
225
- from typing import Unpack
226
-
227
- # typing_extensions.Unpack != typing.Unpack for 3.11, but we assume
228
- # that typing_extensions.Unpack should not be used in that case
229
- return typing.get_origin(obj) is Unpack
230
-
231
- # Python 3.10 requires typing_extensions.Unpack
232
- origin = typing.get_origin(obj)
233
- return (
234
- getattr(origin, '__module__', None) == 'typing_extensions'
235
- and origin.__name__ == 'Unpack'
236
- )
229
+ return typing.get_origin(obj) is typing.Unpack
237
230
 
238
231
 
239
232
  def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> str:
@@ -276,11 +269,11 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
276
269
  return f':py:class:`{module_prefix}{cls.__name__}`'
277
270
  elif ismock(cls):
278
271
  return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
279
- elif is_invalid_builtin_class(cls):
272
+ elif fixed_cls := is_invalid_builtin_class(cls):
280
273
  # The above predicate never raises TypeError but should not be
281
274
  # evaluated before determining whether *cls* is a mocked object
282
275
  # or not; instead of two try-except blocks, we keep it here.
283
- return f':py:class:`{module_prefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
276
+ return f':py:class:`{module_prefix}{fixed_cls}`'
284
277
  elif _is_annotated_form(cls):
285
278
  args = restify(cls.__args__[0], mode)
286
279
  meta_args = []
@@ -305,13 +298,18 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
305
298
  f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
306
299
  rf'\ [{args}, {meta}]'
307
300
  )
308
- elif isinstance(cls, NewType):
301
+ elif isinstance(cls, typing.NewType):
309
302
  return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`' # type: ignore[attr-defined]
310
- elif isinstance(cls, types.UnionType):
303
+ elif isinstance(cls, types.UnionType) or (
304
+ isgenericalias(cls)
305
+ and cls_module_is_typing
306
+ and cls.__origin__ is typing.Union
307
+ ):
311
308
  # Union types (PEP 585) retain their definition order when they
312
309
  # are printed natively and ``None``-like types are kept as is.
310
+ # *cls* is defined in ``typing``, and thus ``__args__`` must exist
313
311
  return ' | '.join(restify(a, mode) for a in cls.__args__)
314
- elif cls.__module__ in ('__builtin__', 'builtins'):
312
+ elif cls.__module__ in {'__builtin__', 'builtins'}:
315
313
  if hasattr(cls, '__args__'):
316
314
  if not cls.__args__: # Empty tuple, list, ...
317
315
  return rf':py:class:`{cls.__name__}`\ [{cls.__args__!r}]'
@@ -321,15 +319,11 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
321
319
  )
322
320
  return rf':py:class:`{cls.__name__}`\ [{concatenated_args}]'
323
321
  return f':py:class:`{cls.__name__}`'
324
- elif isgenericalias(cls) and cls_module_is_typing and cls.__origin__ is Union:
325
- # *cls* is defined in ``typing``, and thus ``__args__`` must exist
326
- return ' | '.join(restify(a, mode) for a in cls.__args__)
327
322
  elif isgenericalias(cls):
328
- if isinstance(cls.__origin__, typing._SpecialForm):
329
- # ClassVar; Concatenate; Final; Literal; Unpack; TypeGuard; TypeIs
330
- # Required/NotRequired
331
- text = restify(cls.__origin__, mode)
332
- elif cls.__name__:
323
+ if cls.__name__ and not isinstance(cls.__origin__, typing._SpecialForm):
324
+ # Represent generic aliases as the classes in ``typing`` rather
325
+ # than the underlying aliased classes,
326
+ # e.g. ``~typing.Tuple`` instead of ``tuple``.
333
327
  text = f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
334
328
  else:
335
329
  text = restify(cls.__origin__, mode)
@@ -361,12 +355,12 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
361
355
  return rf'{text}\ [{args}]'
362
356
  elif isinstance(cls, typing._SpecialForm):
363
357
  return f':py:obj:`~{cls.__module__}.{cls.__name__}`' # type: ignore[attr-defined]
364
- elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
358
+ elif cls is typing.Any:
365
359
  # handle bpo-46998
366
360
  return f':py:obj:`~{cls.__module__}.{cls.__name__}`'
367
361
  elif hasattr(cls, '__qualname__'):
368
362
  return f':py:class:`{module_prefix}{cls.__module__}.{cls.__qualname__}`'
369
- elif isinstance(cls, ForwardRef):
363
+ elif isinstance(cls, typing.ForwardRef):
370
364
  return f':py:class:`{cls.__forward_arg__}`'
371
365
  else:
372
366
  # not a class (ex. TypeVar) but should have a __name__
@@ -395,6 +389,8 @@ def stringify_annotation(
395
389
  annotation: Any,
396
390
  /,
397
391
  mode: _StringifyMode = 'fully-qualified-except-typing',
392
+ *,
393
+ short_literals: bool = False,
398
394
  ) -> str:
399
395
  """Stringify type annotation object.
400
396
 
@@ -408,6 +404,8 @@ def stringify_annotation(
408
404
  Show the name of the annotation.
409
405
  'fully-qualified'
410
406
  Show the module name and qualified name of the annotation.
407
+
408
+ :param short_literals: Render :py:class:`Literals` in PEP 604 style (``|``).
411
409
  """
412
410
  from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading
413
411
 
@@ -437,9 +435,12 @@ def stringify_annotation(
437
435
  annotation_module: str = getattr(annotation, '__module__', '')
438
436
  annotation_name: str = getattr(annotation, '__name__', '')
439
437
  annotation_module_is_typing = annotation_module == 'typing'
438
+ if sys.version_info[:2] >= (3, 14) and isinstance(annotation, typing.ForwardRef):
439
+ # ForwardRef moved from `typing` to `annotationlib` in Python 3.14.
440
+ annotation_module_is_typing = True
440
441
 
441
442
  # Extract the annotation's base type by considering formattable cases
442
- if isinstance(annotation, TypeVar) and not _is_unpack_form(annotation):
443
+ if isinstance(annotation, typing.TypeVar) and not _is_unpack_form(annotation):
443
444
  # typing_extensions.Unpack is incorrectly determined as a TypeVar
444
445
  if annotation_module_is_typing and mode in {
445
446
  'fully-qualified-except-typing',
@@ -447,14 +448,14 @@ def stringify_annotation(
447
448
  }:
448
449
  return annotation_name
449
450
  return module_prefix + f'{annotation_module}.{annotation_name}'
450
- elif isinstance(annotation, NewType):
451
+ elif isinstance(annotation, typing.NewType):
451
452
  return module_prefix + f'{annotation_module}.{annotation_name}'
452
453
  elif ismockmodule(annotation):
453
454
  return module_prefix + annotation_name
454
455
  elif ismock(annotation):
455
456
  return module_prefix + f'{annotation_module}.{annotation_name}'
456
- elif is_invalid_builtin_class(annotation):
457
- return module_prefix + _INVALID_BUILTIN_CLASSES[annotation]
457
+ elif fixed_annotation := is_invalid_builtin_class(annotation):
458
+ return module_prefix + fixed_annotation
458
459
  elif _is_annotated_form(annotation): # for py310+
459
460
  pass
460
461
  elif annotation_module == 'builtins' and annotation_qualname:
@@ -466,7 +467,10 @@ def stringify_annotation(
466
467
  if not args: # Empty tuple, list, ...
467
468
  return repr(annotation)
468
469
 
469
- concatenated_args = ', '.join(stringify_annotation(arg, mode) for arg in args)
470
+ concatenated_args = ', '.join(
471
+ stringify_annotation(arg, mode=mode, short_literals=short_literals)
472
+ for arg in args
473
+ )
470
474
  return f'{annotation_qualname}[{concatenated_args}]'
471
475
  else:
472
476
  # add other special cases that can be directly formatted
@@ -500,13 +504,16 @@ def stringify_annotation(
500
504
  # of ``typing`` and all of them define ``__origin__``
501
505
  qualname = stringify_annotation(
502
506
  annotation.__origin__,
503
- 'fully-qualified-except-typing',
507
+ mode='fully-qualified-except-typing',
508
+ short_literals=short_literals,
504
509
  ).replace('typing.', '') # ex. Union
505
510
  elif annotation_qualname:
506
511
  qualname = annotation_qualname
507
512
  elif hasattr(annotation, '__origin__'):
508
513
  # instantiated generic provided by a user
509
- qualname = stringify_annotation(annotation.__origin__, mode)
514
+ qualname = stringify_annotation(
515
+ annotation.__origin__, mode=mode, short_literals=short_literals
516
+ )
510
517
  elif isinstance(annotation, types.UnionType):
511
518
  qualname = 'types.UnionType'
512
519
  else:
@@ -529,33 +536,49 @@ def stringify_annotation(
529
536
  )
530
537
  return f'{module_prefix}Literal[{args}]'
531
538
  if qualname in {'Optional', 'Union', 'types.UnionType'}:
532
- return ' | '.join(stringify_annotation(a, mode) for a in annotation_args)
539
+ return ' | '.join(
540
+ stringify_annotation(a, mode=mode, short_literals=short_literals)
541
+ for a in annotation_args
542
+ )
533
543
  elif qualname == 'Callable':
534
544
  args = ', '.join(
535
- stringify_annotation(a, mode) for a in annotation_args[:-1]
545
+ stringify_annotation(a, mode=mode, short_literals=short_literals)
546
+ for a in annotation_args[:-1]
547
+ )
548
+ returns = stringify_annotation(
549
+ annotation_args[-1], mode=mode, short_literals=short_literals
536
550
  )
537
- returns = stringify_annotation(annotation_args[-1], mode)
538
551
  return f'{module_prefix}Callable[[{args}], {returns}]'
539
552
  elif qualname == 'Literal':
553
+ if short_literals:
554
+ return ' | '.join(
555
+ _format_literal_arg_stringify(a, mode=mode) for a in annotation_args
556
+ )
540
557
  args = ', '.join(
541
558
  _format_literal_arg_stringify(a, mode=mode) for a in annotation_args
542
559
  )
543
560
  return f'{module_prefix}Literal[{args}]'
544
561
  elif _is_annotated_form(annotation): # for py310+
545
- args = stringify_annotation(annotation_args[0], mode)
562
+ args = stringify_annotation(
563
+ annotation_args[0], mode=mode, short_literals=short_literals
564
+ )
546
565
  meta_args = []
547
566
  for m in annotation.__metadata__:
548
567
  if isinstance(m, type):
549
- meta_args.append(stringify_annotation(m, mode))
568
+ meta_args.append(
569
+ stringify_annotation(
570
+ m, mode=mode, short_literals=short_literals
571
+ )
572
+ )
550
573
  elif dataclasses.is_dataclass(m):
551
574
  # use stringify_annotation for the repr of field values rather than repr
552
575
  d_fields = ', '.join([
553
- f'{f.name}={stringify_annotation(getattr(m, f.name), mode)}'
576
+ f'{f.name}={stringify_annotation(getattr(m, f.name), mode=mode, short_literals=short_literals)}' # NoQA: E501
554
577
  for f in dataclasses.fields(m)
555
578
  if f.repr
556
579
  ])
557
580
  meta_args.append(
558
- f'{stringify_annotation(type(m), mode)}({d_fields})'
581
+ f'{stringify_annotation(type(m), mode=mode, short_literals=short_literals)}({d_fields})' # NoQA: E501
559
582
  )
560
583
  else:
561
584
  meta_args.append(repr(m))
@@ -570,7 +593,10 @@ def stringify_annotation(
570
593
  # Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
571
594
  return module_prefix + qualname
572
595
  else:
573
- args = ', '.join(stringify_annotation(a, mode) for a in annotation_args)
596
+ args = ', '.join(
597
+ stringify_annotation(a, mode=mode, short_literals=short_literals)
598
+ for a in annotation_args
599
+ )
574
600
  return f'{module_prefix}{qualname}[{args}]'
575
601
 
576
602
  return module_prefix + qualname
sphinx/versioning.py CHANGED
@@ -5,14 +5,14 @@ from __future__ import annotations
5
5
  import pickle
6
6
  from itertools import product, zip_longest
7
7
  from operator import itemgetter
8
- from os import path
9
- from typing import TYPE_CHECKING, Any
8
+ from typing import TYPE_CHECKING
10
9
  from uuid import uuid4
11
10
 
12
11
  from sphinx.transforms import SphinxTransform
13
12
 
14
13
  if TYPE_CHECKING:
15
14
  from collections.abc import Callable, Iterator
15
+ from typing import Any
16
16
 
17
17
  from docutils.nodes import Node
18
18
 
@@ -160,8 +160,8 @@ class UIDTransform(SphinxTransform):
160
160
 
161
161
  if env.versioning_compare:
162
162
  # get old doctree
163
+ filename = env.doctreedir / f'{env.docname}.doctree'
163
164
  try:
164
- filename = path.join(env.doctreedir, env.docname + '.doctree')
165
165
  with open(filename, 'rb') as f:
166
166
  old_doctree = pickle.load(f)
167
167
  except OSError:
sphinx/writers/html.py CHANGED
@@ -27,11 +27,12 @@ class HTMLWriter(Writer): # type: ignore[misc]
27
27
  def __init__(self, builder: StandaloneHTMLBuilder) -> None:
28
28
  super().__init__()
29
29
  self.builder = builder
30
+ self._has_maths_elements: bool = False
30
31
 
31
32
  def translate(self) -> None:
32
33
  # sadly, this is mostly copied from parent class
33
34
  visitor = self.builder.create_translator(self.document, self.builder)
34
- self.visitor = cast(HTML5Translator, visitor)
35
+ self.visitor = cast('HTML5Translator', visitor)
35
36
  self.document.walkabout(visitor)
36
37
  self.output = self.visitor.astext()
37
38
  for attr in (
@@ -57,3 +58,4 @@ class HTMLWriter(Writer): # type: ignore[misc]
57
58
  ):
58
59
  setattr(self, attr, getattr(visitor, attr, None))
59
60
  self.clean_meta = ''.join(self.visitor.meta[2:])
61
+ self._has_maths_elements = getattr(visitor, '_has_maths_elements', False)