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
@@ -6,8 +6,8 @@ import functools
6
6
  import os
7
7
  import pickle
8
8
  from collections import defaultdict
9
- from copy import copy
10
- from os import path
9
+ from copy import deepcopy
10
+ from pathlib import Path
11
11
  from typing import TYPE_CHECKING
12
12
 
13
13
  from sphinx import addnodes
@@ -23,17 +23,17 @@ from sphinx.locale import __
23
23
  from sphinx.transforms import SphinxTransformer
24
24
  from sphinx.util import logging
25
25
  from sphinx.util._files import DownloadFiles, FilenameUniqDict
26
+ from sphinx.util._pathlib import _StrPath, _StrPathProperty
26
27
  from sphinx.util._serialise import stable_str
27
28
  from sphinx.util._timestamps import _format_rfc3339_microseconds
28
29
  from sphinx.util.docutils import LoggingReporter
29
30
  from sphinx.util.i18n import CatalogRepository, docname_to_domain
30
31
  from sphinx.util.nodes import is_translatable
31
- from sphinx.util.osutil import _last_modified_time, canon_path, os_path
32
+ from sphinx.util.osutil import _last_modified_time, _relative_path
32
33
 
33
34
  if TYPE_CHECKING:
34
- from collections.abc import Callable, Iterable, Iterator
35
- from pathlib import Path
36
- from typing import Any, Literal
35
+ from collections.abc import Callable, Iterable, Iterator, Mapping
36
+ from typing import Any, Final, Literal
37
37
 
38
38
  from docutils import nodes
39
39
  from docutils.nodes import Node
@@ -43,9 +43,13 @@ if TYPE_CHECKING:
43
43
  from sphinx.builders import Builder
44
44
  from sphinx.config import Config
45
45
  from sphinx.domains import Domain
46
+ from sphinx.domains.c._symbol import Symbol as CSymbol
47
+ from sphinx.domains.cpp._symbol import Symbol as CPPSymbol
46
48
  from sphinx.events import EventManager
49
+ from sphinx.extension import Extension
47
50
  from sphinx.project import Project
48
- from sphinx.util._pathlib import _StrPath
51
+ from sphinx.registry import SphinxComponentRegistry
52
+ from sphinx.util.tags import Tags
49
53
 
50
54
  logger = logging.getLogger(__name__)
51
55
 
@@ -69,7 +73,7 @@ default_settings: dict[str, Any] = {
69
73
 
70
74
  # This is increased every time an environment attribute is added
71
75
  # or changed to properly invalidate pickle files.
72
- ENV_VERSION = 64
76
+ ENV_VERSION = 65
73
77
 
74
78
  # config status
75
79
  CONFIG_UNSET = -1
@@ -92,24 +96,26 @@ versioning_conditions: dict[str, Literal[False] | Callable[[Node], bool]] = {
92
96
 
93
97
 
94
98
  class BuildEnvironment:
95
- """
96
- The environment in which the ReST files are translated.
99
+ """The environment in which the ReST files are translated.
97
100
  Stores an inventory of cross-file targets and provides doctree
98
101
  transformations to resolve links to them.
99
102
  """
100
103
 
101
104
  # --------- ENVIRONMENT INITIALIZATION -------------------------------------
102
105
 
106
+ srcdir = _StrPathProperty()
107
+ doctreedir = _StrPathProperty()
108
+
103
109
  def __init__(self, app: Sphinx) -> None:
104
110
  self.app: Sphinx = app
105
- self.doctreedir: Path = app.doctreedir
106
- self.srcdir: Path = app.srcdir
111
+ self.doctreedir = app.doctreedir
112
+ self.srcdir = app.srcdir
107
113
  self.config: Config = None # type: ignore[assignment]
108
114
  self.config_status: int = CONFIG_UNSET
109
115
  self.config_status_extra: str = ''
110
116
  self.events: EventManager = app.events
111
117
  self.project: Project = app.project
112
- self.version: dict[str, int] = app.registry.get_envversion(app)
118
+ self.version: Mapping[str, int] = _get_env_version(app.extensions)
113
119
 
114
120
  # the method of doctree versioning; see set_versioning_method
115
121
  self.versioning_condition: Literal[False] | Callable[[Node], bool] | None = None
@@ -127,7 +133,7 @@ class BuildEnvironment:
127
133
  self.all_docs: dict[str, int] = {}
128
134
  # docname -> set of dependent file
129
135
  # names, relative to documentation root
130
- self.dependencies: dict[str, set[str]] = defaultdict(set)
136
+ self.dependencies: dict[str, set[_StrPath]] = defaultdict(set)
131
137
  # docname -> set of included file
132
138
  # docnames included from other documents
133
139
  self.included: dict[str, set[str]] = defaultdict(set)
@@ -194,10 +200,10 @@ class BuildEnvironment:
194
200
  self.original_image_uri: dict[_StrPath, str] = {}
195
201
 
196
202
  # temporary data storage while reading a document
197
- self.temp_data: dict[str, Any] = {}
203
+ self.current_document: _CurrentDocument = _CurrentDocument()
198
204
  # context for cross-references (e.g. current module or class)
199
- # this is similar to temp_data, but will for example be copied to
200
- # attributes of "any" cross references
205
+ # this is similar to ``self.current_document``,
206
+ # but will for example be copied to attributes of "any" cross references
201
207
  self.ref_context: dict[str, Any] = {}
202
208
 
203
209
  # search index data
@@ -220,7 +226,9 @@ class BuildEnvironment:
220
226
  self._search_index_objnames: dict[int, tuple[str, str, str]] = {}
221
227
 
222
228
  # all the registered domains, set by the application
223
- self.domains: _DomainsContainer = _DomainsContainer._from_environment(self)
229
+ self.domains: _DomainsContainer = _DomainsContainer._from_environment(
230
+ self, registry=app.registry
231
+ )
224
232
 
225
233
  # set up environment
226
234
  self.setup(app)
@@ -228,7 +236,7 @@ class BuildEnvironment:
228
236
  def __getstate__(self) -> dict[str, Any]:
229
237
  """Obtains serializable data for pickling."""
230
238
  __dict__ = self.__dict__.copy()
231
- # clear unpickable attributes
239
+ # clear unpickleable attributes
232
240
  __dict__.update(app=None, domains=None, events=None)
233
241
  # clear in-memory doctree caches, to reduce memory consumption and
234
242
  # ensure that, upon restoring the state, the most recent pickled files
@@ -241,7 +249,7 @@ class BuildEnvironment:
241
249
 
242
250
  def setup(self, app: Sphinx) -> None:
243
251
  """Set up BuildEnvironment object."""
244
- if self.version and self.version != app.registry.get_envversion(app):
252
+ if self.version and self.version != _get_env_version(app.extensions):
245
253
  raise BuildEnvironmentError(__('build environment version not current'))
246
254
  if self.srcdir and self.srcdir != app.srcdir:
247
255
  raise BuildEnvironmentError(__('source directory has changed'))
@@ -254,12 +262,14 @@ class BuildEnvironment:
254
262
  self.events = app.events
255
263
  self.srcdir = app.srcdir
256
264
  self.project = app.project
257
- self.version = app.registry.get_envversion(app)
265
+ self.version = _get_env_version(app.extensions)
258
266
 
259
267
  # initialise domains
260
268
  if self.domains is None:
261
269
  # if we are unpickling an environment, we need to recreate the domains
262
- self.domains = _DomainsContainer._from_environment(self)
270
+ self.domains = _DomainsContainer._from_environment(
271
+ self, registry=app.registry
272
+ )
263
273
  # setup domains (must do after all initialization)
264
274
  self.domains._setup()
265
275
 
@@ -274,6 +284,14 @@ class BuildEnvironment:
274
284
  # initialize settings
275
285
  self._update_settings(app.config)
276
286
 
287
+ @property
288
+ def _registry(self) -> SphinxComponentRegistry:
289
+ return self.app.registry
290
+
291
+ @property
292
+ def _tags(self) -> Tags:
293
+ return self.app.tags
294
+
277
295
  @staticmethod
278
296
  def _config_status(
279
297
  *, old_config: Config | None, new_config: Config, verbosity: int
@@ -408,7 +426,9 @@ class BuildEnvironment:
408
426
  """
409
427
  return self.project.doc2path(docname, absolute=base)
410
428
 
411
- def relfn2path(self, filename: str, docname: str | None = None) -> tuple[str, str]:
429
+ def relfn2path(
430
+ self, filename: str | Path, docname: str | None = None
431
+ ) -> tuple[str, str]:
412
432
  """Return paths to a file referenced from a document, relative to
413
433
  documentation root and absolute.
414
434
 
@@ -416,17 +436,21 @@ class BuildEnvironment:
416
436
  source dir, while relative filenames are relative to the dir of the
417
437
  containing document.
418
438
  """
419
- filename = os_path(filename)
420
- if filename.startswith(('/', os.sep)):
421
- rel_fn = filename[1:]
439
+ file_name = Path(filename)
440
+ if file_name.parts[:1] in {('/',), ('\\',)}:
441
+ abs_fn = self.srcdir.joinpath(*file_name.parts[1:]).resolve()
422
442
  else:
423
- docdir = path.dirname(self.doc2path(docname or self.docname, base=False))
424
- rel_fn = path.join(docdir, filename)
425
-
426
- return (
427
- canon_path(path.normpath(rel_fn)),
428
- path.normpath(path.join(self.srcdir, rel_fn)),
429
- )
443
+ if not docname:
444
+ if self.docname:
445
+ docname = self.docname
446
+ else:
447
+ msg = 'docname'
448
+ raise KeyError(msg)
449
+ doc_dir = self.doc2path(docname, base=False).parent
450
+ abs_fn = self.srcdir.joinpath(doc_dir, file_name).resolve()
451
+
452
+ rel_fn = _relative_path(abs_fn, self.srcdir)
453
+ return rel_fn.as_posix(), os.fspath(abs_fn)
430
454
 
431
455
  @property
432
456
  def found_docs(self) -> set[str]:
@@ -463,7 +487,7 @@ class BuildEnvironment:
463
487
  for docname in self.found_docs:
464
488
  domain = docname_to_domain(docname, self.config.gettext_compact)
465
489
  if domain in mo_paths:
466
- self.dependencies[docname].add(mo_paths[domain])
490
+ self.note_dependency(mo_paths[domain], docname=docname)
467
491
  except OSError as exc:
468
492
  raise DocumentError(
469
493
  __('Failed to scan documents in %s: %r') % (self.srcdir, exc)
@@ -489,8 +513,8 @@ class BuildEnvironment:
489
513
  added.add(docname)
490
514
  continue
491
515
  # if the doctree file is not there, rebuild
492
- filename = path.join(self.doctreedir, docname + '.doctree')
493
- if not path.isfile(filename):
516
+ filename = self.doctreedir / f'{docname}.doctree'
517
+ if not filename.is_file():
494
518
  logger.debug('[build target] changed %r', docname)
495
519
  changed.add(docname)
496
520
  continue
@@ -512,24 +536,26 @@ class BuildEnvironment:
512
536
  changed.add(docname)
513
537
  continue
514
538
  # finally, check the mtime of dependencies
539
+ if docname not in self.dependencies:
540
+ continue
515
541
  for dep in self.dependencies[docname]:
516
542
  try:
517
543
  # this will do the right thing when dep is absolute too
518
- deppath = path.join(self.srcdir, dep)
519
- if not path.isfile(deppath):
544
+ dep_path = self.srcdir / dep
545
+ if not dep_path.is_file():
520
546
  logger.debug(
521
547
  '[build target] changed %r missing dependency %r',
522
548
  docname,
523
- deppath,
549
+ dep_path,
524
550
  )
525
551
  changed.add(docname)
526
552
  break
527
- depmtime = _last_modified_time(deppath)
553
+ depmtime = _last_modified_time(dep_path)
528
554
  if depmtime > mtime:
529
555
  logger.debug(
530
556
  '[build target] outdated %r from dependency %r: %s -> %s',
531
557
  docname,
532
- deppath,
558
+ dep_path,
533
559
  _format_rfc3339_microseconds(mtime),
534
560
  _format_rfc3339_microseconds(depmtime),
535
561
  )
@@ -554,43 +580,57 @@ class BuildEnvironment:
554
580
 
555
581
  def prepare_settings(self, docname: str) -> None:
556
582
  """Prepare to set up environment for reading."""
557
- self.temp_data['docname'] = docname
558
- # defaults to the global default, but can be re-set in a document
559
- self.temp_data['default_role'] = self.config.default_role
560
- self.temp_data['default_domain'] = self.domains.get(self.config.primary_domain)
583
+ self.current_document = _CurrentDocument(
584
+ docname=docname,
585
+ # defaults to the global default, but can be re-set in a document
586
+ default_role=self.config.default_role,
587
+ default_domain=self.domains.get(self.config.primary_domain),
588
+ )
561
589
 
562
590
  # utilities to use while reading a document
563
591
 
592
+ @property
593
+ def temp_data(self) -> _CurrentDocument:
594
+ """Returns the temporary data storage for the current document.
595
+
596
+ Kept for backwards compatibility.
597
+ """
598
+ return self.current_document
599
+
564
600
  @property
565
601
  def docname(self) -> str:
566
602
  """Returns the docname of the document currently being parsed."""
567
- return self.temp_data['docname']
603
+ return self.current_document.docname
568
604
 
569
605
  @property
570
606
  def parser(self) -> Parser:
571
607
  """Returns the parser being used for to parse the current document."""
572
- return self.temp_data['_parser']
608
+ if (parser := self.current_document._parser) is not None:
609
+ return parser
610
+ msg = 'parser'
611
+ raise KeyError(msg)
573
612
 
574
613
  def new_serialno(self, category: str = '') -> int:
575
614
  """Return a serial number, e.g. for index entry targets.
576
615
 
577
616
  The number is guaranteed to be unique in the current document.
578
617
  """
579
- key = category + 'serialno'
580
- cur = self.temp_data.get(key, 0)
581
- self.temp_data[key] = cur + 1
582
- return cur
618
+ return self.current_document.new_serial_number(category)
583
619
 
584
- def note_dependency(self, filename: str) -> None:
620
+ def note_dependency(
621
+ self, filename: str | os.PathLike[str], *, docname: str | None = None
622
+ ) -> None:
585
623
  """Add *filename* as a dependency of the current document.
586
624
 
587
625
  This means that the document will be rebuilt if this file changes.
588
626
 
589
627
  *filename* should be absolute or relative to the source directory.
590
628
  """
591
- self.dependencies[self.docname].add(filename)
629
+ if docname is None:
630
+ docname = self.docname
631
+ self.dependencies.setdefault(docname, set()).add(_StrPath(filename))
592
632
 
593
- def note_included(self, filename: str) -> None:
633
+ def note_included(self, filename: str | os.PathLike[str]) -> None:
594
634
  """Add *filename* as a included from other document.
595
635
 
596
636
  This means the document is not orphaned.
@@ -599,7 +639,7 @@ class BuildEnvironment:
599
639
  """
600
640
  doc = self.path2doc(filename)
601
641
  if doc:
602
- self.included[self.docname].add(doc)
642
+ self.included.setdefault(self.docname, set()).add(doc)
603
643
 
604
644
  def note_reread(self) -> None:
605
645
  """Add the current document to the list of documents that will
@@ -625,7 +665,7 @@ class BuildEnvironment:
625
665
  try:
626
666
  serialised = self._pickled_doctree_cache[docname]
627
667
  except KeyError:
628
- filename = path.join(self.doctreedir, docname + '.doctree')
668
+ filename = self.doctreedir / f'{docname}.doctree'
629
669
  with open(filename, 'rb') as f:
630
670
  serialised = self._pickled_doctree_cache[docname] = f.read()
631
671
 
@@ -669,6 +709,7 @@ class BuildEnvironment:
669
709
  toctreenode,
670
710
  prune=prune_toctrees,
671
711
  includehidden=includehidden,
712
+ tags=builder.tags,
672
713
  )
673
714
  if result is None:
674
715
  toctreenode.parent.replace(toctreenode, [])
@@ -709,6 +750,7 @@ class BuildEnvironment:
709
750
  titles_only=titles_only,
710
751
  collapse=collapse,
711
752
  includehidden=includehidden,
753
+ tags=builder.tags,
712
754
  )
713
755
 
714
756
  def resolve_references(
@@ -718,17 +760,19 @@ class BuildEnvironment:
718
760
 
719
761
  def apply_post_transforms(self, doctree: nodes.document, docname: str) -> None:
720
762
  """Apply all post-transforms."""
763
+ backup = self.current_document
764
+ new = deepcopy(backup)
765
+ new.docname = docname
721
766
  try:
722
767
  # set env.docname during applying post-transforms
723
- backup = copy(self.temp_data)
724
- self.temp_data['docname'] = docname
768
+ self.current_document = new
725
769
 
726
770
  transformer = SphinxTransformer(doctree)
727
771
  transformer.set_environment(self)
728
- transformer.add_transforms(self.app.registry.get_post_transforms())
772
+ transformer.add_transforms(self._registry.get_post_transforms())
729
773
  transformer.apply_transforms()
730
774
  finally:
731
- self.temp_data = backup
775
+ self.current_document = backup
732
776
 
733
777
  # allow custom references to be resolved
734
778
  self.events.emit('doctree-resolved', doctree, docname)
@@ -766,7 +810,10 @@ class BuildEnvironment:
766
810
  if 'orphan' in self.metadata[docname]:
767
811
  continue
768
812
  logger.warning(
769
- __("document isn't included in any toctree"), location=docname
813
+ __("document isn't included in any toctree"),
814
+ location=docname,
815
+ type='toc',
816
+ subtype='not_included',
770
817
  )
771
818
  # Call _check_toc_parents here rather than in _get_toctree_ancestors()
772
819
  # because that method is called multiple times per document and would
@@ -778,6 +825,16 @@ class BuildEnvironment:
778
825
  self.events.emit('env-check-consistency', self)
779
826
 
780
827
 
828
+ def _get_env_version(extensions: Mapping[str, Extension]) -> Mapping[str, int]:
829
+ env_version = {
830
+ ext.name: ext_env_version
831
+ for ext in extensions.values()
832
+ if (ext_env_version := ext.metadata.get('env_version'))
833
+ }
834
+ env_version['sphinx'] = ENV_VERSION
835
+ return env_version
836
+
837
+
781
838
  def _differing_config_keys(old: Config, new: Config) -> frozenset[str]:
782
839
  """Return a set of keys that differ between two config objects."""
783
840
  old_vals = {c.name: c.value for c in old}
@@ -838,3 +895,230 @@ def _check_toc_parents(toctree_includes: dict[str, list[str]]) -> None:
838
895
  type='toc',
839
896
  subtype='multiple_toc_parents',
840
897
  )
898
+
899
+
900
+ class _CurrentDocument:
901
+ """Temporary data storage while reading a document.
902
+
903
+ This class is only for internal use. Please don't use this in your extensions.
904
+ It will be removed or changed without notice.
905
+ The only stable API is via ``env.current_document``.
906
+ """
907
+
908
+ __slots__ = (
909
+ '_parser',
910
+ '_serial_numbers',
911
+ '_extension_data',
912
+ 'autodoc_annotations',
913
+ 'autodoc_class',
914
+ 'autodoc_module',
915
+ 'c_last_symbol',
916
+ 'c_namespace_stack',
917
+ 'c_parent_symbol',
918
+ 'cpp_domain_name',
919
+ 'cpp_last_symbol',
920
+ 'cpp_namespace_stack',
921
+ 'cpp_parent_symbol',
922
+ 'default_domain',
923
+ 'default_role',
924
+ 'docname',
925
+ 'highlight_language',
926
+ 'obj_desc_name',
927
+ 'reading_started_at',
928
+ )
929
+
930
+ # Map of old-style temp_data keys to _CurrentDocument attributes
931
+ __attr_map: Final = {
932
+ '_parser': '_parser',
933
+ 'annotations': 'autodoc_annotations',
934
+ 'autodoc:class': 'autodoc_class',
935
+ 'autodoc:module': 'autodoc_module',
936
+ 'c:last_symbol': 'c_last_symbol',
937
+ 'c:namespace_stack': 'c_namespace_stack',
938
+ 'c:parent_symbol': 'c_parent_symbol',
939
+ 'cpp:domain_name': 'cpp_domain_name',
940
+ 'cpp:last_symbol': 'cpp_last_symbol',
941
+ 'cpp:namespace_stack': 'cpp_namespace_stack',
942
+ 'cpp:parent_symbol': 'cpp_parent_symbol',
943
+ 'default_domain': 'default_domain',
944
+ 'default_role': 'default_role',
945
+ 'docname': 'docname',
946
+ 'highlight_language': 'highlight_language',
947
+ 'object': 'obj_desc_name',
948
+ 'started_at': 'reading_started_at',
949
+ }
950
+
951
+ # Attributes that should reset to None if popped.
952
+ __attr_default_none: Final = frozenset({
953
+ '_parser',
954
+ 'c:last_symbol',
955
+ 'c:parent_symbol',
956
+ 'cpp:last_symbol',
957
+ 'cpp:parent_symbol',
958
+ 'default_domain',
959
+ })
960
+
961
+ def __init__(
962
+ self,
963
+ *,
964
+ docname: str = '',
965
+ default_role: str = '',
966
+ default_domain: Domain | None = None,
967
+ ) -> None:
968
+ #: The docname of the document currently being parsed.
969
+ self.docname: str = docname
970
+
971
+ #: The default role for the current document.
972
+ #: Set by the ``.. default-role::`` directive.
973
+ self.default_role: str = default_role
974
+
975
+ #: The default domain for the current document.
976
+ #: Set by the ``.. default-domain::`` directive.
977
+ self.default_domain: Domain | None = default_domain
978
+
979
+ #: The parser being used to parse the current document.
980
+ self._parser: Parser | None = None
981
+
982
+ #: The default language for syntax highlighting.
983
+ #: Set by the ``.. highlight::`` directive to override
984
+ #: the ``highlight_language`` config value.
985
+ self.highlight_language: str = ''
986
+
987
+ #: The current object's name.
988
+ #: Used in the Changes builder.
989
+ self.obj_desc_name: str = ''
990
+
991
+ #: Records type hints of Python objects in the current document.
992
+ #: Used in ``sphinx.ext.autodoc.typehints``.
993
+ #: Maps object names to maps of attribute names -> type hints.
994
+ self.autodoc_annotations: dict[str, dict[str, str]] = {}
995
+
996
+ #: The current Python class name.
997
+ #: Used in ``sphinx.ext.autodoc``.
998
+ self.autodoc_class: str = ''
999
+
1000
+ #: The current Python module name.
1001
+ #: Used in ``sphinx.ext.autodoc``.
1002
+ self.autodoc_module: str = ''
1003
+
1004
+ #: The most-recently added declaration in a directive.
1005
+ #: Used in the C Domain.
1006
+ self.c_last_symbol: CSymbol | None = None
1007
+
1008
+ #: The stack of namespace scopes, altered by the ``.. c:namespace::``
1009
+ #: and ``.. c:namespace-(push|pop)::``directives.
1010
+ #: Used in the C Domain.
1011
+ self.c_namespace_stack: list[CSymbol] = []
1012
+
1013
+ #: The parent declaration.
1014
+ #: Used in the C Domain.
1015
+ self.c_parent_symbol: CSymbol | None = None
1016
+
1017
+ #: A stack of the string representation of declarations,
1018
+ #: used to format the table of contents entry.
1019
+ #: Used in the C++ Domain.
1020
+ self.cpp_domain_name: tuple[str, ...] = ()
1021
+
1022
+ #: The most-recently added declaration in a directive.
1023
+ #: Used in the C++ Domain.
1024
+ self.cpp_last_symbol: CPPSymbol | None = None
1025
+
1026
+ #: The stack of namespace scopes, altered by the ``.. cpp:namespace::``
1027
+ #: and ``.. cpp:namespace-(push|pop)::``directives.
1028
+ #: Used in the C++ Domain.
1029
+ self.cpp_namespace_stack: list[CPPSymbol] = []
1030
+
1031
+ #: The parent declaration.
1032
+ #: Used in the C++ Domain.
1033
+ self.cpp_parent_symbol: CPPSymbol | None = None
1034
+
1035
+ #: Records the time when reading begain for the current document.
1036
+ #: Used in ``sphinx.ext.duration``.
1037
+ self.reading_started_at: float = 0.0
1038
+
1039
+ # Used for generating unique serial numbers.
1040
+ self._serial_numbers: dict[str, int] = {}
1041
+
1042
+ # Stores properties relating to the current document set by extensions.
1043
+ self._extension_data: dict[str, Any] = {}
1044
+
1045
+ def new_serial_number(self, category: str = '', /) -> int:
1046
+ """Return a serial number, e.g. for index entry targets.
1047
+
1048
+ The number is guaranteed to be unique in the current document & category.
1049
+ """
1050
+ current = self._serial_numbers.get(category, 0)
1051
+ self._serial_numbers[category] = current + 1
1052
+ return current
1053
+
1054
+ # Mapping interface:
1055
+
1056
+ def __getitem__(self, item: str) -> Any:
1057
+ if item in self.__attr_map:
1058
+ return getattr(self, self.__attr_map[item])
1059
+ return self._extension_data[item]
1060
+
1061
+ def __setitem__(self, key: str, value: Any) -> None:
1062
+ if key in self.__attr_map:
1063
+ setattr(self, self.__attr_map[key], value)
1064
+ else:
1065
+ self._extension_data[key] = value
1066
+
1067
+ def __delitem__(self, key: str) -> None:
1068
+ self.pop(key, default=None)
1069
+
1070
+ def __contains__(self, item: str) -> bool:
1071
+ if item in {'c:parent_symbol', 'cpp:parent_symbol'}:
1072
+ return getattr(self, item) is not None
1073
+ return item in self.__attr_map or item in self._extension_data
1074
+
1075
+ def __iter__(self) -> Iterator[str]:
1076
+ return iter(self.keys())
1077
+
1078
+ def __len__(self) -> int:
1079
+ return len(self.__attr_map) + len(self._extension_data)
1080
+
1081
+ def keys(self) -> Iterable[str]:
1082
+ return frozenset(self.__attr_map.keys() | self._extension_data.keys())
1083
+
1084
+ def items(self) -> Iterable[tuple[str, Any]]:
1085
+ for key in self.keys():
1086
+ yield key, self[key]
1087
+
1088
+ def values(self) -> Iterable[Any]:
1089
+ for key in self.keys():
1090
+ yield self[key]
1091
+
1092
+ def get(self, key: str, default: Any | None = None) -> Any | None:
1093
+ try:
1094
+ return self[key]
1095
+ except KeyError:
1096
+ return default
1097
+
1098
+ __sentinel = object()
1099
+
1100
+ def pop(self, key: str, default: Any | None = __sentinel) -> Any | None:
1101
+ if key in self.__attr_map:
1102
+ # the keys in __attr_map always exist, so ``default`` is ignored
1103
+ value = getattr(self, self.__attr_map[key])
1104
+ if key in self.__attr_default_none:
1105
+ default = None
1106
+ else:
1107
+ default = type(value)() # set key to type's default
1108
+ setattr(self, self.__attr_map[key], default)
1109
+ return value
1110
+ if default is self.__sentinel:
1111
+ return self._extension_data.pop(key)
1112
+ return self._extension_data.pop(key, default)
1113
+
1114
+ def setdefault(self, key: str, default: Any | None = None) -> Any | None:
1115
+ return self._extension_data.setdefault(key, default)
1116
+
1117
+ def clear(self) -> None:
1118
+ _CurrentDocument.__init__(self) # NoQA: PLC2801
1119
+
1120
+ def update(self, other: Iterable[tuple[str, Any]] = (), /, **kwargs: Any) -> None:
1121
+ other_dict = dict(other) if not isinstance(other, dict) else other
1122
+ for dct in other_dict, kwargs:
1123
+ for key, value in dct.items():
1124
+ self[key] = value
@@ -1,8 +1,14 @@
1
1
  """Assets adapter for sphinx.environment."""
2
2
 
3
- from sphinx.environment import BuildEnvironment
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
4
7
  from sphinx.util._pathlib import _StrPath
5
8
 
9
+ if TYPE_CHECKING:
10
+ from sphinx.environment import BuildEnvironment
11
+
6
12
 
7
13
  class ImageAdapter:
8
14
  def __init__(self, env: BuildEnvironment) -> None: