Sphinx 7.4.7__py3-none-any.whl → 8.0.0rc1__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 +2 -2
- sphinx/_cli/__init__.py +4 -4
- sphinx/application.py +7 -7
- sphinx/builders/__init__.py +2 -3
- sphinx/builders/_epub_base.py +33 -12
- sphinx/builders/changes.py +13 -5
- sphinx/builders/epub3.py +6 -2
- sphinx/builders/html/__init__.py +88 -58
- sphinx/builders/latex/__init__.py +38 -12
- sphinx/builders/latex/transforms.py +1 -1
- sphinx/builders/linkcheck.py +8 -49
- sphinx/builders/texinfo.py +12 -6
- sphinx/builders/text.py +7 -3
- sphinx/builders/xml.py +7 -3
- sphinx/cmd/quickstart.py +10 -20
- sphinx/config.py +12 -12
- sphinx/deprecation.py +8 -8
- sphinx/directives/other.py +2 -3
- sphinx/directives/patches.py +2 -2
- sphinx/domains/__init__.py +4 -2
- sphinx/domains/c/__init__.py +2 -2
- sphinx/domains/c/_ast.py +3 -2
- sphinx/domains/c/_parser.py +4 -3
- sphinx/domains/cpp/__init__.py +2 -2
- sphinx/domains/cpp/_ast.py +1 -2
- sphinx/domains/cpp/_parser.py +2 -2
- sphinx/domains/cpp/_symbol.py +2 -2
- sphinx/domains/math.py +1 -1
- sphinx/domains/python/_object.py +0 -1
- sphinx/domains/std/__init__.py +7 -8
- sphinx/environment/__init__.py +14 -32
- sphinx/environment/adapters/indexentries.py +4 -6
- sphinx/environment/adapters/toctree.py +4 -4
- sphinx/environment/collectors/title.py +1 -1
- sphinx/environment/collectors/toctree.py +1 -1
- sphinx/events.py +3 -1
- sphinx/ext/autodoc/__init__.py +17 -63
- sphinx/ext/autodoc/directive.py +7 -5
- sphinx/ext/autodoc/importer.py +2 -1
- sphinx/ext/autodoc/preserve_defaults.py +2 -2
- sphinx/ext/autosummary/__init__.py +7 -6
- sphinx/ext/autosummary/generate.py +5 -4
- sphinx/ext/doctest.py +5 -5
- sphinx/ext/graphviz.py +1 -1
- sphinx/ext/imgmath.py +1 -1
- sphinx/ext/inheritance_diagram.py +1 -1
- sphinx/ext/intersphinx/__init__.py +25 -5
- sphinx/ext/intersphinx/_cli.py +7 -6
- sphinx/ext/intersphinx/_load.py +240 -115
- sphinx/ext/intersphinx/_resolve.py +12 -11
- sphinx/ext/intersphinx/_shared.py +102 -9
- sphinx/ext/mathjax.py +1 -1
- sphinx/ext/napoleon/docstring.py +2 -2
- sphinx/ext/todo.py +2 -2
- sphinx/ext/viewcode.py +2 -1
- sphinx/highlighting.py +3 -3
- sphinx/io.py +2 -2
- sphinx/jinja2glue.py +13 -6
- sphinx/locale/__init__.py +4 -3
- sphinx/project.py +23 -19
- sphinx/pycode/ast.py +2 -2
- sphinx/pycode/parser.py +2 -2
- sphinx/pygments_styles.py +3 -3
- sphinx/registry.py +3 -8
- sphinx/search/__init__.py +1 -1
- sphinx/testing/path.py +2 -1
- sphinx/testing/util.py +1 -1
- sphinx/texinputs/Makefile.jinja +2 -1
- sphinx/texinputs_win/Makefile.jinja +2 -1
- sphinx/theming.py +3 -12
- sphinx/transforms/__init__.py +5 -5
- sphinx/transforms/references.py +1 -1
- sphinx/util/__init__.py +11 -35
- sphinx/util/_timestamps.py +12 -0
- sphinx/util/cfamily.py +5 -5
- sphinx/util/console.py +4 -3
- sphinx/util/display.py +3 -3
- sphinx/util/docfields.py +1 -1
- sphinx/util/docutils.py +44 -10
- sphinx/util/fileutil.py +25 -20
- sphinx/util/i18n.py +9 -4
- sphinx/util/images.py +3 -2
- sphinx/util/inspect.py +28 -43
- sphinx/util/inventory.py +2 -2
- sphinx/util/matching.py +2 -2
- sphinx/util/math.py +1 -1
- sphinx/util/nodes.py +8 -8
- sphinx/util/osutil.py +29 -28
- sphinx/util/parallel.py +2 -2
- sphinx/util/requests.py +1 -1
- sphinx/util/template.py +3 -3
- sphinx/util/typing.py +36 -72
- sphinx/writers/html.py +1 -1
- sphinx/writers/html5.py +1 -1
- sphinx/writers/latex.py +4 -4
- sphinx/writers/manpage.py +2 -2
- sphinx/writers/texinfo.py +5 -5
- sphinx/writers/text.py +4 -4
- sphinx/writers/xml.py +2 -2
- {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/METADATA +10 -9
- {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/RECORD +104 -106
- sphinx/templates/quickstart/Makefile.jinja +0 -98
- sphinx/templates/quickstart/make.bat.jinja +0 -110
- sphinx/util/_pathlib.py +0 -120
- {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/LICENSE.rst +0 -0
- {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/WHEEL +0 -0
- {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -28,10 +28,11 @@ if TYPE_CHECKING:
|
|
|
28
28
|
from sphinx.application import Sphinx
|
|
29
29
|
from sphinx.domains import Domain
|
|
30
30
|
from sphinx.environment import BuildEnvironment
|
|
31
|
+
from sphinx.ext.intersphinx._shared import InventoryName
|
|
31
32
|
from sphinx.util.typing import Inventory, InventoryItem, RoleFunction
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
def _create_element_from_result(domain: Domain, inv_name:
|
|
35
|
+
def _create_element_from_result(domain: Domain, inv_name: InventoryName | None,
|
|
35
36
|
data: InventoryItem,
|
|
36
37
|
node: pending_xref, contnode: TextElement) -> nodes.reference:
|
|
37
38
|
proj, version, uri, dispname = data
|
|
@@ -61,7 +62,7 @@ def _create_element_from_result(domain: Domain, inv_name: str | None,
|
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
def _resolve_reference_in_domain_by_target(
|
|
64
|
-
inv_name:
|
|
65
|
+
inv_name: InventoryName | None, inventory: Inventory,
|
|
65
66
|
domain: Domain, objtypes: Iterable[str],
|
|
66
67
|
target: str,
|
|
67
68
|
node: pending_xref, contnode: TextElement) -> nodes.reference | None:
|
|
@@ -100,7 +101,7 @@ def _resolve_reference_in_domain_by_target(
|
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
def _resolve_reference_in_domain(env: BuildEnvironment,
|
|
103
|
-
inv_name:
|
|
104
|
+
inv_name: InventoryName | None, inventory: Inventory,
|
|
104
105
|
honor_disabled_refs: bool,
|
|
105
106
|
domain: Domain, objtypes: Iterable[str],
|
|
106
107
|
node: pending_xref, contnode: TextElement,
|
|
@@ -142,20 +143,21 @@ def _resolve_reference_in_domain(env: BuildEnvironment,
|
|
|
142
143
|
full_qualified_name, node, contnode)
|
|
143
144
|
|
|
144
145
|
|
|
145
|
-
def _resolve_reference(env: BuildEnvironment,
|
|
146
|
+
def _resolve_reference(env: BuildEnvironment,
|
|
147
|
+
inv_name: InventoryName | None, inventory: Inventory,
|
|
146
148
|
honor_disabled_refs: bool,
|
|
147
149
|
node: pending_xref, contnode: TextElement) -> nodes.reference | None:
|
|
148
150
|
# disabling should only be done if no inventory is given
|
|
149
151
|
honor_disabled_refs = honor_disabled_refs and inv_name is None
|
|
152
|
+
intersphinx_disabled_reftypes = env.config.intersphinx_disabled_reftypes
|
|
150
153
|
|
|
151
|
-
if honor_disabled_refs and '*' in
|
|
154
|
+
if honor_disabled_refs and '*' in intersphinx_disabled_reftypes:
|
|
152
155
|
return None
|
|
153
156
|
|
|
154
157
|
typ = node['reftype']
|
|
155
158
|
if typ == 'any':
|
|
156
159
|
for domain_name, domain in env.domains.items():
|
|
157
|
-
if
|
|
158
|
-
and (domain_name + ':*') in env.config.intersphinx_disabled_reftypes):
|
|
160
|
+
if honor_disabled_refs and f'{domain_name}:*' in intersphinx_disabled_reftypes:
|
|
159
161
|
continue
|
|
160
162
|
objtypes: Iterable[str] = domain.object_types.keys()
|
|
161
163
|
res = _resolve_reference_in_domain(env, inv_name, inventory,
|
|
@@ -170,8 +172,7 @@ def _resolve_reference(env: BuildEnvironment, inv_name: str | None, inventory: I
|
|
|
170
172
|
if not domain_name:
|
|
171
173
|
# only objects in domains are in the inventory
|
|
172
174
|
return None
|
|
173
|
-
if
|
|
174
|
-
and (domain_name + ':*') in env.config.intersphinx_disabled_reftypes):
|
|
175
|
+
if honor_disabled_refs and f'{domain_name}:*' in intersphinx_disabled_reftypes:
|
|
175
176
|
return None
|
|
176
177
|
domain = env.get_domain(domain_name)
|
|
177
178
|
objtypes = domain.objtypes_for_role(typ) or ()
|
|
@@ -183,12 +184,12 @@ def _resolve_reference(env: BuildEnvironment, inv_name: str | None, inventory: I
|
|
|
183
184
|
node, contnode)
|
|
184
185
|
|
|
185
186
|
|
|
186
|
-
def inventory_exists(env: BuildEnvironment, inv_name:
|
|
187
|
+
def inventory_exists(env: BuildEnvironment, inv_name: InventoryName) -> bool:
|
|
187
188
|
return inv_name in InventoryAdapter(env).named_inventory
|
|
188
189
|
|
|
189
190
|
|
|
190
191
|
def resolve_reference_in_inventory(env: BuildEnvironment,
|
|
191
|
-
inv_name:
|
|
192
|
+
inv_name: InventoryName,
|
|
192
193
|
node: pending_xref, contnode: TextElement,
|
|
193
194
|
) -> nodes.reference | None:
|
|
194
195
|
"""Attempt to resolve a missing reference via intersphinx references.
|
|
@@ -2,19 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING, Final,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Final, NoReturn
|
|
6
6
|
|
|
7
7
|
from sphinx.util import logging
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Sequence
|
|
11
|
+
from typing import TypeAlias
|
|
12
|
+
|
|
10
13
|
from sphinx.environment import BuildEnvironment
|
|
11
14
|
from sphinx.util.typing import Inventory
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
#: The inventory project URL to which links are resolved.
|
|
17
|
+
#:
|
|
18
|
+
#: This value is unique in :confval:`intersphinx_mapping`.
|
|
19
|
+
InventoryURI = str
|
|
20
|
+
|
|
21
|
+
#: The inventory (non-empty) name.
|
|
22
|
+
#:
|
|
23
|
+
#: It is unique and in bijection with an inventory remote URL.
|
|
24
|
+
InventoryName = str
|
|
25
|
+
|
|
26
|
+
#: A target (local or remote) containing the inventory data to fetch.
|
|
27
|
+
#:
|
|
28
|
+
#: Empty strings are not expected and ``None`` indicates the default
|
|
29
|
+
#: inventory file name :data:`~sphinx.builder.html.INVENTORY_FILENAME`.
|
|
30
|
+
InventoryLocation = str | None
|
|
31
|
+
|
|
32
|
+
#: Inventory cache entry. The integer field is the cache expiration time.
|
|
33
|
+
InventoryCacheEntry: TypeAlias = tuple[InventoryName, int, Inventory]
|
|
34
|
+
|
|
35
|
+
#: The type of :confval:`intersphinx_mapping` *after* normalisation.
|
|
36
|
+
IntersphinxMapping = dict[
|
|
37
|
+
InventoryName,
|
|
38
|
+
tuple[InventoryName, tuple[InventoryURI, tuple[InventoryLocation, ...]]],
|
|
39
|
+
]
|
|
14
40
|
|
|
15
41
|
LOGGER: Final[logging.SphinxLoggerAdapter] = logging.getLogger('sphinx.ext.intersphinx')
|
|
16
42
|
|
|
17
43
|
|
|
44
|
+
class _IntersphinxProject:
|
|
45
|
+
name: InventoryName
|
|
46
|
+
target_uri: InventoryURI
|
|
47
|
+
locations: tuple[InventoryLocation, ...]
|
|
48
|
+
|
|
49
|
+
__slots__ = {
|
|
50
|
+
'name': 'The inventory name. '
|
|
51
|
+
'It is unique and in bijection with an remote inventory URL.',
|
|
52
|
+
'target_uri': 'The inventory project URL to which links are resolved. '
|
|
53
|
+
'It is unique and in bijection with an inventory name.',
|
|
54
|
+
'locations': 'A tuple of local or remote targets containing '
|
|
55
|
+
'the inventory data to fetch. '
|
|
56
|
+
'None indicates the default inventory file name.',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
*,
|
|
62
|
+
name: InventoryName,
|
|
63
|
+
target_uri: InventoryURI,
|
|
64
|
+
locations: Sequence[InventoryLocation],
|
|
65
|
+
) -> None:
|
|
66
|
+
if not name or not isinstance(name, str):
|
|
67
|
+
msg = 'name must be a non-empty string'
|
|
68
|
+
raise ValueError(msg)
|
|
69
|
+
if not target_uri or not isinstance(target_uri, str):
|
|
70
|
+
msg = 'target_uri must be a non-empty string'
|
|
71
|
+
raise ValueError(msg)
|
|
72
|
+
if not locations or not isinstance(locations, tuple):
|
|
73
|
+
msg = 'locations must be a non-empty tuple'
|
|
74
|
+
raise ValueError(msg)
|
|
75
|
+
if any(
|
|
76
|
+
location is not None and (not location or not isinstance(location, str))
|
|
77
|
+
for location in locations
|
|
78
|
+
):
|
|
79
|
+
msg = 'locations must be a tuple of strings or None'
|
|
80
|
+
raise ValueError(msg)
|
|
81
|
+
object.__setattr__(self, 'name', name)
|
|
82
|
+
object.__setattr__(self, 'target_uri', target_uri)
|
|
83
|
+
object.__setattr__(self, 'locations', tuple(locations))
|
|
84
|
+
|
|
85
|
+
def __repr__(self) -> str:
|
|
86
|
+
return (f'{self.__class__.__name__}('
|
|
87
|
+
f'name={self.name!r}, '
|
|
88
|
+
f'target_uri={self.target_uri!r}, '
|
|
89
|
+
f'locations={self.locations!r})')
|
|
90
|
+
|
|
91
|
+
def __eq__(self, other: object) -> bool:
|
|
92
|
+
if not isinstance(other, _IntersphinxProject):
|
|
93
|
+
return NotImplemented
|
|
94
|
+
return (
|
|
95
|
+
self.name == other.name
|
|
96
|
+
and self.target_uri == other.target_uri
|
|
97
|
+
and self.locations == other.locations
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def __hash__(self) -> int:
|
|
101
|
+
return hash((self.name, self.target_uri, self.locations))
|
|
102
|
+
|
|
103
|
+
def __setattr__(self, key: str, value: Any) -> NoReturn:
|
|
104
|
+
msg = f'{self.__class__.__name__} is immutable'
|
|
105
|
+
raise AttributeError(msg)
|
|
106
|
+
|
|
107
|
+
def __delattr__(self, key: str) -> NoReturn:
|
|
108
|
+
msg = f'{self.__class__.__name__} is immutable'
|
|
109
|
+
raise AttributeError(msg)
|
|
110
|
+
|
|
111
|
+
|
|
18
112
|
class InventoryAdapter:
|
|
19
113
|
"""Inventory adapter for environment"""
|
|
20
114
|
|
|
@@ -29,14 +123,13 @@ class InventoryAdapter:
|
|
|
29
123
|
self.env.intersphinx_named_inventory = {} # type: ignore[attr-defined]
|
|
30
124
|
|
|
31
125
|
@property
|
|
32
|
-
def cache(self) -> dict[
|
|
126
|
+
def cache(self) -> dict[InventoryURI, InventoryCacheEntry]:
|
|
33
127
|
"""Intersphinx cache.
|
|
34
128
|
|
|
35
|
-
- Key is the URI of the remote inventory
|
|
36
|
-
- Element one is the key given in the Sphinx intersphinx_mapping
|
|
37
|
-
|
|
38
|
-
- Element
|
|
39
|
-
- Element three is the loaded remote inventory, type Inventory
|
|
129
|
+
- Key is the URI of the remote inventory.
|
|
130
|
+
- Element one is the key given in the Sphinx :confval:`intersphinx_mapping`.
|
|
131
|
+
- Element two is a time value for cache invalidation, an integer.
|
|
132
|
+
- Element three is the loaded remote inventory of type :class:`!Inventory`.
|
|
40
133
|
"""
|
|
41
134
|
return self.env.intersphinx_cache # type: ignore[attr-defined]
|
|
42
135
|
|
|
@@ -45,7 +138,7 @@ class InventoryAdapter:
|
|
|
45
138
|
return self.env.intersphinx_inventory # type: ignore[attr-defined]
|
|
46
139
|
|
|
47
140
|
@property
|
|
48
|
-
def named_inventory(self) -> dict[
|
|
141
|
+
def named_inventory(self) -> dict[InventoryName, Inventory]:
|
|
49
142
|
return self.env.intersphinx_named_inventory # type: ignore[attr-defined]
|
|
50
143
|
|
|
51
144
|
def clear(self) -> None:
|
sphinx/ext/mathjax.py
CHANGED
|
@@ -22,7 +22,7 @@ from sphinx.util.math import get_node_equation_number
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from sphinx.application import Sphinx
|
|
24
24
|
from sphinx.util.typing import ExtensionMetadata
|
|
25
|
-
from sphinx.writers.
|
|
25
|
+
from sphinx.writers.html5 import HTML5Translator
|
|
26
26
|
|
|
27
27
|
# more information for mathjax secure url is here:
|
|
28
28
|
# https://docs.mathjax.org/en/latest/web/start.html#using-mathjax-from-a-content-delivery-network-cdn
|
sphinx/ext/napoleon/docstring.py
CHANGED
|
@@ -8,14 +8,14 @@ import inspect
|
|
|
8
8
|
import re
|
|
9
9
|
from functools import partial
|
|
10
10
|
from itertools import starmap
|
|
11
|
-
from typing import TYPE_CHECKING, Any
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
12
|
|
|
13
13
|
from sphinx.locale import _, __
|
|
14
14
|
from sphinx.util import logging
|
|
15
15
|
from sphinx.util.typing import get_type_hints, stringify_annotation
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
-
from collections.abc import Iterator
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
19
|
|
|
20
20
|
from sphinx.application import Sphinx
|
|
21
21
|
from sphinx.config import Config as SphinxConfig
|
sphinx/ext/todo.py
CHANGED
|
@@ -29,7 +29,7 @@ if TYPE_CHECKING:
|
|
|
29
29
|
from sphinx.application import Sphinx
|
|
30
30
|
from sphinx.environment import BuildEnvironment
|
|
31
31
|
from sphinx.util.typing import ExtensionMetadata, OptionSpec
|
|
32
|
-
from sphinx.writers.
|
|
32
|
+
from sphinx.writers.html5 import HTML5Translator
|
|
33
33
|
from sphinx.writers.latex import LaTeXTranslator
|
|
34
34
|
|
|
35
35
|
logger = logging.getLogger(__name__)
|
|
@@ -43,7 +43,7 @@ class todolist(nodes.General, nodes.Element):
|
|
|
43
43
|
pass
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
class Todo(BaseAdmonition, SphinxDirective):
|
|
46
|
+
class Todo(BaseAdmonition, SphinxDirective): # type: ignore[misc]
|
|
47
47
|
"""
|
|
48
48
|
A todo entry, displayed (if configured) in the form of an admonition.
|
|
49
49
|
"""
|
sphinx/ext/viewcode.py
CHANGED
|
@@ -21,6 +21,7 @@ from sphinx.transforms.post_transforms import SphinxPostTransform
|
|
|
21
21
|
from sphinx.util import logging
|
|
22
22
|
from sphinx.util.display import status_iterator
|
|
23
23
|
from sphinx.util.nodes import make_refnode
|
|
24
|
+
from sphinx.util.osutil import _last_modified_time
|
|
24
25
|
|
|
25
26
|
if TYPE_CHECKING:
|
|
26
27
|
from collections.abc import Iterable, Iterator
|
|
@@ -231,7 +232,7 @@ def should_generate_module_page(app: Sphinx, modname: str) -> bool:
|
|
|
231
232
|
page_filename = path.join(app.outdir, '_modules/', basename)
|
|
232
233
|
|
|
233
234
|
try:
|
|
234
|
-
if
|
|
235
|
+
if _last_modified_time(module_filename) <= _last_modified_time(page_filename):
|
|
235
236
|
# generation is not needed if the HTML page is newer than module file.
|
|
236
237
|
return False
|
|
237
238
|
except OSError:
|
sphinx/highlighting.py
CHANGED
|
@@ -86,8 +86,8 @@ _LATEX_ADD_STYLES = r"""
|
|
|
86
86
|
class PygmentsBridge:
|
|
87
87
|
# Set these attributes if you want to have different Pygments formatters
|
|
88
88
|
# than the default ones.
|
|
89
|
-
html_formatter = HtmlFormatter
|
|
90
|
-
latex_formatter = LatexFormatter
|
|
89
|
+
html_formatter = HtmlFormatter[str]
|
|
90
|
+
latex_formatter = LatexFormatter[str]
|
|
91
91
|
|
|
92
92
|
def __init__(
|
|
93
93
|
self, dest: str = 'html', stylename: str = 'sphinx', latex_engine: str | None = None
|
|
@@ -98,7 +98,7 @@ class PygmentsBridge:
|
|
|
98
98
|
style = self.get_style(stylename)
|
|
99
99
|
self.formatter_args: dict[str, Any] = {'style': style}
|
|
100
100
|
if dest == 'html':
|
|
101
|
-
self.formatter = self.html_formatter
|
|
101
|
+
self.formatter: type[Formatter[str]] = self.html_formatter
|
|
102
102
|
else:
|
|
103
103
|
self.formatter = self.latex_formatter
|
|
104
104
|
self.formatter_args['commandprefix'] = 'PYG'
|
sphinx/io.py
CHANGED
|
@@ -35,7 +35,7 @@ if TYPE_CHECKING:
|
|
|
35
35
|
logger = logging.getLogger(__name__)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class SphinxBaseReader(standalone.Reader):
|
|
38
|
+
class SphinxBaseReader(standalone.Reader): # type: ignore[misc]
|
|
39
39
|
"""
|
|
40
40
|
A base class of readers for Sphinx.
|
|
41
41
|
|
|
@@ -143,7 +143,7 @@ class SphinxI18nReader(SphinxBaseReader):
|
|
|
143
143
|
self.transforms.remove(transform)
|
|
144
144
|
|
|
145
145
|
|
|
146
|
-
class SphinxDummyWriter(UnfilteredWriter):
|
|
146
|
+
class SphinxDummyWriter(UnfilteredWriter): # type: ignore[misc]
|
|
147
147
|
"""Dummy writer module used for generating doctree."""
|
|
148
148
|
|
|
149
149
|
supported = ('html',) # needed to keep "meta" nodes
|
sphinx/jinja2glue.py
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
from os import path
|
|
6
7
|
from pprint import pformat
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
8
9
|
|
|
9
10
|
from jinja2 import BaseLoader, FileSystemLoader, TemplateNotFound
|
|
10
11
|
from jinja2.sandbox import SandboxedEnvironment
|
|
@@ -12,10 +13,10 @@ from jinja2.utils import open_if_exists, pass_context
|
|
|
12
13
|
|
|
13
14
|
from sphinx.application import TemplateBridge
|
|
14
15
|
from sphinx.util import logging
|
|
15
|
-
from sphinx.util.osutil import
|
|
16
|
+
from sphinx.util.osutil import _last_modified_time
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
|
-
from collections.abc import Iterator
|
|
19
|
+
from collections.abc import Callable, Iterator
|
|
19
20
|
|
|
20
21
|
from jinja2.environment import Environment
|
|
21
22
|
|
|
@@ -127,11 +128,11 @@ class SphinxFileSystemLoader(FileSystemLoader):
|
|
|
127
128
|
with f:
|
|
128
129
|
contents = f.read().decode(self.encoding)
|
|
129
130
|
|
|
130
|
-
mtime =
|
|
131
|
+
mtime = _last_modified_time(filename)
|
|
131
132
|
|
|
132
133
|
def uptodate() -> bool:
|
|
133
134
|
try:
|
|
134
|
-
return
|
|
135
|
+
return _last_modified_time(filename) == mtime
|
|
135
136
|
except OSError:
|
|
136
137
|
return False
|
|
137
138
|
|
|
@@ -203,7 +204,13 @@ class BuiltinTemplateLoader(TemplateBridge, BaseLoader):
|
|
|
203
204
|
return self.environment.from_string(source).render(context)
|
|
204
205
|
|
|
205
206
|
def newest_template_mtime(self) -> float:
|
|
206
|
-
return max(
|
|
207
|
+
return max(
|
|
208
|
+
os.stat(os.path.join(root, sfile)).st_mtime_ns / 10**9
|
|
209
|
+
for dirname in self.pathchain
|
|
210
|
+
for root, _dirs, files in os.walk(dirname)
|
|
211
|
+
for sfile in files
|
|
212
|
+
if sfile.endswith('.html')
|
|
213
|
+
)
|
|
207
214
|
|
|
208
215
|
# Loader interface
|
|
209
216
|
|
sphinx/locale/__init__.py
CHANGED
|
@@ -9,8 +9,9 @@ from os import path
|
|
|
9
9
|
from typing import TYPE_CHECKING
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
-
|
|
13
|
-
from
|
|
12
|
+
import os
|
|
13
|
+
from collections.abc import Callable, Iterable
|
|
14
|
+
from typing import Any
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class _TranslationProxy:
|
|
@@ -98,7 +99,7 @@ translators: dict[tuple[str, str], NullTranslations] = {}
|
|
|
98
99
|
|
|
99
100
|
|
|
100
101
|
def init(
|
|
101
|
-
locale_dirs: Iterable[str | None],
|
|
102
|
+
locale_dirs: Iterable[str | os.PathLike[str] | None],
|
|
102
103
|
language: str | None,
|
|
103
104
|
catalog: str = 'sphinx',
|
|
104
105
|
namespace: str = 'general',
|
sphinx/project.py
CHANGED
|
@@ -4,13 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import os
|
|
7
|
-
from
|
|
7
|
+
from pathlib import Path
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
10
|
from sphinx.locale import __
|
|
11
11
|
from sphinx.util import logging
|
|
12
12
|
from sphinx.util.matching import get_matching_files
|
|
13
|
-
from sphinx.util.osutil import path_stabilize
|
|
13
|
+
from sphinx.util.osutil import path_stabilize
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
from collections.abc import Iterable
|
|
@@ -24,7 +24,7 @@ class Project:
|
|
|
24
24
|
|
|
25
25
|
def __init__(self, srcdir: str | os.PathLike[str], source_suffix: Iterable[str]) -> None:
|
|
26
26
|
#: Source directory.
|
|
27
|
-
self.srcdir = srcdir
|
|
27
|
+
self.srcdir = Path(srcdir)
|
|
28
28
|
|
|
29
29
|
#: source_suffix. Same as :confval:`source_suffix`.
|
|
30
30
|
self.source_suffix = tuple(source_suffix)
|
|
@@ -34,8 +34,8 @@ class Project:
|
|
|
34
34
|
self.docnames: set[str] = set()
|
|
35
35
|
|
|
36
36
|
# Bijective mapping between docnames and (srcdir relative) paths.
|
|
37
|
-
self._path_to_docname: dict[
|
|
38
|
-
self._docname_to_path: dict[str,
|
|
37
|
+
self._path_to_docname: dict[Path, str] = {}
|
|
38
|
+
self._docname_to_path: dict[str, Path] = {}
|
|
39
39
|
|
|
40
40
|
def restore(self, other: Project) -> None:
|
|
41
41
|
"""Take over a result of last build."""
|
|
@@ -60,22 +60,25 @@ class Project:
|
|
|
60
60
|
):
|
|
61
61
|
if docname := self.path2doc(filename):
|
|
62
62
|
if docname in self.docnames:
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
files = [
|
|
64
|
+
str(f.relative_to(self.srcdir))
|
|
65
|
+
for f in self.srcdir.glob(f'{docname}.*')
|
|
66
|
+
]
|
|
65
67
|
logger.warning(
|
|
66
68
|
__(
|
|
67
|
-
'multiple files found for the document "%s": %
|
|
69
|
+
'multiple files found for the document "%s": %s\n'
|
|
68
70
|
'Use %r for the build.'
|
|
69
71
|
),
|
|
70
72
|
docname,
|
|
71
|
-
files,
|
|
73
|
+
', '.join(files),
|
|
72
74
|
self.doc2path(docname, absolute=True),
|
|
73
75
|
once=True,
|
|
74
76
|
)
|
|
75
|
-
elif os.access(
|
|
77
|
+
elif os.access(self.srcdir / filename, os.R_OK):
|
|
76
78
|
self.docnames.add(docname)
|
|
77
|
-
|
|
78
|
-
self.
|
|
79
|
+
path = Path(filename)
|
|
80
|
+
self._path_to_docname[path] = docname
|
|
81
|
+
self._docname_to_path[docname] = path
|
|
79
82
|
else:
|
|
80
83
|
logger.warning(
|
|
81
84
|
__('Ignored unreadable document %r.'), filename, location=docname
|
|
@@ -91,18 +94,19 @@ class Project:
|
|
|
91
94
|
try:
|
|
92
95
|
return self._path_to_docname[filename] # type: ignore[index]
|
|
93
96
|
except KeyError:
|
|
94
|
-
|
|
97
|
+
path = Path(filename)
|
|
98
|
+
if path.is_absolute():
|
|
95
99
|
with contextlib.suppress(ValueError):
|
|
96
|
-
|
|
100
|
+
path = path.relative_to(self.srcdir)
|
|
97
101
|
|
|
98
102
|
for suffix in self.source_suffix:
|
|
99
|
-
if
|
|
100
|
-
return path_stabilize(
|
|
103
|
+
if path.name.endswith(suffix):
|
|
104
|
+
return path_stabilize(path).removesuffix(suffix)
|
|
101
105
|
|
|
102
106
|
# the file does not have a docname
|
|
103
107
|
return None
|
|
104
108
|
|
|
105
|
-
def doc2path(self, docname: str, absolute: bool) ->
|
|
109
|
+
def doc2path(self, docname: str, absolute: bool) -> Path:
|
|
106
110
|
"""Return the filename for the document name.
|
|
107
111
|
|
|
108
112
|
If *absolute* is True, return as an absolute path.
|
|
@@ -112,8 +116,8 @@ class Project:
|
|
|
112
116
|
filename = self._docname_to_path[docname]
|
|
113
117
|
except KeyError:
|
|
114
118
|
# Backwards compatibility: the document does not exist
|
|
115
|
-
filename = docname + self._first_source_suffix
|
|
119
|
+
filename = Path(docname + self._first_source_suffix)
|
|
116
120
|
|
|
117
121
|
if absolute:
|
|
118
|
-
return
|
|
122
|
+
return self.srcdir / filename
|
|
119
123
|
return filename
|
sphinx/pycode/ast.py
CHANGED
|
@@ -130,7 +130,7 @@ class _UnparseVisitor(ast.NodeVisitor):
|
|
|
130
130
|
def visit_Constant(self, node: ast.Constant) -> str:
|
|
131
131
|
if node.value is Ellipsis:
|
|
132
132
|
return "..."
|
|
133
|
-
elif isinstance(node.value,
|
|
133
|
+
elif isinstance(node.value, int | float | complex):
|
|
134
134
|
if self.code:
|
|
135
135
|
return ast.get_source_segment(self.code, node) or repr(node.value)
|
|
136
136
|
else:
|
|
@@ -141,7 +141,7 @@ class _UnparseVisitor(ast.NodeVisitor):
|
|
|
141
141
|
def visit_Dict(self, node: ast.Dict) -> str:
|
|
142
142
|
keys = (self.visit(k) for k in node.keys if k is not None)
|
|
143
143
|
values = (self.visit(v) for v in node.values)
|
|
144
|
-
items = (k + ": " + v for k, v in zip(keys, values))
|
|
144
|
+
items = (k + ": " + v for k, v in zip(keys, values, strict=True))
|
|
145
145
|
return "{" + ", ".join(items) + "}"
|
|
146
146
|
|
|
147
147
|
def visit_Lambda(self, node: ast.Lambda) -> str:
|
sphinx/pycode/parser.py
CHANGED
|
@@ -108,7 +108,7 @@ class Token:
|
|
|
108
108
|
return self.kind == other
|
|
109
109
|
elif isinstance(other, str):
|
|
110
110
|
return self.value == other
|
|
111
|
-
elif isinstance(other,
|
|
111
|
+
elif isinstance(other, list | tuple):
|
|
112
112
|
return [self.kind, self.value] == list(other)
|
|
113
113
|
elif other is None:
|
|
114
114
|
return False
|
|
@@ -404,7 +404,7 @@ class VariableCommentPicker(ast.NodeVisitor):
|
|
|
404
404
|
|
|
405
405
|
def visit_Expr(self, node: ast.Expr) -> None:
|
|
406
406
|
"""Handles Expr node and pick up a comment if string."""
|
|
407
|
-
if (isinstance(self.previous,
|
|
407
|
+
if (isinstance(self.previous, ast.Assign | ast.AnnAssign) and
|
|
408
408
|
isinstance(node.value, ast.Constant) and isinstance(node.value.value, str)):
|
|
409
409
|
try:
|
|
410
410
|
targets = get_assign_targets(self.previous)
|
sphinx/pygments_styles.py
CHANGED
|
@@ -28,12 +28,12 @@ class SphinxStyle(Style):
|
|
|
28
28
|
background_color = '#eeffcc'
|
|
29
29
|
default_style = ''
|
|
30
30
|
|
|
31
|
-
styles =
|
|
32
|
-
|
|
31
|
+
styles = {
|
|
32
|
+
**FriendlyStyle.styles,
|
|
33
33
|
Generic.Output: '#333',
|
|
34
34
|
Comment: 'italic #408090',
|
|
35
35
|
Number: '#208050',
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class PyramidStyle(Style):
|
sphinx/registry.py
CHANGED
|
@@ -2,16 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import sys
|
|
6
5
|
import traceback
|
|
7
6
|
from importlib import import_module
|
|
7
|
+
from importlib.metadata import entry_points
|
|
8
8
|
from types import MethodType
|
|
9
|
-
from typing import TYPE_CHECKING, Any
|
|
10
|
-
|
|
11
|
-
if sys.version_info >= (3, 10):
|
|
12
|
-
from importlib.metadata import entry_points
|
|
13
|
-
else:
|
|
14
|
-
from importlib_metadata import entry_points
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
15
10
|
|
|
16
11
|
from sphinx.domains import Domain, Index, ObjType
|
|
17
12
|
from sphinx.domains.std import GenericObject, Target
|
|
@@ -25,7 +20,7 @@ from sphinx.util import logging
|
|
|
25
20
|
from sphinx.util.logging import prefixed_warnings
|
|
26
21
|
|
|
27
22
|
if TYPE_CHECKING:
|
|
28
|
-
from collections.abc import Iterator, Sequence
|
|
23
|
+
from collections.abc import Callable, Iterator, Sequence
|
|
29
24
|
|
|
30
25
|
from docutils import nodes
|
|
31
26
|
from docutils.core import Publisher
|
sphinx/search/__init__.py
CHANGED
|
@@ -487,7 +487,7 @@ class IndexBuilder:
|
|
|
487
487
|
self._index_entries[docname] = sorted(_index_entries)
|
|
488
488
|
|
|
489
489
|
def _word_collector(self, doctree: nodes.document) -> WordStore:
|
|
490
|
-
def _visit_nodes(node):
|
|
490
|
+
def _visit_nodes(node: nodes.Node) -> None:
|
|
491
491
|
if isinstance(node, nodes.comment):
|
|
492
492
|
return
|
|
493
493
|
elif isinstance(node, nodes.raw):
|
sphinx/testing/path.py
CHANGED
|
@@ -4,12 +4,13 @@ import os
|
|
|
4
4
|
import shutil
|
|
5
5
|
import sys
|
|
6
6
|
import warnings
|
|
7
|
-
from typing import IO, TYPE_CHECKING, Any
|
|
7
|
+
from typing import IO, TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
9
|
from sphinx.deprecation import RemovedInSphinx90Warning
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
import builtins
|
|
13
|
+
from collections.abc import Callable
|
|
13
14
|
|
|
14
15
|
warnings.warn("'sphinx.testing.path' is deprecated. "
|
|
15
16
|
"Use 'os.path' or 'pathlib' instead.",
|
sphinx/testing/util.py
CHANGED
|
@@ -43,7 +43,7 @@ def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: Any) ->
|
|
|
43
43
|
'The node%s has %d child nodes, not one' % (xpath, len(node))
|
|
44
44
|
assert_node(node[0], cls[1:], xpath=xpath + "[0]", **kwargs)
|
|
45
45
|
elif isinstance(cls, tuple):
|
|
46
|
-
assert isinstance(node,
|
|
46
|
+
assert isinstance(node, list | nodes.Element), \
|
|
47
47
|
'The node%s does not have any items' % xpath
|
|
48
48
|
assert len(node) == len(cls), \
|
|
49
49
|
'The node%s has %d child nodes, not %r' % (xpath, len(node), len(cls))
|
sphinx/texinputs/Makefile.jinja
CHANGED
|
@@ -77,7 +77,8 @@ tar: all-$(FMT)
|
|
|
77
77
|
rm -r $(ARCHIVEPREFIX)docs-$(FMT)
|
|
78
78
|
|
|
79
79
|
gz: tar
|
|
80
|
-
|
|
80
|
+
# -n to omit mtime from gzip headers
|
|
81
|
+
gzip -n -9 < $(ARCHIVEPREFIX)docs-$(FMT).tar > $(ARCHIVEPREFIX)docs-$(FMT).tar.gz
|
|
81
82
|
|
|
82
83
|
bz2: tar
|
|
83
84
|
bzip2 -9 -k $(ARCHIVEPREFIX)docs-$(FMT).tar
|
|
@@ -49,7 +49,8 @@ tar: all-$(FMT)
|
|
|
49
49
|
rm -r $(ARCHIVEPREFIX)docs-$(FMT)
|
|
50
50
|
|
|
51
51
|
gz: tar
|
|
52
|
-
|
|
52
|
+
# -n to omit mtime from gzip headers
|
|
53
|
+
gzip -n -9 < $(ARCHIVEPREFIX)docs-$(FMT).tar > $(ARCHIVEPREFIX)docs-$(FMT).tar.gz
|
|
53
54
|
|
|
54
55
|
bz2: tar
|
|
55
56
|
bzip2 -9 -k $(ARCHIVEPREFIX)docs-$(FMT).tar
|