Sphinx 8.1.3__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.
- sphinx/__init__.py +8 -4
- sphinx/__main__.py +2 -0
- sphinx/_cli/__init__.py +2 -5
- sphinx/_cli/util/colour.py +34 -11
- sphinx/_cli/util/errors.py +128 -61
- sphinx/addnodes.py +51 -35
- sphinx/application.py +362 -230
- sphinx/builders/__init__.py +87 -64
- sphinx/builders/_epub_base.py +65 -56
- sphinx/builders/changes.py +17 -23
- sphinx/builders/dirhtml.py +8 -13
- sphinx/builders/epub3.py +70 -38
- sphinx/builders/gettext.py +93 -73
- sphinx/builders/html/__init__.py +240 -186
- sphinx/builders/html/_assets.py +9 -2
- sphinx/builders/html/_build_info.py +3 -0
- sphinx/builders/latex/__init__.py +64 -54
- sphinx/builders/latex/constants.py +14 -11
- sphinx/builders/latex/nodes.py +2 -0
- sphinx/builders/latex/theming.py +8 -9
- sphinx/builders/latex/transforms.py +7 -5
- sphinx/builders/linkcheck.py +193 -149
- sphinx/builders/manpage.py +17 -17
- sphinx/builders/singlehtml.py +28 -16
- sphinx/builders/texinfo.py +28 -21
- sphinx/builders/text.py +10 -15
- sphinx/builders/xml.py +10 -19
- sphinx/cmd/build.py +49 -119
- sphinx/cmd/make_mode.py +35 -31
- sphinx/cmd/quickstart.py +78 -62
- sphinx/config.py +265 -163
- sphinx/directives/__init__.py +51 -54
- sphinx/directives/admonitions.py +107 -0
- sphinx/directives/code.py +24 -19
- sphinx/directives/other.py +21 -42
- sphinx/directives/patches.py +28 -16
- sphinx/domains/__init__.py +54 -31
- sphinx/domains/_domains_container.py +22 -17
- sphinx/domains/_index.py +5 -8
- sphinx/domains/c/__init__.py +366 -245
- sphinx/domains/c/_ast.py +378 -256
- sphinx/domains/c/_ids.py +89 -31
- sphinx/domains/c/_parser.py +283 -214
- sphinx/domains/c/_symbol.py +269 -198
- sphinx/domains/changeset.py +39 -24
- sphinx/domains/citation.py +54 -24
- sphinx/domains/cpp/__init__.py +517 -362
- sphinx/domains/cpp/_ast.py +999 -682
- sphinx/domains/cpp/_ids.py +133 -65
- sphinx/domains/cpp/_parser.py +746 -588
- sphinx/domains/cpp/_symbol.py +692 -489
- sphinx/domains/index.py +10 -8
- sphinx/domains/javascript.py +152 -74
- sphinx/domains/math.py +50 -40
- sphinx/domains/python/__init__.py +402 -211
- sphinx/domains/python/_annotations.py +134 -61
- sphinx/domains/python/_object.py +155 -68
- sphinx/domains/rst.py +94 -49
- sphinx/domains/std/__init__.py +510 -249
- sphinx/environment/__init__.py +345 -61
- sphinx/environment/adapters/asset.py +7 -1
- sphinx/environment/adapters/indexentries.py +15 -20
- sphinx/environment/adapters/toctree.py +19 -9
- sphinx/environment/collectors/__init__.py +3 -1
- sphinx/environment/collectors/asset.py +18 -15
- sphinx/environment/collectors/dependencies.py +8 -10
- sphinx/environment/collectors/metadata.py +6 -4
- sphinx/environment/collectors/title.py +3 -1
- sphinx/environment/collectors/toctree.py +4 -4
- sphinx/errors.py +1 -3
- sphinx/events.py +4 -4
- sphinx/ext/apidoc/__init__.py +66 -0
- sphinx/ext/apidoc/__main__.py +9 -0
- sphinx/ext/apidoc/_cli.py +356 -0
- sphinx/ext/apidoc/_extension.py +262 -0
- sphinx/ext/apidoc/_generate.py +356 -0
- sphinx/ext/apidoc/_shared.py +99 -0
- sphinx/ext/autodoc/__init__.py +829 -480
- sphinx/ext/autodoc/directive.py +57 -21
- sphinx/ext/autodoc/importer.py +184 -67
- sphinx/ext/autodoc/mock.py +25 -10
- sphinx/ext/autodoc/preserve_defaults.py +17 -9
- sphinx/ext/autodoc/type_comment.py +56 -29
- sphinx/ext/autodoc/typehints.py +49 -26
- sphinx/ext/autosectionlabel.py +28 -11
- sphinx/ext/autosummary/__init__.py +281 -142
- sphinx/ext/autosummary/generate.py +121 -51
- sphinx/ext/coverage.py +152 -91
- sphinx/ext/doctest.py +169 -101
- sphinx/ext/duration.py +12 -6
- sphinx/ext/extlinks.py +33 -21
- sphinx/ext/githubpages.py +8 -8
- sphinx/ext/graphviz.py +175 -109
- sphinx/ext/ifconfig.py +11 -6
- sphinx/ext/imgconverter.py +48 -25
- sphinx/ext/imgmath.py +127 -97
- sphinx/ext/inheritance_diagram.py +177 -103
- sphinx/ext/intersphinx/__init__.py +22 -13
- sphinx/ext/intersphinx/__main__.py +3 -1
- sphinx/ext/intersphinx/_cli.py +18 -14
- sphinx/ext/intersphinx/_load.py +91 -82
- sphinx/ext/intersphinx/_resolve.py +108 -74
- sphinx/ext/intersphinx/_shared.py +2 -2
- sphinx/ext/linkcode.py +28 -12
- sphinx/ext/mathjax.py +60 -29
- sphinx/ext/napoleon/__init__.py +19 -7
- sphinx/ext/napoleon/docstring.py +229 -231
- sphinx/ext/todo.py +44 -49
- sphinx/ext/viewcode.py +105 -57
- sphinx/extension.py +3 -1
- sphinx/highlighting.py +13 -7
- sphinx/io.py +9 -13
- sphinx/jinja2glue.py +29 -26
- sphinx/locale/__init__.py +8 -9
- sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ar/LC_MESSAGES/sphinx.po +2155 -2050
- sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bg/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/bn/LC_MESSAGES/sphinx.po +2175 -2070
- sphinx/locale/ca/LC_MESSAGES/sphinx.js +3 -3
- sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca/LC_MESSAGES/sphinx.po +2690 -2585
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.js +63 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ca@valencia/LC_MESSAGES/sphinx.po +4216 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cak/LC_MESSAGES/sphinx.po +2096 -1991
- sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cs/LC_MESSAGES/sphinx.po +2248 -2143
- sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/cy/LC_MESSAGES/sphinx.po +2201 -2096
- sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/da/LC_MESSAGES/sphinx.po +2282 -2177
- sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de/LC_MESSAGES/sphinx.po +2261 -2156
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/el/LC_MESSAGES/sphinx.po +2604 -2499
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eo/LC_MESSAGES/sphinx.po +2078 -1973
- sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es/LC_MESSAGES/sphinx.po +2633 -2528
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/et/LC_MESSAGES/sphinx.po +2449 -2344
- sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/eu/LC_MESSAGES/sphinx.po +2241 -2136
- sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fa/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fi/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr/LC_MESSAGES/sphinx.po +513 -509
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/gl/LC_MESSAGES/sphinx.po +2644 -2539
- sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/he/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi/LC_MESSAGES/sphinx.po +504 -500
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hr/LC_MESSAGES/sphinx.po +501 -497
- sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/hu/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/id/LC_MESSAGES/sphinx.po +2609 -2504
- sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/is/LC_MESSAGES/sphinx.po +499 -495
- sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/it/LC_MESSAGES/sphinx.po +2265 -2160
- sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ja/LC_MESSAGES/sphinx.po +2621 -2516
- sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ka/LC_MESSAGES/sphinx.po +2567 -2462
- sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ko/LC_MESSAGES/sphinx.po +2631 -2526
- sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lt/LC_MESSAGES/sphinx.po +2214 -2109
- sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/lv/LC_MESSAGES/sphinx.po +2218 -2113
- sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/mk/LC_MESSAGES/sphinx.po +2088 -1983
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +2247 -2142
- sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ne/LC_MESSAGES/sphinx.po +2227 -2122
- sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/nl/LC_MESSAGES/sphinx.po +2316 -2211
- sphinx/locale/pl/LC_MESSAGES/sphinx.js +2 -2
- sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pl/LC_MESSAGES/sphinx.po +2442 -2336
- sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +2657 -2552
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +2243 -2138
- sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ro/LC_MESSAGES/sphinx.po +2244 -2139
- sphinx/locale/ru/LC_MESSAGES/sphinx.js +1 -1
- sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ru/LC_MESSAGES/sphinx.po +2660 -2555
- sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/si/LC_MESSAGES/sphinx.po +2134 -2029
- sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sk/LC_MESSAGES/sphinx.po +2614 -2509
- sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sl/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/sphinx.pot +2069 -1964
- sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sq/LC_MESSAGES/sphinx.po +2661 -2556
- sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sr/LC_MESSAGES/sphinx.po +2213 -2108
- sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/sv/LC_MESSAGES/sphinx.po +2229 -2124
- sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/te/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/tr/LC_MESSAGES/sphinx.po +2608 -2503
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +2167 -2062
- sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ur/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/vi/LC_MESSAGES/sphinx.po +2204 -2099
- sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/yue/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +2659 -2554
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +2045 -1940
- sphinx/parsers.py +8 -7
- sphinx/project.py +2 -2
- sphinx/pycode/__init__.py +31 -21
- sphinx/pycode/ast.py +6 -3
- sphinx/pycode/parser.py +14 -8
- sphinx/pygments_styles.py +4 -5
- sphinx/registry.py +192 -92
- sphinx/roles.py +58 -7
- sphinx/search/__init__.py +75 -54
- sphinx/search/en.py +11 -13
- sphinx/search/fi.py +1 -1
- sphinx/search/ja.py +8 -6
- sphinx/search/nl.py +1 -1
- sphinx/search/zh.py +19 -21
- sphinx/testing/fixtures.py +26 -29
- sphinx/testing/path.py +26 -62
- sphinx/testing/restructuredtext.py +14 -8
- sphinx/testing/util.py +21 -19
- sphinx/texinputs/make.bat.jinja +50 -50
- sphinx/texinputs/sphinx.sty +4 -3
- sphinx/texinputs/sphinxlatexadmonitions.sty +1 -1
- sphinx/texinputs/sphinxlatexobjects.sty +29 -10
- sphinx/themes/basic/static/searchtools.js +8 -5
- sphinx/theming.py +49 -61
- sphinx/transforms/__init__.py +17 -38
- sphinx/transforms/compact_bullet_list.py +5 -3
- sphinx/transforms/i18n.py +8 -21
- sphinx/transforms/post_transforms/__init__.py +142 -93
- sphinx/transforms/post_transforms/code.py +5 -5
- sphinx/transforms/post_transforms/images.py +28 -24
- sphinx/transforms/references.py +3 -1
- sphinx/util/__init__.py +109 -60
- sphinx/util/_files.py +39 -23
- sphinx/util/_importer.py +4 -1
- sphinx/util/_inventory_file_reader.py +76 -0
- sphinx/util/_io.py +2 -2
- sphinx/util/_lines.py +6 -3
- sphinx/util/_pathlib.py +40 -2
- sphinx/util/build_phase.py +2 -0
- sphinx/util/cfamily.py +19 -14
- sphinx/util/console.py +44 -179
- sphinx/util/display.py +9 -10
- sphinx/util/docfields.py +140 -122
- sphinx/util/docstrings.py +1 -1
- sphinx/util/docutils.py +118 -77
- sphinx/util/fileutil.py +25 -26
- sphinx/util/http_date.py +2 -0
- sphinx/util/i18n.py +77 -64
- sphinx/util/images.py +8 -6
- sphinx/util/inspect.py +147 -38
- sphinx/util/inventory.py +215 -116
- sphinx/util/logging.py +33 -33
- sphinx/util/matching.py +12 -4
- sphinx/util/nodes.py +18 -13
- sphinx/util/osutil.py +38 -39
- sphinx/util/parallel.py +22 -13
- sphinx/util/parsing.py +2 -1
- sphinx/util/png.py +6 -2
- sphinx/util/requests.py +33 -2
- sphinx/util/rst.py +3 -2
- sphinx/util/tags.py +1 -1
- sphinx/util/template.py +18 -10
- sphinx/util/texescape.py +8 -6
- sphinx/util/typing.py +148 -122
- sphinx/versioning.py +3 -3
- sphinx/writers/html.py +3 -1
- sphinx/writers/html5.py +63 -52
- sphinx/writers/latex.py +83 -67
- sphinx/writers/manpage.py +19 -38
- sphinx/writers/texinfo.py +47 -47
- sphinx/writers/text.py +50 -32
- sphinx/writers/xml.py +11 -8
- {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/LICENSE.rst +1 -1
- {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/METADATA +25 -15
- sphinx-8.2.0.dist-info/RECORD +606 -0
- {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/WHEEL +1 -1
- sphinx/builders/html/transforms.py +0 -90
- sphinx/ext/apidoc.py +0 -721
- sphinx/util/exceptions.py +0 -74
- sphinx-8.1.3.dist-info/RECORD +0 -598
- {sphinx-8.1.3.dist-info → sphinx-8.2.0.dist-info}/entry_points.txt +0 -0
sphinx/util/inventory.py
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import posixpath
|
|
6
6
|
import re
|
|
7
|
+
import warnings
|
|
7
8
|
import zlib
|
|
8
9
|
from typing import TYPE_CHECKING
|
|
9
10
|
|
|
11
|
+
from sphinx.deprecation import RemovedInSphinx10Warning
|
|
10
12
|
from sphinx.locale import __
|
|
11
13
|
from sphinx.util import logging
|
|
12
14
|
|
|
@@ -14,127 +16,101 @@ BUFSIZE = 16 * 1024
|
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
|
-
|
|
19
|
+
import os
|
|
20
|
+
from collections.abc import Callable, Iterator, Sequence
|
|
21
|
+
from typing import NoReturn, Protocol
|
|
18
22
|
|
|
19
23
|
from sphinx.builders import Builder
|
|
20
24
|
from sphinx.environment import BuildEnvironment
|
|
21
|
-
from sphinx.util.typing import Inventory
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if chunk == b'':
|
|
38
|
-
self.eof = True
|
|
39
|
-
self.buffer += chunk
|
|
40
|
-
|
|
41
|
-
def readline(self) -> str:
|
|
42
|
-
pos = self.buffer.find(b'\n')
|
|
43
|
-
if pos != -1:
|
|
44
|
-
line = self.buffer[:pos].decode()
|
|
45
|
-
self.buffer = self.buffer[pos + 1 :]
|
|
46
|
-
elif self.eof:
|
|
47
|
-
line = self.buffer.decode()
|
|
48
|
-
self.buffer = b''
|
|
49
|
-
else:
|
|
50
|
-
self.read_buffer()
|
|
51
|
-
line = self.readline()
|
|
52
|
-
|
|
53
|
-
return line
|
|
54
|
-
|
|
55
|
-
def readlines(self) -> Iterator[str]:
|
|
56
|
-
while not self.eof:
|
|
57
|
-
line = self.readline()
|
|
58
|
-
if line:
|
|
59
|
-
yield line
|
|
60
|
-
|
|
61
|
-
def read_compressed_chunks(self) -> Iterator[bytes]:
|
|
62
|
-
decompressor = zlib.decompressobj()
|
|
63
|
-
while not self.eof:
|
|
64
|
-
self.read_buffer()
|
|
65
|
-
yield decompressor.decompress(self.buffer)
|
|
66
|
-
self.buffer = b''
|
|
67
|
-
yield decompressor.flush()
|
|
68
|
-
|
|
69
|
-
def read_compressed_lines(self) -> Iterator[str]:
|
|
70
|
-
buf = b''
|
|
71
|
-
for chunk in self.read_compressed_chunks():
|
|
72
|
-
buf += chunk
|
|
73
|
-
pos = buf.find(b'\n')
|
|
74
|
-
while pos != -1:
|
|
75
|
-
yield buf[:pos].decode()
|
|
76
|
-
buf = buf[pos + 1 :]
|
|
77
|
-
pos = buf.find(b'\n')
|
|
25
|
+
from sphinx.util.typing import Inventory
|
|
26
|
+
|
|
27
|
+
# Readable file stream for inventory loading
|
|
28
|
+
class _SupportsRead(Protocol):
|
|
29
|
+
def read(self, size: int = ...) -> bytes: ...
|
|
30
|
+
|
|
31
|
+
_JoinFunc = Callable[[str, str], str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __getattr__(name: str) -> object:
|
|
35
|
+
if name == 'InventoryFileReader':
|
|
36
|
+
from sphinx.util._inventory_file_reader import InventoryFileReader
|
|
37
|
+
|
|
38
|
+
return InventoryFileReader
|
|
39
|
+
msg = f'module {__name__!r} has no attribute {name!r}'
|
|
40
|
+
raise AttributeError(msg)
|
|
78
41
|
|
|
79
42
|
|
|
80
43
|
class InventoryFile:
|
|
81
44
|
@classmethod
|
|
82
|
-
def
|
|
83
|
-
cls
|
|
84
|
-
|
|
45
|
+
def loads(
|
|
46
|
+
cls,
|
|
47
|
+
content: bytes,
|
|
48
|
+
*,
|
|
85
49
|
uri: str,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return cls.
|
|
94
|
-
|
|
95
|
-
|
|
50
|
+
) -> _Inventory:
|
|
51
|
+
format_line, _, content = content.partition(b'\n')
|
|
52
|
+
format_line = format_line.rstrip() # remove trailing \r or spaces
|
|
53
|
+
if format_line == b'# Sphinx inventory version 2':
|
|
54
|
+
return cls._loads_v2(content, uri=uri)
|
|
55
|
+
if format_line == b'# Sphinx inventory version 1':
|
|
56
|
+
lines = content.decode().splitlines()
|
|
57
|
+
return cls._loads_v1(lines, uri=uri)
|
|
58
|
+
if format_line.startswith(b'# Sphinx inventory version '):
|
|
59
|
+
unknown_version = format_line[27:].decode()
|
|
60
|
+
msg = f'unknown or unsupported inventory version: {unknown_version!r}'
|
|
61
|
+
raise ValueError(msg)
|
|
62
|
+
msg = f'invalid inventory header: {format_line.decode()}'
|
|
63
|
+
raise ValueError(msg)
|
|
96
64
|
|
|
97
65
|
@classmethod
|
|
98
|
-
def
|
|
99
|
-
cls
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
66
|
+
def load(cls, stream: _SupportsRead, uri: str, joinfunc: _JoinFunc) -> Inventory:
|
|
67
|
+
return cls.loads(stream.read(), uri=uri).data
|
|
68
|
+
|
|
69
|
+
@classmethod
|
|
70
|
+
def _loads_v1(cls, lines: Sequence[str], *, uri: str) -> _Inventory:
|
|
71
|
+
if len(lines) < 2:
|
|
72
|
+
msg = 'invalid inventory header: missing project name or version'
|
|
73
|
+
raise ValueError(msg)
|
|
74
|
+
inv = _Inventory({})
|
|
75
|
+
projname = lines[0].rstrip()[11:] # Project name
|
|
76
|
+
version = lines[1].rstrip()[11:] # Project version
|
|
77
|
+
for line in lines[2:]:
|
|
78
|
+
name, item_type, location = line.rstrip().split(None, 2)
|
|
79
|
+
location = posixpath.join(uri, location)
|
|
110
80
|
# version 1 did not add anchors to the location
|
|
111
|
-
if
|
|
112
|
-
|
|
113
|
-
location += '#module-'
|
|
81
|
+
if item_type == 'mod':
|
|
82
|
+
item_type = 'py:module'
|
|
83
|
+
location += f'#module-{name}'
|
|
114
84
|
else:
|
|
115
|
-
|
|
116
|
-
location += '#'
|
|
117
|
-
|
|
118
|
-
|
|
85
|
+
item_type = f'py:{item_type}'
|
|
86
|
+
location += f'#{name}'
|
|
87
|
+
inv[item_type, name] = _InventoryItem(
|
|
88
|
+
project_name=projname,
|
|
89
|
+
project_version=version,
|
|
90
|
+
uri=location,
|
|
91
|
+
display_name='-',
|
|
92
|
+
)
|
|
93
|
+
return inv
|
|
119
94
|
|
|
120
95
|
@classmethod
|
|
121
|
-
def
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
projname =
|
|
129
|
-
version =
|
|
130
|
-
# definition -> priority, location, display name
|
|
96
|
+
def _loads_v2(cls, inv_data: bytes, *, uri: str) -> _Inventory:
|
|
97
|
+
try:
|
|
98
|
+
line_1, line_2, check_line, compressed = inv_data.split(b'\n', maxsplit=3)
|
|
99
|
+
except ValueError:
|
|
100
|
+
msg = 'invalid inventory header: missing project name or version'
|
|
101
|
+
raise ValueError(msg) from None
|
|
102
|
+
inv = _Inventory({})
|
|
103
|
+
projname = line_1.rstrip()[11:].decode() # Project name
|
|
104
|
+
version = line_2.rstrip()[11:].decode() # Project version
|
|
105
|
+
# definition -> (priority, location, display name)
|
|
131
106
|
potential_ambiguities: dict[str, tuple[str, str, str]] = {}
|
|
132
107
|
actual_ambiguities = set()
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
raise ValueError(
|
|
108
|
+
if b'zlib' not in check_line: # '... compressed using zlib'
|
|
109
|
+
msg = f'invalid inventory header (not compressed): {check_line.decode()}'
|
|
110
|
+
raise ValueError(msg)
|
|
136
111
|
|
|
137
|
-
|
|
112
|
+
decompressed_content = zlib.decompress(compressed)
|
|
113
|
+
for line in decompressed_content.decode().splitlines():
|
|
138
114
|
# be careful to handle names with embedded spaces correctly
|
|
139
115
|
m = re.match(
|
|
140
116
|
r'(.+?)\s+(\S+)\s+(-?\d+)\s+?(\S*)\s+(.*)',
|
|
@@ -147,9 +123,10 @@ class InventoryFile:
|
|
|
147
123
|
if ':' not in type:
|
|
148
124
|
# wrong type value. type should be in the form of "{domain}:{objtype}"
|
|
149
125
|
#
|
|
150
|
-
# Note: To avoid the regex DoS, this is implemented in
|
|
126
|
+
# Note: To avoid the regex DoS, this is implemented in Python
|
|
127
|
+
# See: https://github.com/sphinx-doc/sphinx/issues/8175
|
|
151
128
|
continue
|
|
152
|
-
if type == 'py:module' and type
|
|
129
|
+
if type == 'py:module' and (type, name) in inv:
|
|
153
130
|
# due to a bug in 1.1 and below,
|
|
154
131
|
# two inventory entries are created
|
|
155
132
|
# for Python modules, and the first
|
|
@@ -177,9 +154,13 @@ class InventoryFile:
|
|
|
177
154
|
potential_ambiguities[lowercase_definition] = content
|
|
178
155
|
if location.endswith('$'):
|
|
179
156
|
location = location[:-1] + name
|
|
180
|
-
location = join(uri, location)
|
|
181
|
-
|
|
182
|
-
|
|
157
|
+
location = posixpath.join(uri, location)
|
|
158
|
+
inv[type, name] = _InventoryItem(
|
|
159
|
+
project_name=projname,
|
|
160
|
+
project_version=version,
|
|
161
|
+
uri=location,
|
|
162
|
+
display_name=dispname,
|
|
163
|
+
)
|
|
183
164
|
for ambiguity in actual_ambiguities:
|
|
184
165
|
logger.info(
|
|
185
166
|
__('inventory <%s> contains multiple definitions for %s'),
|
|
@@ -188,19 +169,16 @@ class InventoryFile:
|
|
|
188
169
|
type='intersphinx',
|
|
189
170
|
subtype='external',
|
|
190
171
|
)
|
|
191
|
-
return
|
|
172
|
+
return inv
|
|
192
173
|
|
|
193
174
|
@classmethod
|
|
194
175
|
def dump(
|
|
195
|
-
cls:
|
|
196
|
-
filename: str,
|
|
197
|
-
env: BuildEnvironment,
|
|
198
|
-
builder: Builder,
|
|
176
|
+
cls, filename: str | os.PathLike[str], env: BuildEnvironment, builder: Builder
|
|
199
177
|
) -> None:
|
|
200
178
|
def escape(string: str) -> str:
|
|
201
179
|
return re.sub('\\s+', ' ', string)
|
|
202
180
|
|
|
203
|
-
with open(
|
|
181
|
+
with open(filename, 'wb') as f:
|
|
204
182
|
# header
|
|
205
183
|
f.write(
|
|
206
184
|
(
|
|
@@ -227,3 +205,124 @@ class InventoryFile:
|
|
|
227
205
|
entry = f'{fullname} {domain.name}:{type} {prio} {uri} {dispname}\n'
|
|
228
206
|
f.write(compressor.compress(entry.encode()))
|
|
229
207
|
f.write(compressor.flush())
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class _Inventory:
|
|
211
|
+
"""Inventory data in memory."""
|
|
212
|
+
|
|
213
|
+
__slots__ = ('data',)
|
|
214
|
+
|
|
215
|
+
data: dict[str, dict[str, _InventoryItem]]
|
|
216
|
+
|
|
217
|
+
def __init__(self, data: dict[str, dict[str, _InventoryItem]], /) -> None:
|
|
218
|
+
# type -> name -> _InventoryItem
|
|
219
|
+
self.data: dict[str, dict[str, _InventoryItem]] = data
|
|
220
|
+
|
|
221
|
+
def __repr__(self) -> str:
|
|
222
|
+
return f'_Inventory({self.data!r})'
|
|
223
|
+
|
|
224
|
+
def __eq__(self, other: object) -> bool:
|
|
225
|
+
if not isinstance(other, _Inventory):
|
|
226
|
+
return NotImplemented
|
|
227
|
+
return self.data == other.data
|
|
228
|
+
|
|
229
|
+
def __hash__(self) -> int:
|
|
230
|
+
return hash(self.data)
|
|
231
|
+
|
|
232
|
+
def __getitem__(self, item: tuple[str, str]) -> _InventoryItem:
|
|
233
|
+
obj_type, name = item
|
|
234
|
+
return self.data.setdefault(obj_type, {})[name]
|
|
235
|
+
|
|
236
|
+
def __setitem__(self, item: tuple[str, str], value: _InventoryItem) -> None:
|
|
237
|
+
obj_type, name = item
|
|
238
|
+
self.data.setdefault(obj_type, {})[name] = value
|
|
239
|
+
|
|
240
|
+
def __contains__(self, item: tuple[str, str]) -> bool:
|
|
241
|
+
obj_type, name = item
|
|
242
|
+
return obj_type in self.data and name in self.data[obj_type]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class _InventoryItem:
|
|
246
|
+
__slots__ = 'project_name', 'project_version', 'uri', 'display_name'
|
|
247
|
+
|
|
248
|
+
project_name: str
|
|
249
|
+
project_version: str
|
|
250
|
+
uri: str
|
|
251
|
+
display_name: str
|
|
252
|
+
|
|
253
|
+
def __init__(
|
|
254
|
+
self,
|
|
255
|
+
*,
|
|
256
|
+
project_name: str,
|
|
257
|
+
project_version: str,
|
|
258
|
+
uri: str,
|
|
259
|
+
display_name: str,
|
|
260
|
+
) -> None:
|
|
261
|
+
object.__setattr__(self, 'project_name', project_name)
|
|
262
|
+
object.__setattr__(self, 'project_version', project_version)
|
|
263
|
+
object.__setattr__(self, 'uri', uri)
|
|
264
|
+
object.__setattr__(self, 'display_name', display_name)
|
|
265
|
+
|
|
266
|
+
def __repr__(self) -> str:
|
|
267
|
+
return (
|
|
268
|
+
'_InventoryItem('
|
|
269
|
+
f'project_name={self.project_name!r}, '
|
|
270
|
+
f'project_version={self.project_version!r}, '
|
|
271
|
+
f'uri={self.uri!r}, '
|
|
272
|
+
f'display_name={self.display_name!r}'
|
|
273
|
+
')'
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def __eq__(self, other: object) -> bool:
|
|
277
|
+
if not isinstance(other, _InventoryItem):
|
|
278
|
+
return NotImplemented
|
|
279
|
+
return (
|
|
280
|
+
self.project_name == other.project_name
|
|
281
|
+
and self.project_version == other.project_version
|
|
282
|
+
and self.uri == other.uri
|
|
283
|
+
and self.display_name == other.display_name
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def __hash__(self) -> int:
|
|
287
|
+
return hash((
|
|
288
|
+
self.project_name,
|
|
289
|
+
self.project_version,
|
|
290
|
+
self.uri,
|
|
291
|
+
self.display_name,
|
|
292
|
+
))
|
|
293
|
+
|
|
294
|
+
def __setattr__(self, key: str, value: object) -> NoReturn:
|
|
295
|
+
msg = '_InventoryItem is immutable'
|
|
296
|
+
raise AttributeError(msg)
|
|
297
|
+
|
|
298
|
+
def __delattr__(self, key: str) -> NoReturn:
|
|
299
|
+
msg = '_InventoryItem is immutable'
|
|
300
|
+
raise AttributeError(msg)
|
|
301
|
+
|
|
302
|
+
def __getstate__(self) -> tuple[str, str, str, str]:
|
|
303
|
+
return self.project_name, self.project_version, self.uri, self.display_name
|
|
304
|
+
|
|
305
|
+
def __setstate__(self, state: tuple[str, str, str, str]) -> None:
|
|
306
|
+
project_name, project_version, uri, display_name = state
|
|
307
|
+
object.__setattr__(self, 'project_name', project_name)
|
|
308
|
+
object.__setattr__(self, 'project_version', project_version)
|
|
309
|
+
object.__setattr__(self, 'uri', uri)
|
|
310
|
+
object.__setattr__(self, 'display_name', display_name)
|
|
311
|
+
|
|
312
|
+
def __getitem__(self, key: int | slice) -> str | tuple[str, ...]:
|
|
313
|
+
warnings.warn(
|
|
314
|
+
'The tuple interface for _InventoryItem objects is deprecated.',
|
|
315
|
+
RemovedInSphinx10Warning,
|
|
316
|
+
stacklevel=2,
|
|
317
|
+
)
|
|
318
|
+
tpl = self.project_name, self.project_version, self.uri, self.display_name
|
|
319
|
+
return tpl[key]
|
|
320
|
+
|
|
321
|
+
def __iter__(self) -> Iterator[str]:
|
|
322
|
+
warnings.warn(
|
|
323
|
+
'The iter() interface for _InventoryItem objects is deprecated.',
|
|
324
|
+
RemovedInSphinx10Warning,
|
|
325
|
+
stacklevel=2,
|
|
326
|
+
)
|
|
327
|
+
tpl = self.project_name, self.project_version, self.uri, self.display_name
|
|
328
|
+
return iter(tpl)
|
sphinx/util/logging.py
CHANGED
|
@@ -4,20 +4,20 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import logging.handlers
|
|
7
|
+
import os.path
|
|
7
8
|
from collections import defaultdict
|
|
8
9
|
from contextlib import contextmanager, nullcontext
|
|
9
|
-
from typing import
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
10
11
|
|
|
11
12
|
from docutils import nodes
|
|
12
13
|
from docutils.utils import get_source_line
|
|
13
14
|
|
|
15
|
+
from sphinx._cli.util.colour import colourise
|
|
14
16
|
from sphinx.errors import SphinxWarning
|
|
15
|
-
from sphinx.util.console import colorize
|
|
16
|
-
from sphinx.util.osutil import abspath
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from collections.abc import Iterator, Sequence, Set
|
|
20
|
-
from typing import NoReturn
|
|
19
|
+
from collections.abc import Iterator, Mapping, Sequence, Set
|
|
20
|
+
from typing import IO, Any, NoReturn
|
|
21
21
|
|
|
22
22
|
from docutils.nodes import Node
|
|
23
23
|
|
|
@@ -49,14 +49,11 @@ VERBOSITY_MAP: defaultdict[int, int] = defaultdict(
|
|
|
49
49
|
},
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
COLOR_MAP:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
logging.DEBUG: 'darkgray',
|
|
58
|
-
},
|
|
59
|
-
)
|
|
52
|
+
COLOR_MAP: dict[int, str] = {
|
|
53
|
+
logging.ERROR: 'darkred',
|
|
54
|
+
logging.WARNING: 'red',
|
|
55
|
+
logging.DEBUG: 'darkgray',
|
|
56
|
+
}
|
|
60
57
|
|
|
61
58
|
|
|
62
59
|
def getLogger(name: str) -> SphinxLoggerAdapter:
|
|
@@ -129,7 +126,7 @@ class SphinxWarningLogRecord(SphinxLogRecord):
|
|
|
129
126
|
return 'WARNING: '
|
|
130
127
|
|
|
131
128
|
|
|
132
|
-
class SphinxLoggerAdapter(logging.LoggerAdapter):
|
|
129
|
+
class SphinxLoggerAdapter(logging.LoggerAdapter[logging.Logger]):
|
|
133
130
|
"""LoggerAdapter allowing ``type`` and ``subtype`` keywords."""
|
|
134
131
|
|
|
135
132
|
KEYWORDS = ['type', 'subtype', 'location', 'nonl', 'color', 'once']
|
|
@@ -146,7 +143,7 @@ class SphinxLoggerAdapter(logging.LoggerAdapter):
|
|
|
146
143
|
def verbose(self, msg: str, *args: Any, **kwargs: Any) -> None:
|
|
147
144
|
self.log(VERBOSE, msg, *args, **kwargs)
|
|
148
145
|
|
|
149
|
-
def process(self, msg: str, kwargs: dict) -> tuple[str, dict]: # type: ignore[override]
|
|
146
|
+
def process(self, msg: str, kwargs: dict[str, Any]) -> tuple[str, dict[str, Any]]: # type: ignore[override]
|
|
150
147
|
extra = kwargs.setdefault('extra', {})
|
|
151
148
|
for keyword in self.KEYWORDS:
|
|
152
149
|
if keyword in kwargs:
|
|
@@ -161,9 +158,9 @@ class SphinxLoggerAdapter(logging.LoggerAdapter):
|
|
|
161
158
|
self,
|
|
162
159
|
msg: object,
|
|
163
160
|
*args: object,
|
|
164
|
-
type:
|
|
165
|
-
subtype:
|
|
166
|
-
location:
|
|
161
|
+
type: str | None = None,
|
|
162
|
+
subtype: str | None = None,
|
|
163
|
+
location: str | tuple[str | None, int | None] | Node | None = None,
|
|
167
164
|
nonl: bool = True,
|
|
168
165
|
color: str | None = None,
|
|
169
166
|
once: bool = False,
|
|
@@ -204,13 +201,13 @@ class SphinxLoggerAdapter(logging.LoggerAdapter):
|
|
|
204
201
|
)
|
|
205
202
|
|
|
206
203
|
|
|
207
|
-
class WarningStreamHandler(logging.StreamHandler):
|
|
204
|
+
class WarningStreamHandler(logging.StreamHandler['SafeEncodingWriter']):
|
|
208
205
|
"""StreamHandler for warnings."""
|
|
209
206
|
|
|
210
207
|
pass
|
|
211
208
|
|
|
212
209
|
|
|
213
|
-
class NewLineStreamHandler(logging.StreamHandler):
|
|
210
|
+
class NewLineStreamHandler(logging.StreamHandler['SafeEncodingWriter']):
|
|
214
211
|
"""StreamHandler which switches line terminator by record.nonl flag."""
|
|
215
212
|
|
|
216
213
|
def emit(self, record: logging.LogRecord) -> None:
|
|
@@ -471,7 +468,9 @@ class OnceFilter(logging.Filter):
|
|
|
471
468
|
|
|
472
469
|
def __init__(self, name: str = '') -> None:
|
|
473
470
|
super().__init__(name)
|
|
474
|
-
self.messages: dict[
|
|
471
|
+
self.messages: dict[
|
|
472
|
+
str, list[tuple[object, ...] | Mapping[str, object] | None]
|
|
473
|
+
] = {}
|
|
475
474
|
|
|
476
475
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
477
476
|
once = getattr(record, 'once', '')
|
|
@@ -555,9 +554,9 @@ class WarningLogRecordTranslator(SphinxLogRecordTranslator):
|
|
|
555
554
|
def get_node_location(node: Node) -> str | None:
|
|
556
555
|
source, line = get_source_line(node)
|
|
557
556
|
if source and line:
|
|
558
|
-
return f'{abspath(source)}:{line}'
|
|
557
|
+
return f'{os.path.abspath(source)}:{line}'
|
|
559
558
|
if source:
|
|
560
|
-
return f'{abspath(source)}:'
|
|
559
|
+
return f'{os.path.abspath(source)}:'
|
|
561
560
|
if line:
|
|
562
561
|
return f'<unknown>:{line}'
|
|
563
562
|
return None
|
|
@@ -566,20 +565,21 @@ def get_node_location(node: Node) -> str | None:
|
|
|
566
565
|
class ColorizeFormatter(logging.Formatter):
|
|
567
566
|
def format(self, record: logging.LogRecord) -> str:
|
|
568
567
|
message = super().format(record)
|
|
569
|
-
|
|
570
|
-
if
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
568
|
+
colour_name = getattr(record, 'color', '')
|
|
569
|
+
if not colour_name:
|
|
570
|
+
colour_name = COLOR_MAP.get(record.levelno, '')
|
|
571
|
+
if not colour_name:
|
|
572
|
+
return message
|
|
573
|
+
try:
|
|
574
|
+
return colourise(colour_name, message)
|
|
575
|
+
except ValueError:
|
|
576
576
|
return message
|
|
577
577
|
|
|
578
578
|
|
|
579
579
|
class SafeEncodingWriter:
|
|
580
580
|
"""Stream writer which ignores UnicodeEncodeError silently"""
|
|
581
581
|
|
|
582
|
-
def __init__(self, stream: IO) -> None:
|
|
582
|
+
def __init__(self, stream: IO[str]) -> None:
|
|
583
583
|
self.stream = stream
|
|
584
584
|
self.encoding = getattr(stream, 'encoding', 'ascii') or 'ascii'
|
|
585
585
|
|
|
@@ -601,14 +601,14 @@ class SafeEncodingWriter:
|
|
|
601
601
|
class LastMessagesWriter:
|
|
602
602
|
"""Stream writer storing last 10 messages in memory to save trackback"""
|
|
603
603
|
|
|
604
|
-
def __init__(self, app: Sphinx, stream: IO) -> None:
|
|
604
|
+
def __init__(self, app: Sphinx, stream: IO[str]) -> None:
|
|
605
605
|
self.app = app
|
|
606
606
|
|
|
607
607
|
def write(self, data: str) -> None:
|
|
608
608
|
self.app.messagelog.append(data)
|
|
609
609
|
|
|
610
610
|
|
|
611
|
-
def setup(app: Sphinx, status: IO, warning: IO) -> None:
|
|
611
|
+
def setup(app: Sphinx, status: IO[str], warning: IO[str]) -> None:
|
|
612
612
|
"""Setup root logger for Sphinx"""
|
|
613
613
|
logger = logging.getLogger(NAMESPACE)
|
|
614
614
|
logger.setLevel(logging.DEBUG)
|
sphinx/util/matching.py
CHANGED
|
@@ -4,9 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os.path
|
|
6
6
|
import re
|
|
7
|
+
import unicodedata
|
|
8
|
+
from pathlib import Path
|
|
7
9
|
from typing import TYPE_CHECKING
|
|
8
10
|
|
|
9
|
-
from sphinx.util.osutil import canon_path
|
|
11
|
+
from sphinx.util.osutil import canon_path
|
|
10
12
|
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from collections.abc import Callable, Iterable, Iterator
|
|
@@ -125,7 +127,7 @@ def get_matching_files(
|
|
|
125
127
|
|
|
126
128
|
"""
|
|
127
129
|
# dirname is a normalized absolute path.
|
|
128
|
-
dirname =
|
|
130
|
+
dirname = Path(dirname).resolve()
|
|
129
131
|
|
|
130
132
|
exclude_matchers = compile_matchers(exclude_patterns)
|
|
131
133
|
include_matchers = compile_matchers(include_patterns)
|
|
@@ -134,11 +136,12 @@ def get_matching_files(
|
|
|
134
136
|
relative_root = os.path.relpath(root, dirname)
|
|
135
137
|
if relative_root == '.':
|
|
136
138
|
relative_root = '' # suppress dirname for files on the target dir
|
|
139
|
+
relative_root_path = Path(relative_root)
|
|
137
140
|
|
|
138
141
|
# Filter files
|
|
139
142
|
included_files = []
|
|
140
143
|
for entry in sorted(files):
|
|
141
|
-
entry =
|
|
144
|
+
entry = _unicode_nfc((relative_root_path / entry).as_posix())
|
|
142
145
|
keep = False
|
|
143
146
|
for matcher in include_matchers:
|
|
144
147
|
if matcher(entry):
|
|
@@ -156,7 +159,7 @@ def get_matching_files(
|
|
|
156
159
|
# Filter directories
|
|
157
160
|
filtered_dirs = []
|
|
158
161
|
for dir_name in sorted(dirs):
|
|
159
|
-
normalised =
|
|
162
|
+
normalised = _unicode_nfc((relative_root_path / dir_name).as_posix())
|
|
160
163
|
for matcher in exclude_matchers:
|
|
161
164
|
if matcher(normalised):
|
|
162
165
|
break # break the inner loop
|
|
@@ -168,3 +171,8 @@ def get_matching_files(
|
|
|
168
171
|
|
|
169
172
|
# Yield filtered files
|
|
170
173
|
yield from included_files
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _unicode_nfc(s: str, /) -> str:
|
|
177
|
+
"""Normalise the string to NFC form."""
|
|
178
|
+
return unicodedata.normalize('NFC', s)
|