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.

Files changed (107) hide show
  1. sphinx/__init__.py +2 -2
  2. sphinx/_cli/__init__.py +4 -4
  3. sphinx/application.py +7 -7
  4. sphinx/builders/__init__.py +2 -3
  5. sphinx/builders/_epub_base.py +33 -12
  6. sphinx/builders/changes.py +13 -5
  7. sphinx/builders/epub3.py +6 -2
  8. sphinx/builders/html/__init__.py +88 -58
  9. sphinx/builders/latex/__init__.py +38 -12
  10. sphinx/builders/latex/transforms.py +1 -1
  11. sphinx/builders/linkcheck.py +8 -49
  12. sphinx/builders/texinfo.py +12 -6
  13. sphinx/builders/text.py +7 -3
  14. sphinx/builders/xml.py +7 -3
  15. sphinx/cmd/quickstart.py +10 -20
  16. sphinx/config.py +12 -12
  17. sphinx/deprecation.py +8 -8
  18. sphinx/directives/other.py +2 -3
  19. sphinx/directives/patches.py +2 -2
  20. sphinx/domains/__init__.py +4 -2
  21. sphinx/domains/c/__init__.py +2 -2
  22. sphinx/domains/c/_ast.py +3 -2
  23. sphinx/domains/c/_parser.py +4 -3
  24. sphinx/domains/cpp/__init__.py +2 -2
  25. sphinx/domains/cpp/_ast.py +1 -2
  26. sphinx/domains/cpp/_parser.py +2 -2
  27. sphinx/domains/cpp/_symbol.py +2 -2
  28. sphinx/domains/math.py +1 -1
  29. sphinx/domains/python/_object.py +0 -1
  30. sphinx/domains/std/__init__.py +7 -8
  31. sphinx/environment/__init__.py +14 -32
  32. sphinx/environment/adapters/indexentries.py +4 -6
  33. sphinx/environment/adapters/toctree.py +4 -4
  34. sphinx/environment/collectors/title.py +1 -1
  35. sphinx/environment/collectors/toctree.py +1 -1
  36. sphinx/events.py +3 -1
  37. sphinx/ext/autodoc/__init__.py +17 -63
  38. sphinx/ext/autodoc/directive.py +7 -5
  39. sphinx/ext/autodoc/importer.py +2 -1
  40. sphinx/ext/autodoc/preserve_defaults.py +2 -2
  41. sphinx/ext/autosummary/__init__.py +7 -6
  42. sphinx/ext/autosummary/generate.py +5 -4
  43. sphinx/ext/doctest.py +5 -5
  44. sphinx/ext/graphviz.py +1 -1
  45. sphinx/ext/imgmath.py +1 -1
  46. sphinx/ext/inheritance_diagram.py +1 -1
  47. sphinx/ext/intersphinx/__init__.py +25 -5
  48. sphinx/ext/intersphinx/_cli.py +7 -6
  49. sphinx/ext/intersphinx/_load.py +240 -115
  50. sphinx/ext/intersphinx/_resolve.py +12 -11
  51. sphinx/ext/intersphinx/_shared.py +102 -9
  52. sphinx/ext/mathjax.py +1 -1
  53. sphinx/ext/napoleon/docstring.py +2 -2
  54. sphinx/ext/todo.py +2 -2
  55. sphinx/ext/viewcode.py +2 -1
  56. sphinx/highlighting.py +3 -3
  57. sphinx/io.py +2 -2
  58. sphinx/jinja2glue.py +13 -6
  59. sphinx/locale/__init__.py +4 -3
  60. sphinx/project.py +23 -19
  61. sphinx/pycode/ast.py +2 -2
  62. sphinx/pycode/parser.py +2 -2
  63. sphinx/pygments_styles.py +3 -3
  64. sphinx/registry.py +3 -8
  65. sphinx/search/__init__.py +1 -1
  66. sphinx/testing/path.py +2 -1
  67. sphinx/testing/util.py +1 -1
  68. sphinx/texinputs/Makefile.jinja +2 -1
  69. sphinx/texinputs_win/Makefile.jinja +2 -1
  70. sphinx/theming.py +3 -12
  71. sphinx/transforms/__init__.py +5 -5
  72. sphinx/transforms/references.py +1 -1
  73. sphinx/util/__init__.py +11 -35
  74. sphinx/util/_timestamps.py +12 -0
  75. sphinx/util/cfamily.py +5 -5
  76. sphinx/util/console.py +4 -3
  77. sphinx/util/display.py +3 -3
  78. sphinx/util/docfields.py +1 -1
  79. sphinx/util/docutils.py +44 -10
  80. sphinx/util/fileutil.py +25 -20
  81. sphinx/util/i18n.py +9 -4
  82. sphinx/util/images.py +3 -2
  83. sphinx/util/inspect.py +28 -43
  84. sphinx/util/inventory.py +2 -2
  85. sphinx/util/matching.py +2 -2
  86. sphinx/util/math.py +1 -1
  87. sphinx/util/nodes.py +8 -8
  88. sphinx/util/osutil.py +29 -28
  89. sphinx/util/parallel.py +2 -2
  90. sphinx/util/requests.py +1 -1
  91. sphinx/util/template.py +3 -3
  92. sphinx/util/typing.py +36 -72
  93. sphinx/writers/html.py +1 -1
  94. sphinx/writers/html5.py +1 -1
  95. sphinx/writers/latex.py +4 -4
  96. sphinx/writers/manpage.py +2 -2
  97. sphinx/writers/texinfo.py +5 -5
  98. sphinx/writers/text.py +4 -4
  99. sphinx/writers/xml.py +2 -2
  100. {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/METADATA +10 -9
  101. {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/RECORD +104 -106
  102. sphinx/templates/quickstart/Makefile.jinja +0 -98
  103. sphinx/templates/quickstart/make.bat.jinja +0 -110
  104. sphinx/util/_pathlib.py +0 -120
  105. {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/LICENSE.rst +0 -0
  106. {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/WHEEL +0 -0
  107. {sphinx-7.4.7.dist-info → sphinx-8.0.0rc1.dist-info}/entry_points.txt +0 -0
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 ForwardRef, stringify_annotation
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, Union
30
+ from typing import Final, Protocol, TypeAlias
31
31
 
32
- from typing_extensions import TypeAlias, TypeIs
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 = Union[
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 = Union[
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__, (list, tuple)) and all(isinstance(e, str) for e in __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
- if sys.version_info >= (3, 10, 0) or not isinstance(obj, type):
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__, (list, tuple)):
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, (partial, partialmethod))
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, (property, cached_property))
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, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
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:
@@ -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, Callable
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, Callable
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.builders.html import HTML5Translator
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, Callable, Generic, TypeVar, cast
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 (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
- ))):
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.deprecation import _deprecation_warning
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,32 +71,38 @@ def ensuredir(file: str | os.PathLike[str]) -> None:
72
71
  os.makedirs(file, exist_ok=True)
73
72
 
74
73
 
75
- def mtimes_of_files(dirnames: list[str], suffix: str) -> Iterator[float]:
76
- for dirname in dirnames:
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
- def copytimes(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> None:
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
- if hasattr(os, 'utime'):
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
95
  def copyfile(
92
96
  source: str | os.PathLike[str],
93
97
  dest: str | os.PathLike[str],
94
98
  *,
95
- __overwrite_warning__: bool = True,
99
+ force: bool = False,
96
100
  ) -> None:
97
101
  """Copy a file and its modification times, if possible.
98
102
 
99
103
  :param source: An existing source to copy.
100
104
  :param dest: The destination path.
105
+ :param bool force: Overwrite the destination file even if it exists.
101
106
  :raise FileNotFoundError: The *source* does not exist.
102
107
 
103
108
  .. note:: :func:`copyfile` is a no-op if *source* and *dest* are identical.
@@ -112,22 +117,22 @@ def copyfile(
112
117
  # two different files might have the same size
113
118
  not filecmp.cmp(source, dest, shallow=False)
114
119
  ):
115
- if __overwrite_warning__ and dest_exists:
120
+ if not force and dest_exists:
116
121
  # sphinx.util.logging imports sphinx.util.osutil,
117
122
  # so use a local import to avoid circular imports
118
123
  from sphinx.util import logging
119
124
  logger = logging.getLogger(__name__)
120
125
 
121
- msg = ('Copying the source path %s to %s will overwrite data, '
122
- 'as a file already exists at the destination path '
123
- 'and the content does not match.')
124
- logger.info(msg, os.fsdecode(source), os.fsdecode(dest),
125
- type='misc', subtype='copy_overwrite')
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
126
131
 
127
132
  shutil.copyfile(source, dest)
128
133
  with contextlib.suppress(OSError):
129
134
  # don't do full copystat because the source may be read-only
130
- copytimes(source, dest)
135
+ _copy_times(source, dest)
131
136
 
132
137
 
133
138
  _no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
@@ -183,12 +188,8 @@ class _chdir:
183
188
  os.chdir(self._dirs.pop())
184
189
 
185
190
 
186
- @contextlib.contextmanager
187
- def cd(target_dir: str) -> Iterator[None]:
188
- if sys.version_info[:2] >= (3, 11):
189
- _deprecation_warning(__name__, 'cd', 'contextlib.chdir', remove=(8, 0))
190
- with _chdir(target_dir):
191
- yield
191
+ if sys.version_info[:2] < (3, 11):
192
+ cd = _chdir
192
193
 
193
194
 
194
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, Callable
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, (str, tuple)):
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, Callable
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, (str, os.PathLike)):
41
+ if isinstance(search_path, str | os.PathLike):
42
42
  search_path = [search_path]
43
43
  else:
44
44
  # filter "None" paths