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/__init__.py CHANGED
@@ -2,56 +2,33 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import hashlib
6
5
  import os
7
6
  import posixpath
8
7
  import re
9
- from typing import Any
10
-
11
- from sphinx.errors import ExtensionError as _ExtensionError
12
- from sphinx.errors import FiletypeNotFoundError
13
- from sphinx.util import _files, _importer, logging
14
- from sphinx.util import index_entries as _index_entries
15
- from sphinx.util._lines import parse_line_num_spec as parselinenos # NoQA: F401
16
- from sphinx.util._uri import encode_uri # NoQA: F401
17
- from sphinx.util._uri import is_url as isurl # NoQA: F401
18
- from sphinx.util.console import strip_colors # NoQA: F401
19
- from sphinx.util.matching import patfilter # NoQA: F401
20
- from sphinx.util.nodes import ( # NoQA: F401
21
- caption_ref_re,
22
- explicit_title_re,
23
- nested_parse_with_titles,
24
- split_explicit_title,
25
- )
26
-
27
- # import other utilities; partly for backwards compatibility, so don't
28
- # prune unused ones indiscriminately
29
- from sphinx.util.osutil import ( # NoQA: F401
30
- SEP,
31
- copyfile,
32
- ensuredir,
33
- make_filename,
34
- os_path,
35
- relative_uri,
36
- )
37
-
38
- logger = logging.getLogger(__name__)
8
+
9
+ TYPE_CHECKING = False
10
+ if TYPE_CHECKING:
11
+ import hashlib
12
+ from collections.abc import Callable
13
+ from types import ModuleType
14
+ from typing import Any
39
15
 
40
16
  # Generally useful regular expressions.
41
17
  ws_re: re.Pattern[str] = re.compile(r'\s+')
42
18
  url_re: re.Pattern[str] = re.compile(r'(?P<schema>.+)://.*')
43
19
 
44
-
45
20
  # High-level utility functions.
46
21
 
47
22
 
48
23
  def docname_join(basedocname: str, docname: str) -> str:
49
- return posixpath.normpath(posixpath.join('/' + basedocname, '..', docname))[1:]
24
+ return posixpath.normpath(posixpath.join(f'/{basedocname}', '..', docname))[1:]
50
25
 
51
26
 
52
27
  def get_filetype(
53
28
  source_suffix: dict[str, str], filename: str | os.PathLike[str]
54
29
  ) -> str:
30
+ from sphinx.errors import FiletypeNotFoundError
31
+
55
32
  for suffix, filetype in source_suffix.items():
56
33
  if os.fspath(filename).endswith(suffix):
57
34
  # If default filetype (None), considered as restructuredtext.
@@ -64,6 +41,8 @@ def _md5(data: bytes = b'', **_kw: Any) -> hashlib._Hash:
64
41
 
65
42
  To be removed in Sphinx 9.0
66
43
  """
44
+ import hashlib
45
+
67
46
  return hashlib.md5(data, usedforsecurity=False)
68
47
 
69
48
 
@@ -72,37 +51,107 @@ def _sha1(data: bytes = b'', **_kw: Any) -> hashlib._Hash:
72
51
 
73
52
  To be removed in Sphinx 9.0
74
53
  """
54
+ import hashlib
55
+
75
56
  return hashlib.sha1(data, usedforsecurity=False)
76
57
 
77
58
 
78
- # deprecated name -> (object to return, canonical path or empty string)
79
- _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
80
- 'split_index_msg': (
81
- _index_entries.split_index_msg,
82
- 'sphinx.util.index_entries.split_index_msg',
83
- (9, 0),
84
- ),
85
- 'split_into': (
86
- _index_entries.split_index_msg,
87
- 'sphinx.util.index_entries.split_into',
88
- (9, 0),
89
- ),
90
- 'ExtensionError': (_ExtensionError, 'sphinx.errors.ExtensionError', (9, 0)),
91
- 'md5': (_md5, '', (9, 0)),
92
- 'sha1': (_sha1, '', (9, 0)),
93
- 'import_object': (_importer.import_object, '', (10, 0)),
94
- 'FilenameUniqDict': (_files.FilenameUniqDict, '', (10, 0)),
95
- 'DownloadFiles': (_files.DownloadFiles, '', (10, 0)),
96
- }
59
+ def __getattr__(name: str) -> Any:
60
+ from sphinx.deprecation import _deprecation_warning
97
61
 
62
+ obj: Callable[..., Any]
63
+ mod: ModuleType
98
64
 
99
- def __getattr__(name: str) -> Any:
100
- if name not in _DEPRECATED_OBJECTS:
101
- msg = f'module {__name__!r} has no attribute {name!r}'
102
- raise AttributeError(msg)
65
+ # RemovedInSphinx90Warning
66
+ if name == 'split_index_msg':
67
+ from sphinx.util.index_entries import split_index_msg as obj
103
68
 
104
- from sphinx.deprecation import _deprecation_warning
69
+ canonical_name = f'{obj.__module__}.{obj.__qualname__}'
70
+ _deprecation_warning(__name__, name, canonical_name, remove=(9, 0))
71
+ return obj
72
+
73
+ if name == 'split_into':
74
+ from sphinx.util.index_entries import _split_into as obj
75
+
76
+ _deprecation_warning(__name__, name, '', remove=(9, 0))
77
+ return obj
78
+
79
+ if name == 'ExtensionError':
80
+ from sphinx.errors import ExtensionError as obj # NoQA: N813
81
+
82
+ canonical_name = f'{obj.__module__}.{obj.__qualname__}'
83
+ _deprecation_warning(__name__, name, canonical_name, remove=(9, 0))
84
+ return obj
85
+
86
+ if name in {'md5', 'sha1'}:
87
+ obj = globals()[f'_{name}']
88
+ canonical_name = f'hashlib.{name}'
89
+ _deprecation_warning(__name__, name, canonical_name, remove=(9, 0))
90
+ return obj
91
+
92
+ # RemovedInSphinx10Warning
93
+
94
+ if name in {'DownloadFiles', 'FilenameUniqDict'}:
95
+ from sphinx.util import _files as mod
96
+
97
+ obj = getattr(mod, name)
98
+ _deprecation_warning(__name__, name, '', remove=(10, 0))
99
+ return obj
100
+
101
+ # Re-exported for backwards compatibility,
102
+ # but not currently deprecated
103
+
104
+ if name == 'encode_uri':
105
+ from sphinx.util._uri import encode_uri
106
+
107
+ return encode_uri
108
+
109
+ if name == 'import_object':
110
+ from sphinx.util._importer import import_object
111
+
112
+ return import_object
113
+
114
+ if name == 'isurl':
115
+ from sphinx.util._uri import is_url
116
+
117
+ return is_url
118
+
119
+ if name == 'parselinenos':
120
+ from sphinx.util._lines import parse_line_num_spec
121
+
122
+ return parse_line_num_spec
123
+
124
+ if name == 'patfilter':
125
+ from sphinx.util.matching import patfilter
126
+
127
+ return patfilter
128
+
129
+ if name == 'strip_escape_sequences':
130
+ from sphinx._cli.util.errors import strip_escape_sequences
131
+
132
+ return strip_escape_sequences
133
+
134
+ if name in {
135
+ 'caption_ref_re',
136
+ 'explicit_title_re',
137
+ 'nested_parse_with_titles',
138
+ 'split_explicit_title',
139
+ }:
140
+ from sphinx.util import nodes as mod
141
+
142
+ return getattr(mod, name)
143
+
144
+ if name in {
145
+ 'SEP',
146
+ 'copyfile',
147
+ 'ensuredir',
148
+ 'make_filename',
149
+ 'os_path',
150
+ 'relative_uri',
151
+ }:
152
+ from sphinx.util import osutil as mod
153
+
154
+ return getattr(mod, name)
105
155
 
106
- deprecated_object, canonical_name, remove = _DEPRECATED_OBJECTS[name]
107
- _deprecation_warning(__name__, name, canonical_name, remove=remove)
108
- return deprecated_object
156
+ msg = f'module {__name__!r} has no attribute {name!r}'
157
+ raise AttributeError(msg)
sphinx/util/_files.py CHANGED
@@ -1,33 +1,44 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import hashlib
4
- import os.path
5
- from typing import Any
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ from sphinx.util._pathlib import _StrPath
8
+
9
+ if TYPE_CHECKING:
10
+ import os
11
+ from collections.abc import Set
6
12
 
7
13
 
8
14
  class FilenameUniqDict(dict[str, tuple[set[str], str]]):
9
- """
10
- A dictionary that automatically generates unique names for its keys,
15
+ """A dictionary that automatically generates unique names for its keys,
11
16
  interpreted as filenames, and keeps track of a set of docnames they
12
17
  appear in. Used for images and downloadable files in the environment.
13
18
  """
14
19
 
15
20
  def __init__(self) -> None:
21
+ super().__init__()
16
22
  self._existing: set[str] = set()
17
23
 
18
- def add_file(self, docname: str, newfile: str) -> str:
24
+ def add_file(self, docname: str, newfile: str | os.PathLike[str]) -> str:
25
+ newfile = str(newfile)
19
26
  if newfile in self:
20
- self[newfile][0].add(docname)
21
- return self[newfile][1]
22
- uniquename = os.path.basename(newfile)
23
- base, ext = os.path.splitext(uniquename)
27
+ docnames, unique_name = self[newfile]
28
+ docnames.add(docname)
29
+ return unique_name
30
+
31
+ new_file = Path(newfile)
32
+ unique_name = new_file.name
33
+ base = new_file.stem
34
+ ext = new_file.suffix
24
35
  i = 0
25
- while uniquename in self._existing:
36
+ while unique_name in self._existing:
26
37
  i += 1
27
- uniquename = f'{base}{i}{ext}'
28
- self[newfile] = ({docname}, uniquename)
29
- self._existing.add(uniquename)
30
- return uniquename
38
+ unique_name = f'{base}{i}{ext}'
39
+ self[newfile] = ({docname}, unique_name)
40
+ self._existing.add(unique_name)
41
+ return unique_name
31
42
 
32
43
  def purge_doc(self, docname: str) -> None:
33
44
  for filename, (docs, unique) in list(self.items()):
@@ -37,7 +48,7 @@ class FilenameUniqDict(dict[str, tuple[set[str], str]]):
37
48
  self._existing.discard(unique)
38
49
 
39
50
  def merge_other(
40
- self, docnames: set[str], other: dict[str, tuple[set[str], Any]]
51
+ self, docnames: Set[str], other: dict[str, tuple[set[str], str]]
41
52
  ) -> None:
42
53
  for filename, (docs, _unique) in other.items():
43
54
  for doc in docs & set(docnames):
@@ -50,21 +61,26 @@ class FilenameUniqDict(dict[str, tuple[set[str], str]]):
50
61
  self._existing = state
51
62
 
52
63
 
53
- class DownloadFiles(dict[str, tuple[set[str], str]]):
64
+ class DownloadFiles(dict[Path, tuple[set[str], _StrPath]]):
54
65
  """A special dictionary for download files.
55
66
 
56
67
  .. important:: This class would be refactored in nearly future.
57
68
  Hence don't hack this directly.
58
69
  """
59
70
 
60
- def add_file(self, docname: str, filename: str) -> str:
71
+ def add_file(self, docname: str, filename: str | os.PathLike[str]) -> _StrPath:
72
+ filename = Path(filename)
61
73
  if filename not in self:
62
- digest = hashlib.md5(filename.encode(), usedforsecurity=False).hexdigest()
63
- dest = f'{digest}/{os.path.basename(filename)}'
64
- self[filename] = (set(), dest)
74
+ digest = hashlib.md5(
75
+ filename.as_posix().encode(), usedforsecurity=False
76
+ ).hexdigest()
77
+ dest_path = _StrPath(digest, filename.name)
78
+ self[filename] = ({docname}, dest_path)
79
+ return dest_path
65
80
 
66
- self[filename][0].add(docname)
67
- return self[filename][1]
81
+ docnames, dest_path = self[filename]
82
+ docnames.add(docname)
83
+ return dest_path
68
84
 
69
85
  def purge_doc(self, docname: str) -> None:
70
86
  for filename, (docs, _dest) in list(self.items()):
@@ -73,7 +89,7 @@ class DownloadFiles(dict[str, tuple[set[str], str]]):
73
89
  del self[filename]
74
90
 
75
91
  def merge_other(
76
- self, docnames: set[str], other: dict[str, tuple[set[str], Any]]
92
+ self, docnames: Set[str], other: dict[Path, tuple[set[str], _StrPath]]
77
93
  ) -> None:
78
94
  for filename, (docs, _dest) in other.items():
79
95
  for docname in docs & set(docnames):
sphinx/util/_importer.py CHANGED
@@ -1,10 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from importlib import import_module
4
- from typing import Any
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from sphinx.errors import ExtensionError
7
7
 
8
+ if TYPE_CHECKING:
9
+ from typing import Any
10
+
8
11
 
9
12
  def import_object(object_name: str, /, source: str = '') -> Any:
10
13
  """Import python object by qualname."""
@@ -0,0 +1,76 @@
1
+ from __future__ import annotations
2
+
3
+ import zlib
4
+ from typing import TYPE_CHECKING
5
+
6
+ from sphinx.util import logging
7
+
8
+ BUFSIZE = 16 * 1024
9
+ logger = logging.getLogger(__name__)
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Iterator
13
+ from typing import Protocol
14
+
15
+ # Readable file stream for inventory loading
16
+ class _SupportsRead(Protocol):
17
+ def read(self, size: int = ...) -> bytes: ...
18
+
19
+
20
+ __all__ = ('InventoryFileReader',)
21
+
22
+
23
+ class InventoryFileReader:
24
+ """A file reader for an inventory file.
25
+
26
+ This reader supports mixture of texts and compressed texts.
27
+ """
28
+
29
+ def __init__(self, stream: _SupportsRead) -> None:
30
+ self.stream = stream
31
+ self.buffer = b''
32
+ self.eof = False
33
+
34
+ def read_buffer(self) -> None:
35
+ chunk = self.stream.read(BUFSIZE)
36
+ if chunk == b'':
37
+ self.eof = True
38
+ self.buffer += chunk
39
+
40
+ def readline(self) -> str:
41
+ pos = self.buffer.find(b'\n')
42
+ if pos != -1:
43
+ line = self.buffer[:pos].decode()
44
+ self.buffer = self.buffer[pos + 1 :]
45
+ elif self.eof:
46
+ line = self.buffer.decode()
47
+ self.buffer = b''
48
+ else:
49
+ self.read_buffer()
50
+ line = self.readline()
51
+
52
+ return line
53
+
54
+ def readlines(self) -> Iterator[str]:
55
+ while not self.eof:
56
+ line = self.readline()
57
+ if line:
58
+ yield line
59
+
60
+ def read_compressed_chunks(self) -> Iterator[bytes]:
61
+ decompressor = zlib.decompressobj()
62
+ while not self.eof:
63
+ self.read_buffer()
64
+ yield decompressor.decompress(self.buffer)
65
+ self.buffer = b''
66
+ yield decompressor.flush()
67
+
68
+ def read_compressed_lines(self) -> Iterator[str]:
69
+ buf = b''
70
+ for chunk in self.read_compressed_chunks():
71
+ buf += chunk
72
+ pos = buf.find(b'\n')
73
+ while pos != -1:
74
+ yield buf[:pos].decode()
75
+ buf = buf[pos + 1 :]
76
+ pos = buf.find(b'\n')
sphinx/util/_io.py CHANGED
@@ -2,13 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from sphinx.util.console import strip_escape_sequences
5
+ from sphinx._cli.util.errors import strip_escape_sequences
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from typing import Protocol
9
9
 
10
10
  class SupportsWrite(Protocol):
11
- def write(self, text: str, /) -> int | None: ... # NoQA: E704
11
+ def write(self, text: str, /) -> int | None: ...
12
12
 
13
13
 
14
14
  class TeeStripANSI:
sphinx/util/_lines.py CHANGED
@@ -1,3 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+
1
4
  def parse_line_num_spec(spec: str, total: int) -> list[int]:
2
5
  """Parse a line number spec (such as "1,2,4-6") and return a list of
3
6
  wanted line numbers.
@@ -8,17 +11,17 @@ def parse_line_num_spec(spec: str, total: int) -> list[int]:
8
11
  try:
9
12
  begend = part.strip().split('-')
10
13
  if begend == ['', '']:
11
- raise ValueError
14
+ raise ValueError # NoQA: TRY301
12
15
  if len(begend) == 1:
13
16
  items.append(int(begend[0]) - 1)
14
17
  elif len(begend) == 2:
15
18
  start = int(begend[0] or 1) # left half open (cf. -10)
16
19
  end = int(begend[1] or max(start, total)) # right half open (cf. 10-)
17
20
  if start > end: # invalid range (cf. 10-1)
18
- raise ValueError
21
+ raise ValueError # NoQA: TRY301
19
22
  items.extend(range(start - 1, end))
20
23
  else:
21
- raise ValueError
24
+ raise ValueError # NoQA: TRY301
22
25
  except ValueError as exc:
23
26
  msg = f'invalid line number spec: {spec!r}'
24
27
  raise ValueError(msg) from exc
sphinx/util/_pathlib.py CHANGED
@@ -3,7 +3,7 @@
3
3
  Instances of _StrPath should not be constructed except in Sphinx itself.
4
4
  Consumers of Sphinx APIs should prefer using ``pathlib.Path`` objects
5
5
  where possible. _StrPath objects can be treated as equivalent to ``Path``,
6
- save that ``_StrPath.replace`` is overriden with ``str.replace``.
6
+ save that ``_StrPath.replace`` is overridden with ``str.replace``.
7
7
 
8
8
  To continue treating path-like objects as strings, use ``os.fspath``,
9
9
  or explicit string coercion.
@@ -17,10 +17,13 @@ from __future__ import annotations
17
17
  import sys
18
18
  import warnings
19
19
  from pathlib import Path, PosixPath, PurePath, WindowsPath
20
- from typing import Any
20
+ from typing import TYPE_CHECKING, overload
21
21
 
22
22
  from sphinx.deprecation import RemovedInSphinx90Warning
23
23
 
24
+ if TYPE_CHECKING:
25
+ from typing import Any
26
+
24
27
  _STR_METHODS = frozenset(str.__dict__)
25
28
  _PATH_NAME = Path().__class__.__name__
26
29
 
@@ -133,3 +136,38 @@ else:
133
136
  def __len__(self) -> int:
134
137
  warnings.warn(_MSG, RemovedInSphinx90Warning, stacklevel=2)
135
138
  return len(self.__str__())
139
+
140
+
141
+ class _StrPathProperty:
142
+ def __init__(self) -> None:
143
+ self.instance_attr: str = ''
144
+
145
+ def __set_name__(self, owner: object, name: str) -> None:
146
+ self.instance_attr = f'_{name}' # i.e. '_srcdir'
147
+
148
+ @overload
149
+ def __get__(self, obj: None, objtype: None) -> _StrPathProperty: ...
150
+
151
+ @overload
152
+ def __get__(self, obj: object, objtype: type[object]) -> _StrPath: ...
153
+
154
+ def __get__(
155
+ self, obj: object | None, objtype: type[object] | None = None
156
+ ) -> _StrPathProperty | _StrPath:
157
+ if obj is None:
158
+ return self
159
+ if not self.instance_attr:
160
+ raise AttributeError
161
+ return getattr(obj, self.instance_attr)
162
+
163
+ def __set__(self, obj: Any, value: _StrPath | Path) -> None:
164
+ try:
165
+ setattr(obj, self.instance_attr, _StrPath(value))
166
+ except TypeError as err:
167
+ cls_name = type(obj).__qualname__
168
+ name = self.instance_attr.removeprefix('_')
169
+ msg = f'{cls_name}.{name} may only be set to path-like objects'
170
+ raise TypeError(msg) from err
171
+
172
+ def __delete__(self, obj: Any) -> None:
173
+ delattr(obj, self.instance_attr)
@@ -1,5 +1,7 @@
1
1
  """Build phase of Sphinx application."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from enum import IntEnum
4
6
 
5
7
 
sphinx/util/cfamily.py CHANGED
@@ -13,7 +13,7 @@ from sphinx.util import logging
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from collections.abc import Callable, Sequence
16
- from typing import Any, TypeAlias
16
+ from typing import Any, NoReturn, TypeAlias
17
17
 
18
18
  from docutils.nodes import TextElement
19
19
 
@@ -33,7 +33,7 @@ identifier_re = re.compile(
33
33
  | (@[a-zA-Z0-9_]) # our extension for names of anonymous entities
34
34
  )
35
35
  [a-zA-Z0-9_]*\b
36
- """,
36
+ """,
37
37
  flags=re.VERBOSE,
38
38
  )
39
39
  integer_literal_re = re.compile(r'[1-9][0-9]*(\'[0-9]+)*')
@@ -50,7 +50,7 @@ integers_literal_suffix_re = re.compile(
50
50
  )\b
51
51
  # the ending word boundary is important for distinguishing
52
52
  # between suffixes and UDLs in C++
53
- """,
53
+ """,
54
54
  flags=re.VERBOSE,
55
55
  )
56
56
  float_literal_re = re.compile(
@@ -66,7 +66,7 @@ float_literal_re = re.compile(
66
66
  [0-9a-fA-F]+(\'[0-9a-fA-F]+)*([pP][+-]?[0-9a-fA-F]+(\'[0-9a-fA-F]+)*)?)
67
67
  | (0[xX][0-9a-fA-F]+(\'[0-9a-fA-F]+)*\.([pP][+-]?[0-9a-fA-F]+(\'[0-9a-fA-F]+)*)?)
68
68
  )
69
- """,
69
+ """,
70
70
  flags=re.VERBOSE,
71
71
  )
72
72
  float_literal_suffix_re = re.compile(r'[fFlL]\b')
@@ -84,13 +84,13 @@ char_literal_re = re.compile(
84
84
  | (?:U[0-9a-fA-F]{8})
85
85
  ))
86
86
  )'
87
- """,
87
+ """,
88
88
  flags=re.VERBOSE,
89
89
  )
90
90
 
91
91
 
92
92
  def verify_description_mode(mode: str) -> None:
93
- if mode not in ('lastIsName', 'noneIsName', 'markType', 'markName', 'param', 'udl'):
93
+ if mode not in {'lastIsName', 'noneIsName', 'markType', 'markName', 'param', 'udl'}:
94
94
  raise Exception("Description mode '%s' is invalid." % mode)
95
95
 
96
96
 
@@ -108,11 +108,14 @@ class ASTBaseBase:
108
108
  except AttributeError:
109
109
  return False
110
110
 
111
+ def __hash__(self) -> int:
112
+ return hash(sorted(self.__dict__.items()))
113
+
111
114
  def clone(self) -> Any:
112
115
  return deepcopy(self)
113
116
 
114
117
  def _stringify(self, transform: StringifyTransform) -> str:
115
- raise NotImplementedError(repr(self))
118
+ raise NotImplementedError
116
119
 
117
120
  def __str__(self) -> str:
118
121
  return self._stringify(str)
@@ -121,7 +124,9 @@ class ASTBaseBase:
121
124
  return self._stringify(lambda ast: ast.get_display_string())
122
125
 
123
126
  def __repr__(self) -> str:
124
- return f'<{self.__class__.__name__}: {self._stringify(repr)}>'
127
+ if repr_string := self._stringify(repr):
128
+ return f'<{self.__class__.__name__}: {repr_string}>'
129
+ return f'<{self.__class__.__name__}>'
125
130
 
126
131
 
127
132
  ################################################################################
@@ -336,7 +341,7 @@ class BaseParser:
336
341
  indicator = '-' * self.pos + '^'
337
342
  logger.debug(f'{msg}\n{self.definition}\n{indicator}') # NoQA: G004
338
343
 
339
- def fail(self, msg: str) -> None:
344
+ def fail(self, msg: str) -> NoReturn:
340
345
  errors = []
341
346
  indicator = '-' * self.pos + '^'
342
347
  msg = (
@@ -431,7 +436,7 @@ class BaseParser:
431
436
  def _parse_balanced_token_seq(self, end: list[str]) -> str:
432
437
  # TODO: add handling of string literals and similar
433
438
  brackets = {'(': ')', '[': ']', '{': '}'}
434
- startPos = self.pos
439
+ start_pos = self.pos
435
440
  symbols: list[str] = []
436
441
  while not self.eof:
437
442
  if len(symbols) == 0 and self.current_char in end:
@@ -445,17 +450,17 @@ class BaseParser:
445
450
  self.pos += 1
446
451
  if self.eof:
447
452
  self.fail(
448
- f'Could not find end of balanced-token-seq starting at {startPos}.'
453
+ f'Could not find end of balanced-token-seq starting at {start_pos}.'
449
454
  )
450
- return self.definition[startPos : self.pos]
455
+ return self.definition[start_pos : self.pos]
451
456
 
452
457
  def _parse_attribute(self) -> ASTAttribute | None:
453
458
  self.skip_ws()
454
459
  # try C++11 style
455
- startPos = self.pos
460
+ start_pos = self.pos
456
461
  if self.skip_string_and_ws('['):
457
462
  if not self.skip_string('['):
458
- self.pos = startPos
463
+ self.pos = start_pos
459
464
  else:
460
465
  # TODO: actually implement the correct grammar
461
466
  arg = self._parse_balanced_token_seq(end=[']'])