Sphinx 7.4.6__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 +90 -59
- 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/__init__.py +14 -9
- 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/javascript.py +1 -1
- sphinx/domains/math.py +1 -1
- sphinx/domains/python/__init__.py +1 -1
- sphinx/domains/python/_annotations.py +23 -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 +25 -67
- 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 +15 -7
- 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/locale/ta/LC_MESSAGES/sphinx.js +54 -54
- sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
- sphinx/locale/ta/LC_MESSAGES/sphinx.po +1578 -1843
- sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +496 -704
- 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 +41 -9
- sphinx/util/i18n.py +9 -4
- sphinx/util/images.py +3 -2
- sphinx/util/inspect.py +29 -44
- 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 +46 -23
- sphinx/util/parallel.py +2 -2
- sphinx/util/requests.py +1 -1
- sphinx/util/template.py +3 -3
- sphinx/util/typing.py +67 -70
- 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.6.dist-info → sphinx-8.0.0rc1.dist-info}/METADATA +10 -9
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/RECORD +112 -114
- sphinx/templates/quickstart/Makefile.jinja +0 -98
- sphinx/templates/quickstart/make.bat.jinja +0 -110
- sphinx/util/_pathlib.py +0 -120
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/LICENSE.rst +0 -0
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/WHEEL +0 -0
- {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/entry_points.txt +0 -0
sphinx/util/fileutil.py
CHANGED
|
@@ -4,16 +4,22 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import posixpath
|
|
7
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
9
9
|
from docutils.utils import relative_path
|
|
10
10
|
|
|
11
|
+
from sphinx.locale import __
|
|
12
|
+
from sphinx.util import logging
|
|
11
13
|
from sphinx.util.osutil import copyfile, ensuredir
|
|
12
14
|
|
|
13
15
|
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
|
|
14
18
|
from sphinx.util.template import BaseRenderer
|
|
15
19
|
from sphinx.util.typing import PathMatcher
|
|
16
20
|
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
17
23
|
|
|
18
24
|
def _template_basename(filename: str | os.PathLike[str]) -> str | None:
|
|
19
25
|
"""Given an input filename:
|
|
@@ -30,7 +36,9 @@ def _template_basename(filename: str | os.PathLike[str]) -> str | None:
|
|
|
30
36
|
|
|
31
37
|
def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLike[str],
|
|
32
38
|
context: dict[str, Any] | None = None,
|
|
33
|
-
renderer: BaseRenderer | None = None
|
|
39
|
+
renderer: BaseRenderer | None = None,
|
|
40
|
+
*,
|
|
41
|
+
force: bool = False) -> None:
|
|
34
42
|
"""Copy an asset file to destination.
|
|
35
43
|
|
|
36
44
|
On copying, it expands the template variables if context argument is given and
|
|
@@ -40,6 +48,7 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
|
|
|
40
48
|
:param destination: The path to destination file or directory
|
|
41
49
|
:param context: The template variables. If not given, template files are simply copied
|
|
42
50
|
:param renderer: The template engine. If not given, SphinxRenderer is used by default
|
|
51
|
+
:param bool force: Overwrite the destination file even if it exists.
|
|
43
52
|
"""
|
|
44
53
|
if not os.path.exists(source):
|
|
45
54
|
return
|
|
@@ -56,28 +65,46 @@ def copy_asset_file(source: str | os.PathLike[str], destination: str | os.PathLi
|
|
|
56
65
|
renderer = SphinxRenderer()
|
|
57
66
|
|
|
58
67
|
with open(source, encoding='utf-8') as fsrc:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
template_content = fsrc.read()
|
|
69
|
+
rendered_template = renderer.render_string(template_content, context)
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
not force
|
|
73
|
+
and os.path.exists(destination)
|
|
74
|
+
and template_content != rendered_template
|
|
75
|
+
):
|
|
76
|
+
msg = __('Aborted attempted copy from rendered template %s to %s '
|
|
77
|
+
'(the destination path has existing data).')
|
|
78
|
+
logger.warning(msg, os.fsdecode(source), os.fsdecode(destination),
|
|
79
|
+
type='misc', subtype='copy_overwrite')
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
destination = _template_basename(destination) or destination
|
|
83
|
+
with open(destination, 'w', encoding='utf-8') as fdst:
|
|
84
|
+
fdst.write(rendered_template)
|
|
62
85
|
else:
|
|
63
|
-
copyfile(source, destination)
|
|
86
|
+
copyfile(source, destination, force=force)
|
|
64
87
|
|
|
65
88
|
|
|
66
89
|
def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[str],
|
|
67
90
|
excluded: PathMatcher = lambda path: False,
|
|
68
91
|
context: dict[str, Any] | None = None, renderer: BaseRenderer | None = None,
|
|
69
|
-
onerror: Callable[[str, Exception], None] | None = None
|
|
92
|
+
onerror: Callable[[str, Exception], None] | None = None,
|
|
93
|
+
*, force: bool = False) -> None:
|
|
70
94
|
"""Copy asset files to destination recursively.
|
|
71
95
|
|
|
72
96
|
On copying, it expands the template variables if context argument is given and
|
|
73
97
|
the asset is a template file.
|
|
74
98
|
|
|
99
|
+
Use ``copy_asset_file`` instead to copy a single file.
|
|
100
|
+
|
|
75
101
|
:param source: The path to source file or directory
|
|
76
102
|
:param destination: The path to destination directory
|
|
77
103
|
:param excluded: The matcher to determine the given path should be copied or not
|
|
78
104
|
:param context: The template variables. If not given, template files are simply copied
|
|
79
105
|
:param renderer: The template engine. If not given, SphinxRenderer is used by default
|
|
80
106
|
:param onerror: The error handler.
|
|
107
|
+
:param bool force: Overwrite the destination file even if it exists.
|
|
81
108
|
"""
|
|
82
109
|
if not os.path.exists(source):
|
|
83
110
|
return
|
|
@@ -88,7 +115,10 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
|
|
|
88
115
|
|
|
89
116
|
ensuredir(destination)
|
|
90
117
|
if os.path.isfile(source):
|
|
91
|
-
copy_asset_file(source, destination,
|
|
118
|
+
copy_asset_file(source, destination,
|
|
119
|
+
context=context,
|
|
120
|
+
renderer=renderer,
|
|
121
|
+
force=force)
|
|
92
122
|
return
|
|
93
123
|
|
|
94
124
|
for root, dirs, files in os.walk(source, followlinks=True):
|
|
@@ -104,7 +134,9 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
|
|
|
104
134
|
try:
|
|
105
135
|
copy_asset_file(posixpath.join(root, filename),
|
|
106
136
|
posixpath.join(destination, reldir),
|
|
107
|
-
context,
|
|
137
|
+
context=context,
|
|
138
|
+
renderer=renderer,
|
|
139
|
+
force=force)
|
|
108
140
|
except Exception as exc:
|
|
109
141
|
if onerror:
|
|
110
142
|
onerror(posixpath.join(root, filename), exc)
|
sphinx/util/i18n.py
CHANGED
|
@@ -15,12 +15,17 @@ from babel.messages.pofile import read_po
|
|
|
15
15
|
from sphinx.errors import SphinxError
|
|
16
16
|
from sphinx.locale import __
|
|
17
17
|
from sphinx.util import logging
|
|
18
|
-
from sphinx.util.osutil import
|
|
18
|
+
from sphinx.util.osutil import (
|
|
19
|
+
SEP,
|
|
20
|
+
_last_modified_time,
|
|
21
|
+
canon_path,
|
|
22
|
+
relpath,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
if TYPE_CHECKING:
|
|
21
26
|
import datetime as dt
|
|
22
27
|
from collections.abc import Iterator
|
|
23
|
-
from typing import Protocol,
|
|
28
|
+
from typing import Protocol, TypeAlias
|
|
24
29
|
|
|
25
30
|
from babel.core import Locale
|
|
26
31
|
|
|
@@ -52,7 +57,7 @@ if TYPE_CHECKING:
|
|
|
52
57
|
locale: str | Locale | None = ...,
|
|
53
58
|
) -> str: ...
|
|
54
59
|
|
|
55
|
-
Formatter =
|
|
60
|
+
Formatter: TypeAlias = DateFormatter | TimeFormatter | DatetimeFormatter
|
|
56
61
|
|
|
57
62
|
logger = logging.getLogger(__name__)
|
|
58
63
|
|
|
@@ -84,7 +89,7 @@ class CatalogInfo(LocaleFileInfoBase):
|
|
|
84
89
|
def is_outdated(self) -> bool:
|
|
85
90
|
return (
|
|
86
91
|
not path.exists(self.mo_path) or
|
|
87
|
-
|
|
92
|
+
_last_modified_time(self.mo_path) < _last_modified_time(self.po_path))
|
|
88
93
|
|
|
89
94
|
def write_mo(self, locale: str, use_fuzzy: bool = False) -> None:
|
|
90
95
|
with open(self.po_path, encoding=self.charset) as file_po:
|
sphinx/util/images.py
CHANGED
|
@@ -13,8 +13,9 @@ if TYPE_CHECKING:
|
|
|
13
13
|
|
|
14
14
|
try:
|
|
15
15
|
from PIL import Image
|
|
16
|
+
PILLOW_AVAILABLE = True
|
|
16
17
|
except ImportError:
|
|
17
|
-
|
|
18
|
+
PILLOW_AVAILABLE = False
|
|
18
19
|
|
|
19
20
|
mime_suffixes = {
|
|
20
21
|
'.gif': 'image/gif',
|
|
@@ -43,7 +44,7 @@ def get_image_size(filename: str) -> tuple[int, int] | None:
|
|
|
43
44
|
elif isinstance(size[0], float) or isinstance(size[1], float):
|
|
44
45
|
size = (int(size[0]), int(size[1]))
|
|
45
46
|
|
|
46
|
-
if size is None and
|
|
47
|
+
if size is None and PILLOW_AVAILABLE: # fallback to Pillow
|
|
47
48
|
with Image.open(filename) as im:
|
|
48
49
|
size = im.size
|
|
49
50
|
|
sphinx/util/inspect.py
CHANGED
|
@@ -17,19 +17,19 @@ from importlib import import_module
|
|
|
17
17
|
from inspect import Parameter, Signature
|
|
18
18
|
from io import StringIO
|
|
19
19
|
from types import ClassMethodDescriptorType, MethodDescriptorType, WrapperDescriptorType
|
|
20
|
-
from typing import TYPE_CHECKING, Any
|
|
20
|
+
from typing import TYPE_CHECKING, Any, ForwardRef
|
|
21
21
|
|
|
22
22
|
from sphinx.pycode.ast import unparse as ast_unparse
|
|
23
23
|
from sphinx.util import logging
|
|
24
|
-
from sphinx.util.typing import
|
|
24
|
+
from sphinx.util.typing import stringify_annotation
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
27
|
from collections.abc import Callable, Sequence
|
|
28
28
|
from inspect import _ParameterKind
|
|
29
29
|
from types import MethodType, ModuleType
|
|
30
|
-
from typing import Final, Protocol,
|
|
30
|
+
from typing import Final, Protocol, TypeAlias
|
|
31
31
|
|
|
32
|
-
from typing_extensions import
|
|
32
|
+
from typing_extensions import TypeIs
|
|
33
33
|
|
|
34
34
|
class _SupportsGet(Protocol):
|
|
35
35
|
def __get__(self, __instance: Any, __owner: type | None = ...) -> Any: ... # NoQA: E704
|
|
@@ -42,21 +42,21 @@ if TYPE_CHECKING:
|
|
|
42
42
|
# instance is contravariant but we do not need that precision
|
|
43
43
|
def __delete__(self, __instance: Any) -> None: ... # NoQA: E704
|
|
44
44
|
|
|
45
|
-
_RoutineType: TypeAlias =
|
|
46
|
-
types.FunctionType
|
|
47
|
-
types.LambdaType
|
|
48
|
-
types.MethodType
|
|
49
|
-
types.BuiltinFunctionType
|
|
50
|
-
types.BuiltinMethodType
|
|
51
|
-
types.WrapperDescriptorType
|
|
52
|
-
types.MethodDescriptorType
|
|
53
|
-
types.ClassMethodDescriptorType
|
|
54
|
-
|
|
55
|
-
_SignatureType: TypeAlias =
|
|
56
|
-
Callable[..., Any]
|
|
57
|
-
staticmethod
|
|
58
|
-
classmethod
|
|
59
|
-
|
|
45
|
+
_RoutineType: TypeAlias = (
|
|
46
|
+
types.FunctionType
|
|
47
|
+
| types.LambdaType
|
|
48
|
+
| types.MethodType
|
|
49
|
+
| types.BuiltinFunctionType
|
|
50
|
+
| types.BuiltinMethodType
|
|
51
|
+
| types.WrapperDescriptorType
|
|
52
|
+
| types.MethodDescriptorType
|
|
53
|
+
| types.ClassMethodDescriptorType
|
|
54
|
+
)
|
|
55
|
+
_SignatureType: TypeAlias = (
|
|
56
|
+
Callable[..., Any]
|
|
57
|
+
| staticmethod
|
|
58
|
+
| classmethod
|
|
59
|
+
)
|
|
60
60
|
|
|
61
61
|
logger = logging.getLogger(__name__)
|
|
62
62
|
|
|
@@ -128,20 +128,14 @@ def getall(obj: Any) -> Sequence[str] | None:
|
|
|
128
128
|
__all__ = safe_getattr(obj, '__all__', None)
|
|
129
129
|
if __all__ is None:
|
|
130
130
|
return None
|
|
131
|
-
if isinstance(__all__,
|
|
131
|
+
if isinstance(__all__, list | tuple) and all(isinstance(e, str) for e in __all__):
|
|
132
132
|
return __all__
|
|
133
133
|
raise ValueError(__all__)
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
def getannotations(obj: Any) -> Mapping[str, Any]:
|
|
137
137
|
"""Safely get the ``__annotations__`` attribute of an object."""
|
|
138
|
-
|
|
139
|
-
__annotations__ = safe_getattr(obj, '__annotations__', None)
|
|
140
|
-
else:
|
|
141
|
-
# Workaround for bugfix not available until python 3.10 as recommended by docs
|
|
142
|
-
# https://docs.python.org/3.10/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
|
|
143
|
-
__dict__ = safe_getattr(obj, '__dict__', {})
|
|
144
|
-
__annotations__ = __dict__.get('__annotations__', None)
|
|
138
|
+
__annotations__ = safe_getattr(obj, '__annotations__', None)
|
|
145
139
|
if isinstance(__annotations__, Mapping):
|
|
146
140
|
return __annotations__
|
|
147
141
|
return {}
|
|
@@ -198,21 +192,12 @@ def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None:
|
|
|
198
192
|
return __slots__
|
|
199
193
|
elif isinstance(__slots__, str):
|
|
200
194
|
return {__slots__: None}
|
|
201
|
-
elif isinstance(__slots__,
|
|
195
|
+
elif isinstance(__slots__, list | tuple):
|
|
202
196
|
return dict.fromkeys(__slots__)
|
|
203
197
|
else:
|
|
204
198
|
raise ValueError
|
|
205
199
|
|
|
206
200
|
|
|
207
|
-
def isNewType(obj: Any) -> bool:
|
|
208
|
-
"""Check the if object is a kind of :class:`~typing.NewType`."""
|
|
209
|
-
if sys.version_info[:2] >= (3, 10):
|
|
210
|
-
return isinstance(obj, typing.NewType)
|
|
211
|
-
__module__ = safe_getattr(obj, '__module__', None)
|
|
212
|
-
__qualname__ = safe_getattr(obj, '__qualname__', None)
|
|
213
|
-
return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type'
|
|
214
|
-
|
|
215
|
-
|
|
216
201
|
def isenumclass(x: Any) -> TypeIs[type[enum.Enum]]:
|
|
217
202
|
"""Check if the object is an :class:`enumeration class <enum.Enum>`."""
|
|
218
203
|
return isclass(x) and issubclass(x, enum.Enum)
|
|
@@ -237,7 +222,7 @@ def unpartial(obj: Any) -> Any:
|
|
|
237
222
|
|
|
238
223
|
def ispartial(obj: Any) -> TypeIs[partial | partialmethod]:
|
|
239
224
|
"""Check if the object is a partial function or method."""
|
|
240
|
-
return isinstance(obj,
|
|
225
|
+
return isinstance(obj, partial | partialmethod)
|
|
241
226
|
|
|
242
227
|
|
|
243
228
|
def isclassmethod(
|
|
@@ -397,12 +382,12 @@ def _is_wrapped_coroutine(obj: Any) -> bool:
|
|
|
397
382
|
|
|
398
383
|
def isproperty(obj: Any) -> TypeIs[property | cached_property]:
|
|
399
384
|
"""Check if the object is property (possibly cached)."""
|
|
400
|
-
return isinstance(obj,
|
|
385
|
+
return isinstance(obj, property | cached_property)
|
|
401
386
|
|
|
402
387
|
|
|
403
388
|
def isgenericalias(obj: Any) -> TypeIs[types.GenericAlias]:
|
|
404
389
|
"""Check if the object is a generic alias."""
|
|
405
|
-
return isinstance(obj,
|
|
390
|
+
return isinstance(obj, types.GenericAlias | typing._BaseGenericAlias) # type: ignore[attr-defined]
|
|
406
391
|
|
|
407
392
|
|
|
408
393
|
def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
|
|
@@ -652,7 +637,7 @@ def signature(
|
|
|
652
637
|
try:
|
|
653
638
|
# Resolve annotations using ``get_type_hints()`` and type_aliases.
|
|
654
639
|
localns = TypeAliasNamespace(type_aliases)
|
|
655
|
-
annotations = typing.get_type_hints(subject, None, localns)
|
|
640
|
+
annotations = typing.get_type_hints(subject, None, localns, include_extras=True)
|
|
656
641
|
for i, param in enumerate(parameters):
|
|
657
642
|
if param.name in annotations:
|
|
658
643
|
annotation = annotations[param.name]
|
|
@@ -852,11 +837,11 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
|
|
|
852
837
|
params: list[Parameter] = []
|
|
853
838
|
|
|
854
839
|
# positional-only arguments (introduced in Python 3.8)
|
|
855
|
-
for arg, defexpr in zip(args.posonlyargs, defaults):
|
|
840
|
+
for arg, defexpr in zip(args.posonlyargs, defaults, strict=False):
|
|
856
841
|
params.append(_define(Parameter.POSITIONAL_ONLY, arg, code, defexpr=defexpr))
|
|
857
842
|
|
|
858
843
|
# normal arguments
|
|
859
|
-
for arg, defexpr in zip(args.args, defaults[pos_only_offset:]):
|
|
844
|
+
for arg, defexpr in zip(args.args, defaults[pos_only_offset:], strict=False):
|
|
860
845
|
params.append(_define(Parameter.POSITIONAL_OR_KEYWORD, arg, code, defexpr=defexpr))
|
|
861
846
|
|
|
862
847
|
# variadic positional argument (no possible default expression)
|
|
@@ -864,7 +849,7 @@ def signature_from_ast(node: ast.FunctionDef, code: str = '') -> Signature:
|
|
|
864
849
|
params.append(_define(Parameter.VAR_POSITIONAL, args.vararg, code, defexpr=None))
|
|
865
850
|
|
|
866
851
|
# keyword-only arguments
|
|
867
|
-
for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults):
|
|
852
|
+
for arg, defexpr in zip(args.kwonlyargs, args.kw_defaults, strict=False):
|
|
868
853
|
params.append(_define(Parameter.KEYWORD_ONLY, arg, code, defexpr=defexpr))
|
|
869
854
|
|
|
870
855
|
# variadic keyword argument (no possible default expression)
|
sphinx/util/inventory.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
import zlib
|
|
7
|
-
from typing import IO, TYPE_CHECKING
|
|
7
|
+
from typing import IO, TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from sphinx.locale import __
|
|
10
10
|
from sphinx.util import logging
|
|
@@ -13,7 +13,7 @@ BUFSIZE = 16 * 1024
|
|
|
13
13
|
logger = logging.getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
-
from collections.abc import Iterator
|
|
16
|
+
from collections.abc import Callable, Iterator
|
|
17
17
|
|
|
18
18
|
from sphinx.builders import Builder
|
|
19
19
|
from sphinx.environment import BuildEnvironment
|
sphinx/util/matching.py
CHANGED
|
@@ -4,12 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os.path
|
|
6
6
|
import re
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from sphinx.util.osutil import canon_path, path_stabilize
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
-
from collections.abc import Iterable, Iterator
|
|
12
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def _translate_pattern(pat: str) -> str:
|
sphinx/util/math.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from docutils import nodes
|
|
9
9
|
|
|
10
|
-
from sphinx.
|
|
10
|
+
from sphinx.writers.html5 import HTML5Translator
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def get_node_equation_number(writer: HTML5Translator, node: nodes.math_block) -> str:
|
sphinx/util/nodes.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import re
|
|
7
7
|
import unicodedata
|
|
8
|
-
from typing import TYPE_CHECKING, Any,
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast
|
|
9
9
|
|
|
10
10
|
from docutils import nodes
|
|
11
11
|
from docutils.nodes import Node
|
|
@@ -16,7 +16,7 @@ from sphinx.util import logging
|
|
|
16
16
|
from sphinx.util.parsing import _fresh_title_style_context
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from collections.abc import Iterable, Iterator
|
|
19
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
20
20
|
|
|
21
21
|
from docutils.nodes import Element
|
|
22
22
|
from docutils.parsers.rst import Directive
|
|
@@ -178,12 +178,12 @@ def apply_source_workaround(node: Element) -> None:
|
|
|
178
178
|
return
|
|
179
179
|
|
|
180
180
|
# workaround: some docutils nodes doesn't have source, line.
|
|
181
|
-
if
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
))
|
|
181
|
+
if isinstance(node, (
|
|
182
|
+
nodes.rubric # #1305 rubric directive
|
|
183
|
+
| nodes.line # #1477 line node
|
|
184
|
+
| nodes.image # #3093 image directive in substitution
|
|
185
|
+
| nodes.field_name # #3335 field list syntax
|
|
186
|
+
)):
|
|
187
187
|
logger.debug('[i18n] PATCH: %r to have source and line: %s',
|
|
188
188
|
get_full_module_name(node), repr_domxml(node))
|
|
189
189
|
try:
|
sphinx/util/osutil.py
CHANGED
|
@@ -13,10 +13,9 @@ from io import StringIO
|
|
|
13
13
|
from os import path
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
-
from sphinx.
|
|
16
|
+
from sphinx.locale import __
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from collections.abc import Iterator
|
|
20
19
|
from pathlib import Path
|
|
21
20
|
from types import TracebackType
|
|
22
21
|
from typing import Any
|
|
@@ -51,7 +50,7 @@ def relative_uri(base: str, to: str) -> str:
|
|
|
51
50
|
b2 = base.split('#')[0].split(SEP)
|
|
52
51
|
t2 = to.split('#')[0].split(SEP)
|
|
53
52
|
# remove common segments (except the last segment)
|
|
54
|
-
for x, y in zip(b2[:-1], t2[:-1]):
|
|
53
|
+
for x, y in zip(b2[:-1], t2[:-1], strict=False):
|
|
55
54
|
if x != y:
|
|
56
55
|
break
|
|
57
56
|
b2.pop(0)
|
|
@@ -72,27 +71,38 @@ def ensuredir(file: str | os.PathLike[str]) -> None:
|
|
|
72
71
|
os.makedirs(file, exist_ok=True)
|
|
73
72
|
|
|
74
73
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
for root, _dirs, files in os.walk(dirname):
|
|
78
|
-
for sfile in files:
|
|
79
|
-
if sfile.endswith(suffix):
|
|
80
|
-
with contextlib.suppress(OSError):
|
|
81
|
-
yield path.getmtime(path.join(root, sfile))
|
|
74
|
+
def _last_modified_time(source: str | os.PathLike[str], /) -> int:
|
|
75
|
+
"""Return the last modified time of ``filename``.
|
|
82
76
|
|
|
77
|
+
The time is returned as integer microseconds.
|
|
78
|
+
The lowest common denominator of modern file-systems seems to be
|
|
79
|
+
microsecond-level precision.
|
|
83
80
|
|
|
84
|
-
|
|
81
|
+
We prefer to err on the side of re-rendering a file,
|
|
82
|
+
so we round up to the nearest microsecond.
|
|
83
|
+
"""
|
|
84
|
+
st = source.stat() if isinstance(source, os.DirEntry) else os.stat(source)
|
|
85
|
+
# upside-down floor division to get the ceiling
|
|
86
|
+
return -(st.st_mtime_ns // -1_000)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _copy_times(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> None:
|
|
85
90
|
"""Copy a file's modification times."""
|
|
86
|
-
st = os.stat(source)
|
|
87
|
-
|
|
88
|
-
os.utime(dest, (st.st_atime, st.st_mtime))
|
|
91
|
+
st = source.stat() if isinstance(source, os.DirEntry) else os.stat(source)
|
|
92
|
+
os.utime(dest, ns=(st.st_atime_ns, st.st_mtime_ns))
|
|
89
93
|
|
|
90
94
|
|
|
91
|
-
def copyfile(
|
|
95
|
+
def copyfile(
|
|
96
|
+
source: str | os.PathLike[str],
|
|
97
|
+
dest: str | os.PathLike[str],
|
|
98
|
+
*,
|
|
99
|
+
force: bool = False,
|
|
100
|
+
) -> None:
|
|
92
101
|
"""Copy a file and its modification times, if possible.
|
|
93
102
|
|
|
94
103
|
:param source: An existing source to copy.
|
|
95
104
|
:param dest: The destination path.
|
|
105
|
+
:param bool force: Overwrite the destination file even if it exists.
|
|
96
106
|
:raise FileNotFoundError: The *source* does not exist.
|
|
97
107
|
|
|
98
108
|
.. note:: :func:`copyfile` is a no-op if *source* and *dest* are identical.
|
|
@@ -101,11 +111,28 @@ def copyfile(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> No
|
|
|
101
111
|
msg = f'{os.fsdecode(source)} does not exist'
|
|
102
112
|
raise FileNotFoundError(msg)
|
|
103
113
|
|
|
104
|
-
if
|
|
114
|
+
if (
|
|
115
|
+
not (dest_exists := path.exists(dest)) or
|
|
116
|
+
# comparison must be done using shallow=False since
|
|
117
|
+
# two different files might have the same size
|
|
118
|
+
not filecmp.cmp(source, dest, shallow=False)
|
|
119
|
+
):
|
|
120
|
+
if not force and dest_exists:
|
|
121
|
+
# sphinx.util.logging imports sphinx.util.osutil,
|
|
122
|
+
# so use a local import to avoid circular imports
|
|
123
|
+
from sphinx.util import logging
|
|
124
|
+
logger = logging.getLogger(__name__)
|
|
125
|
+
|
|
126
|
+
msg = __('Aborted attempted copy from %s to %s '
|
|
127
|
+
'(the destination path has existing data).')
|
|
128
|
+
logger.warning(msg, os.fsdecode(source), os.fsdecode(dest),
|
|
129
|
+
type='misc', subtype='copy_overwrite')
|
|
130
|
+
return
|
|
131
|
+
|
|
105
132
|
shutil.copyfile(source, dest)
|
|
106
133
|
with contextlib.suppress(OSError):
|
|
107
134
|
# don't do full copystat because the source may be read-only
|
|
108
|
-
|
|
135
|
+
_copy_times(source, dest)
|
|
109
136
|
|
|
110
137
|
|
|
111
138
|
_no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
|
|
@@ -161,12 +188,8 @@ class _chdir:
|
|
|
161
188
|
os.chdir(self._dirs.pop())
|
|
162
189
|
|
|
163
190
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if sys.version_info[:2] >= (3, 11):
|
|
167
|
-
_deprecation_warning(__name__, 'cd', 'contextlib.chdir', remove=(8, 0))
|
|
168
|
-
with _chdir(target_dir):
|
|
169
|
-
yield
|
|
191
|
+
if sys.version_info[:2] < (3, 11):
|
|
192
|
+
cd = _chdir
|
|
170
193
|
|
|
171
194
|
|
|
172
195
|
class FileAvoidWrite:
|
sphinx/util/parallel.py
CHANGED
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import time
|
|
7
7
|
import traceback
|
|
8
8
|
from math import sqrt
|
|
9
|
-
from typing import TYPE_CHECKING, Any
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
10
|
|
|
11
11
|
try:
|
|
12
12
|
import multiprocessing
|
|
@@ -18,7 +18,7 @@ from sphinx.errors import SphinxParallelError
|
|
|
18
18
|
from sphinx.util import logging
|
|
19
19
|
|
|
20
20
|
if TYPE_CHECKING:
|
|
21
|
-
from collections.abc import Sequence
|
|
21
|
+
from collections.abc import Callable, Sequence
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
sphinx/util/requests.py
CHANGED
|
@@ -19,7 +19,7 @@ def _get_tls_cacert(url: str, certs: str | dict[str, str] | None) -> str | bool:
|
|
|
19
19
|
"""Get additional CA cert for a specific URL."""
|
|
20
20
|
if not certs:
|
|
21
21
|
return True
|
|
22
|
-
elif isinstance(certs,
|
|
22
|
+
elif isinstance(certs, str | tuple):
|
|
23
23
|
return certs
|
|
24
24
|
else:
|
|
25
25
|
hostname = urlsplit(url).netloc
|
sphinx/util/template.py
CHANGED
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import os
|
|
6
6
|
from functools import partial
|
|
7
7
|
from os import path
|
|
8
|
-
from typing import TYPE_CHECKING, Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
|
10
10
|
from jinja2 import TemplateNotFound
|
|
11
11
|
from jinja2.loaders import BaseLoader
|
|
@@ -17,7 +17,7 @@ from sphinx.locale import get_translator
|
|
|
17
17
|
from sphinx.util import rst, texescape
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
|
-
from collections.abc import Sequence
|
|
20
|
+
from collections.abc import Callable, Sequence
|
|
21
21
|
|
|
22
22
|
from jinja2.environment import Environment
|
|
23
23
|
|
|
@@ -38,7 +38,7 @@ class BaseRenderer:
|
|
|
38
38
|
|
|
39
39
|
class FileRenderer(BaseRenderer):
|
|
40
40
|
def __init__(self, search_path: Sequence[str | os.PathLike[str]]) -> None:
|
|
41
|
-
if isinstance(search_path,
|
|
41
|
+
if isinstance(search_path, str | os.PathLike):
|
|
42
42
|
search_path = [search_path]
|
|
43
43
|
else:
|
|
44
44
|
# filter "None" paths
|