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.

Files changed (115) 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 +90 -59
  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/__init__.py +14 -9
  19. sphinx/directives/other.py +2 -3
  20. sphinx/directives/patches.py +2 -2
  21. sphinx/domains/__init__.py +4 -2
  22. sphinx/domains/c/__init__.py +2 -2
  23. sphinx/domains/c/_ast.py +3 -2
  24. sphinx/domains/c/_parser.py +4 -3
  25. sphinx/domains/cpp/__init__.py +2 -2
  26. sphinx/domains/cpp/_ast.py +1 -2
  27. sphinx/domains/cpp/_parser.py +2 -2
  28. sphinx/domains/cpp/_symbol.py +2 -2
  29. sphinx/domains/javascript.py +1 -1
  30. sphinx/domains/math.py +1 -1
  31. sphinx/domains/python/__init__.py +1 -1
  32. sphinx/domains/python/_annotations.py +23 -1
  33. sphinx/domains/python/_object.py +0 -1
  34. sphinx/domains/std/__init__.py +7 -8
  35. sphinx/environment/__init__.py +14 -32
  36. sphinx/environment/adapters/indexentries.py +4 -6
  37. sphinx/environment/adapters/toctree.py +4 -4
  38. sphinx/environment/collectors/title.py +1 -1
  39. sphinx/environment/collectors/toctree.py +1 -1
  40. sphinx/events.py +3 -1
  41. sphinx/ext/autodoc/__init__.py +25 -67
  42. sphinx/ext/autodoc/directive.py +7 -5
  43. sphinx/ext/autodoc/importer.py +2 -1
  44. sphinx/ext/autodoc/preserve_defaults.py +2 -2
  45. sphinx/ext/autosummary/__init__.py +15 -7
  46. sphinx/ext/autosummary/generate.py +5 -4
  47. sphinx/ext/doctest.py +5 -5
  48. sphinx/ext/graphviz.py +1 -1
  49. sphinx/ext/imgmath.py +1 -1
  50. sphinx/ext/inheritance_diagram.py +1 -1
  51. sphinx/ext/intersphinx/__init__.py +25 -5
  52. sphinx/ext/intersphinx/_cli.py +7 -6
  53. sphinx/ext/intersphinx/_load.py +240 -115
  54. sphinx/ext/intersphinx/_resolve.py +12 -11
  55. sphinx/ext/intersphinx/_shared.py +102 -9
  56. sphinx/ext/mathjax.py +1 -1
  57. sphinx/ext/napoleon/docstring.py +2 -2
  58. sphinx/ext/todo.py +2 -2
  59. sphinx/ext/viewcode.py +2 -1
  60. sphinx/highlighting.py +3 -3
  61. sphinx/io.py +2 -2
  62. sphinx/jinja2glue.py +13 -6
  63. sphinx/locale/__init__.py +4 -3
  64. sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
  65. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  66. sphinx/locale/ta/LC_MESSAGES/sphinx.po +1578 -1843
  67. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +496 -704
  68. sphinx/project.py +23 -19
  69. sphinx/pycode/ast.py +2 -2
  70. sphinx/pycode/parser.py +2 -2
  71. sphinx/pygments_styles.py +3 -3
  72. sphinx/registry.py +3 -8
  73. sphinx/search/__init__.py +1 -1
  74. sphinx/testing/path.py +2 -1
  75. sphinx/testing/util.py +1 -1
  76. sphinx/texinputs/Makefile.jinja +2 -1
  77. sphinx/texinputs_win/Makefile.jinja +2 -1
  78. sphinx/theming.py +3 -12
  79. sphinx/transforms/__init__.py +5 -5
  80. sphinx/transforms/references.py +1 -1
  81. sphinx/util/__init__.py +11 -35
  82. sphinx/util/_timestamps.py +12 -0
  83. sphinx/util/cfamily.py +5 -5
  84. sphinx/util/console.py +4 -3
  85. sphinx/util/display.py +3 -3
  86. sphinx/util/docfields.py +1 -1
  87. sphinx/util/docutils.py +44 -10
  88. sphinx/util/fileutil.py +41 -9
  89. sphinx/util/i18n.py +9 -4
  90. sphinx/util/images.py +3 -2
  91. sphinx/util/inspect.py +29 -44
  92. sphinx/util/inventory.py +2 -2
  93. sphinx/util/matching.py +2 -2
  94. sphinx/util/math.py +1 -1
  95. sphinx/util/nodes.py +8 -8
  96. sphinx/util/osutil.py +46 -23
  97. sphinx/util/parallel.py +2 -2
  98. sphinx/util/requests.py +1 -1
  99. sphinx/util/template.py +3 -3
  100. sphinx/util/typing.py +67 -70
  101. sphinx/writers/html.py +1 -1
  102. sphinx/writers/html5.py +1 -1
  103. sphinx/writers/latex.py +4 -4
  104. sphinx/writers/manpage.py +2 -2
  105. sphinx/writers/texinfo.py +5 -5
  106. sphinx/writers/text.py +4 -4
  107. sphinx/writers/xml.py +2 -2
  108. {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/METADATA +10 -9
  109. {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/RECORD +112 -114
  110. sphinx/templates/quickstart/Makefile.jinja +0 -98
  111. sphinx/templates/quickstart/make.bat.jinja +0 -110
  112. sphinx/util/_pathlib.py +0 -120
  113. {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/LICENSE.rst +0 -0
  114. {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/WHEEL +0 -0
  115. {sphinx-7.4.6.dist-info → sphinx-8.0.0rc1.dist-info}/entry_points.txt +0 -0
sphinx/util/typing.py CHANGED
@@ -2,18 +2,19 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import dataclasses
5
6
  import sys
6
7
  import types
7
8
  import typing
8
- from collections.abc import Sequence
9
+ from collections.abc import Callable, Sequence
9
10
  from contextvars import Context, ContextVar, Token
10
11
  from struct import Struct
11
12
  from typing import (
12
13
  TYPE_CHECKING,
13
14
  Annotated,
14
15
  Any,
15
- Callable,
16
16
  ForwardRef,
17
+ NewType,
17
18
  TypedDict,
18
19
  TypeVar,
19
20
  Union,
@@ -24,9 +25,9 @@ from docutils.parsers.rst.states import Inliner
24
25
 
25
26
  if TYPE_CHECKING:
26
27
  from collections.abc import Mapping
27
- from typing import Final, Literal, Protocol
28
+ from typing import Final, Literal, Protocol, TypeAlias
28
29
 
29
- from typing_extensions import TypeAlias, TypeIs
30
+ from typing_extensions import TypeIs
30
31
 
31
32
  from sphinx.application import Sphinx
32
33
 
@@ -40,10 +41,6 @@ if TYPE_CHECKING:
40
41
  'smart',
41
42
  ]
42
43
 
43
- if sys.version_info >= (3, 10):
44
- from types import UnionType
45
- else:
46
- UnionType = None
47
44
 
48
45
  # classes that have an incorrect .__module__ attribute
49
46
  _INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = {
@@ -84,13 +81,10 @@ def is_invalid_builtin_class(obj: Any) -> bool:
84
81
 
85
82
 
86
83
  # Text like nodes which are initialized with text and rawsource
87
- TextlikeNode = Union[nodes.Text, nodes.TextElement]
88
-
89
- # type of None
90
- NoneType = type(None)
84
+ TextlikeNode: TypeAlias = nodes.Text | nodes.TextElement
91
85
 
92
86
  # path matcher
93
- PathMatcher = Callable[[str], bool]
87
+ PathMatcher: TypeAlias = Callable[[str], bool]
94
88
 
95
89
  # common role functions
96
90
  if TYPE_CHECKING:
@@ -108,25 +102,25 @@ if TYPE_CHECKING:
108
102
  ) -> tuple[list[nodes.Node], list[nodes.system_message]]:
109
103
  ...
110
104
  else:
111
- RoleFunction = Callable[
105
+ RoleFunction: TypeAlias = Callable[
112
106
  [str, str, str, int, Inliner, dict[str, Any], Sequence[str]],
113
107
  tuple[list[nodes.Node], list[nodes.system_message]],
114
108
  ]
115
109
 
116
110
  # A option spec for directive
117
- OptionSpec = dict[str, Callable[[str], Any]]
111
+ OptionSpec: TypeAlias = dict[str, Callable[[str], Any]]
118
112
 
119
113
  # title getter functions for enumerable nodes (see sphinx.domains.std)
120
- TitleGetter = Callable[[nodes.Node], str]
114
+ TitleGetter: TypeAlias = Callable[[nodes.Node], str]
121
115
 
122
116
  # inventory data on memory
123
- InventoryItem = tuple[
117
+ InventoryItem: TypeAlias = tuple[
124
118
  str, # project name
125
119
  str, # project version
126
120
  str, # URL
127
121
  str, # display name
128
122
  ]
129
- Inventory = dict[str, dict[str, InventoryItem]]
123
+ Inventory: TypeAlias = dict[str, dict[str, InventoryItem]]
130
124
 
131
125
 
132
126
  class ExtensionMetadata(TypedDict, total=False):
@@ -150,13 +144,14 @@ class ExtensionMetadata(TypedDict, total=False):
150
144
 
151
145
 
152
146
  if TYPE_CHECKING:
153
- _ExtensionSetupFunc = Callable[[Sphinx], ExtensionMetadata]
147
+ _ExtensionSetupFunc: TypeAlias = Callable[[Sphinx], ExtensionMetadata]
154
148
 
155
149
 
156
150
  def get_type_hints(
157
151
  obj: Any,
158
152
  globalns: dict[str, Any] | None = None,
159
153
  localns: dict[str, Any] | None = None,
154
+ include_extras: bool = False,
160
155
  ) -> dict[str, Any]:
161
156
  """Return a dictionary containing type hints for a function, method, module or class
162
157
  object.
@@ -167,7 +162,7 @@ def get_type_hints(
167
162
  from sphinx.util.inspect import safe_getattr # lazy loading
168
163
 
169
164
  try:
170
- return typing.get_type_hints(obj, globalns, localns)
165
+ return typing.get_type_hints(obj, globalns, localns, include_extras=include_extras)
171
166
  except NameError:
172
167
  # Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
173
168
  return safe_getattr(obj, '__annotations__', {})
@@ -202,24 +197,14 @@ def _is_unpack_form(obj: Any) -> bool:
202
197
  # that typing_extensions.Unpack should not be used in that case
203
198
  return typing.get_origin(obj) is Unpack
204
199
 
205
- # 3.9 and 3.10 require typing_extensions.Unpack
200
+ # Python 3.10 requires typing_extensions.Unpack
206
201
  origin = typing.get_origin(obj)
207
202
  return (
208
203
  getattr(origin, '__module__', None) == 'typing_extensions'
209
- and _typing_internal_name(origin) == 'Unpack'
204
+ and origin.__name__ == 'Unpack'
210
205
  )
211
206
 
212
207
 
213
- def _typing_internal_name(obj: Any) -> str | None:
214
- if sys.version_info[:2] >= (3, 10):
215
- try:
216
- return obj.__name__
217
- except AttributeError:
218
- # e.g. ParamSpecArgs, ParamSpecKwargs
219
- return ''
220
- return getattr(obj, '_name', None)
221
-
222
-
223
208
  def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> str:
224
209
  """Convert a type-like object to a reST reference.
225
210
 
@@ -232,7 +217,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
232
217
  Show the name of the annotation.
233
218
  """
234
219
  from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading
235
- from sphinx.util import inspect # lazy loading
220
+ from sphinx.util.inspect import isgenericalias, object_description # lazy loading
236
221
 
237
222
  valid_modes = {'fully-qualified-except-typing', 'smart'}
238
223
  if mode not in valid_modes:
@@ -241,7 +226,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
241
226
  raise ValueError(msg)
242
227
 
243
228
  # things that are not types
244
- if cls is None or cls == NoneType:
229
+ if cls is None or cls == types.NoneType:
245
230
  return ':py:obj:`None`'
246
231
  if cls is Ellipsis:
247
232
  return '...'
@@ -267,18 +252,28 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
267
252
  return f':py:class:`{module_prefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
268
253
  elif _is_annotated_form(cls):
269
254
  args = restify(cls.__args__[0], mode)
270
- meta = ', '.join(map(repr, cls.__metadata__))
255
+ meta_args = []
256
+ for m in cls.__metadata__:
257
+ if isinstance(m, type):
258
+ meta_args.append(restify(m, mode))
259
+ elif dataclasses.is_dataclass(m):
260
+ # use restify for the repr of field values rather than repr
261
+ d_fields = ', '.join([
262
+ fr"{f.name}=\ {restify(getattr(m, f.name), mode)}"
263
+ for f in dataclasses.fields(m) if f.repr
264
+ ])
265
+ meta_args.append(fr'{restify(type(m), mode)}\ ({d_fields})')
266
+ else:
267
+ meta_args.append(repr(m))
268
+ meta = ', '.join(meta_args)
271
269
  if sys.version_info[:2] <= (3, 11):
272
270
  # Hardcoded to fix errors on Python 3.11 and earlier.
273
271
  return fr':py:class:`~typing.Annotated`\ [{args}, {meta}]'
274
272
  return (f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
275
273
  fr'\ [{args}, {meta}]')
276
- elif inspect.isNewType(cls):
277
- if sys.version_info[:2] >= (3, 10):
278
- # newtypes have correct module info since Python 3.10+
279
- return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
280
- return f':py:class:`{cls.__name__}`'
281
- elif UnionType and isinstance(cls, UnionType):
274
+ elif isinstance(cls, NewType):
275
+ return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`' # type: ignore[attr-defined]
276
+ elif isinstance(cls, types.UnionType):
282
277
  # Union types (PEP 585) retain their definition order when they
283
278
  # are printed natively and ``None``-like types are kept as is.
284
279
  return ' | '.join(restify(a, mode) for a in cls.__args__)
@@ -290,23 +285,18 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
290
285
  concatenated_args = ', '.join(restify(arg, mode) for arg in cls.__args__)
291
286
  return fr':py:class:`{cls.__name__}`\ [{concatenated_args}]'
292
287
  return f':py:class:`{cls.__name__}`'
293
- elif (inspect.isgenericalias(cls)
288
+ elif (isgenericalias(cls)
294
289
  and cls_module_is_typing
295
290
  and cls.__origin__ is Union):
296
291
  # *cls* is defined in ``typing``, and thus ``__args__`` must exist
297
292
  return ' | '.join(restify(a, mode) for a in cls.__args__)
298
- elif inspect.isgenericalias(cls):
299
- # A generic alias always has an __origin__, but it is difficult to
300
- # use a type guard on inspect.isgenericalias()
301
- # (ideally, we would use ``TypeIs`` introduced in Python 3.13).
302
- cls_name = _typing_internal_name(cls)
303
-
293
+ elif isgenericalias(cls):
304
294
  if isinstance(cls.__origin__, typing._SpecialForm):
305
295
  # ClassVar; Concatenate; Final; Literal; Unpack; TypeGuard; TypeIs
306
296
  # Required/NotRequired
307
297
  text = restify(cls.__origin__, mode)
308
- elif cls_name:
309
- text = f':py:class:`{module_prefix}{cls.__module__}.{cls_name}`'
298
+ elif cls.__name__:
299
+ text = f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
310
300
  else:
311
301
  text = restify(cls.__origin__, mode)
312
302
 
@@ -319,14 +309,14 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
319
309
 
320
310
  # Callable has special formatting
321
311
  if (
322
- (cls_module_is_typing and _typing_internal_name(cls) == 'Callable')
312
+ (cls_module_is_typing and cls.__name__ == 'Callable')
323
313
  or (cls.__module__ == 'collections.abc' and cls.__name__ == 'Callable')
324
314
  ):
325
315
  args = ', '.join(restify(a, mode) for a in __args__[:-1])
326
316
  returns = restify(__args__[-1], mode)
327
317
  return fr'{text}\ [[{args}], {returns}]'
328
318
 
329
- if cls_module_is_typing and _typing_internal_name(cls.__origin__) == 'Literal':
319
+ if cls_module_is_typing and cls.__origin__.__name__ == 'Literal':
330
320
  args = ', '.join(_format_literal_arg_restify(a, mode=mode)
331
321
  for a in cls.__args__)
332
322
  return fr'{text}\ [{args}]'
@@ -335,8 +325,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
335
325
  args = ', '.join(restify(a, mode) for a in __args__)
336
326
  return fr'{text}\ [{args}]'
337
327
  elif isinstance(cls, typing._SpecialForm):
338
- cls_name = _typing_internal_name(cls)
339
- return f':py:obj:`~{cls.__module__}.{cls_name}`'
328
+ return f':py:obj:`~{cls.__module__}.{cls.__name__}`' # type: ignore[attr-defined]
340
329
  elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
341
330
  # handle bpo-46998
342
331
  return f':py:obj:`~{cls.__module__}.{cls.__name__}`'
@@ -348,7 +337,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
348
337
  # not a class (ex. TypeVar) but should have a __name__
349
338
  return f':py:obj:`{module_prefix}{cls.__module__}.{cls.__name__}`'
350
339
  except (AttributeError, TypeError):
351
- return inspect.object_description(cls)
340
+ return object_description(cls)
352
341
 
353
342
 
354
343
  def _format_literal_arg_restify(arg: Any, /, *, mode: str) -> str:
@@ -383,7 +372,6 @@ def stringify_annotation(
383
372
  Show the module name and qualified name of the annotation.
384
373
  """
385
374
  from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading
386
- from sphinx.util.inspect import isNewType # lazy loading
387
375
 
388
376
  valid_modes = {'fully-qualified-except-typing', 'fully-qualified', 'smart'}
389
377
  if mode not in valid_modes:
@@ -392,7 +380,7 @@ def stringify_annotation(
392
380
  raise ValueError(msg)
393
381
 
394
382
  # things that are not types
395
- if annotation is None or annotation == NoneType:
383
+ if annotation is None or annotation == types.NoneType:
396
384
  return 'None'
397
385
  if annotation is Ellipsis:
398
386
  return '...'
@@ -418,18 +406,15 @@ def stringify_annotation(
418
406
  if annotation_module_is_typing and mode in {'fully-qualified-except-typing', 'smart'}:
419
407
  return annotation_name
420
408
  return module_prefix + f'{annotation_module}.{annotation_name}'
421
- elif isNewType(annotation):
422
- if sys.version_info[:2] >= (3, 10):
423
- # newtypes have correct module info since Python 3.10+
424
- return module_prefix + f'{annotation_module}.{annotation_name}'
425
- return annotation_name
409
+ elif isinstance(annotation, NewType):
410
+ return module_prefix + f'{annotation_module}.{annotation_name}'
426
411
  elif ismockmodule(annotation):
427
412
  return module_prefix + annotation_name
428
413
  elif ismock(annotation):
429
414
  return module_prefix + f'{annotation_module}.{annotation_name}'
430
415
  elif is_invalid_builtin_class(annotation):
431
416
  return module_prefix + _INVALID_BUILTIN_CLASSES[annotation]
432
- elif _is_annotated_form(annotation): # for py39+
417
+ elif _is_annotated_form(annotation): # for py310+
433
418
  pass
434
419
  elif annotation_module == 'builtins' and annotation_qualname:
435
420
  args = getattr(annotation, '__args__', None)
@@ -463,8 +448,8 @@ def stringify_annotation(
463
448
  # handle ForwardRefs
464
449
  qualname = annotation_forward_arg
465
450
  else:
466
- if internal_name := _typing_internal_name(annotation):
467
- qualname = internal_name
451
+ if annotation_name:
452
+ qualname = annotation_name
468
453
  elif annotation_qualname:
469
454
  qualname = annotation_qualname
470
455
  else:
@@ -478,7 +463,7 @@ def stringify_annotation(
478
463
  elif hasattr(annotation, '__origin__'):
479
464
  # instantiated generic provided by a user
480
465
  qualname = stringify_annotation(annotation.__origin__, mode)
481
- elif UnionType and isinstance(annotation, UnionType): # types.UnionType (for py3.10+)
466
+ elif isinstance(annotation, types.UnionType):
482
467
  qualname = 'types.UnionType'
483
468
  else:
484
469
  # we weren't able to extract the base type, appending arguments would
@@ -488,7 +473,7 @@ def stringify_annotation(
488
473
  # Process the generic arguments (if any).
489
474
  # They must be a list or a tuple, otherwise they are considered 'broken'.
490
475
  annotation_args = getattr(annotation, '__args__', ())
491
- if annotation_args and isinstance(annotation_args, (list, tuple)):
476
+ if annotation_args and isinstance(annotation_args, list | tuple):
492
477
  if (
493
478
  qualname in {'Union', 'types.UnionType'}
494
479
  and all(getattr(a, '__origin__', ...) is typing.Literal for a in annotation_args)
@@ -508,9 +493,22 @@ def stringify_annotation(
508
493
  args = ', '.join(_format_literal_arg_stringify(a, mode=mode)
509
494
  for a in annotation_args)
510
495
  return f'{module_prefix}Literal[{args}]'
511
- elif _is_annotated_form(annotation): # for py39+
496
+ elif _is_annotated_form(annotation): # for py310+
512
497
  args = stringify_annotation(annotation_args[0], mode)
513
- meta = ', '.join(map(repr, annotation.__metadata__))
498
+ meta_args = []
499
+ for m in annotation.__metadata__:
500
+ if isinstance(m, type):
501
+ meta_args.append(stringify_annotation(m, mode))
502
+ elif dataclasses.is_dataclass(m):
503
+ # use stringify_annotation for the repr of field values rather than repr
504
+ d_fields = ', '.join([
505
+ f"{f.name}={stringify_annotation(getattr(m, f.name), mode)}"
506
+ for f in dataclasses.fields(m) if f.repr
507
+ ])
508
+ meta_args.append(f'{stringify_annotation(type(m), mode)}({d_fields})')
509
+ else:
510
+ meta_args.append(repr(m))
511
+ meta = ', '.join(meta_args)
514
512
  if sys.version_info[:2] <= (3, 11):
515
513
  if mode == 'fully-qualified-except-typing':
516
514
  return f'Annotated[{args}, {meta}]'
@@ -542,7 +540,6 @@ def _format_literal_arg_stringify(arg: Any, /, *, mode: str) -> str:
542
540
 
543
541
  # deprecated name -> (object to return, canonical path or empty string, removal version)
544
542
  _DEPRECATED_OBJECTS: dict[str, tuple[Any, str, tuple[int, int]]] = {
545
- 'stringify': (stringify_annotation, 'sphinx.util.typing.stringify_annotation', (8, 0)),
546
543
  }
547
544
 
548
545
 
sphinx/writers/html.py CHANGED
@@ -20,7 +20,7 @@ HTMLTranslator = HTML5Translator
20
20
  # https://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
21
21
 
22
22
 
23
- class HTMLWriter(Writer):
23
+ class HTMLWriter(Writer): # type: ignore[misc]
24
24
 
25
25
  # override embed-stylesheet default value to False.
26
26
  settings_default_overrides = {"embed_stylesheet": False}
sphinx/writers/html5.py CHANGED
@@ -43,7 +43,7 @@ def multiply_length(length: str, scale: int) -> str:
43
43
  return f"{int(result)}{unit}"
44
44
 
45
45
 
46
- class HTML5Translator(SphinxTranslator, BaseTranslator):
46
+ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
47
47
  """
48
48
  Our custom HTML translator.
49
49
  """
sphinx/writers/latex.py CHANGED
@@ -66,7 +66,7 @@ class UnsupportedError(SphinxError):
66
66
  category = 'Markup is unsupported in LaTeX'
67
67
 
68
68
 
69
- class LaTeXWriter(writers.Writer):
69
+ class LaTeXWriter(writers.Writer): # type: ignore[misc]
70
70
 
71
71
  supported = ('sphinxlatex',)
72
72
 
@@ -1369,7 +1369,7 @@ class LaTeXTranslator(SphinxTranslator):
1369
1369
  not isinstance(node.parent[index - 1], nodes.compound)):
1370
1370
  # insert blank line, if the paragraph follows a non-paragraph node in a compound
1371
1371
  self.body.append(r'\noindent' + CR)
1372
- elif index == 1 and isinstance(node.parent, (nodes.footnote, footnotetext)):
1372
+ elif index == 1 and isinstance(node.parent, nodes.footnote | footnotetext):
1373
1373
  # don't insert blank line, if the paragraph is second child of a footnote
1374
1374
  # (first one is label node)
1375
1375
  pass
@@ -2081,7 +2081,7 @@ class LaTeXTranslator(SphinxTranslator):
2081
2081
  done = 0
2082
2082
  if len(node.children) == 1:
2083
2083
  child = node.children[0]
2084
- if isinstance(child, (nodes.bullet_list, nodes.enumerated_list)):
2084
+ if isinstance(child, nodes.bullet_list | nodes.enumerated_list):
2085
2085
  done = 1
2086
2086
  if not done:
2087
2087
  self.body.append(r'\begin{quote}' + CR)
@@ -2092,7 +2092,7 @@ class LaTeXTranslator(SphinxTranslator):
2092
2092
  done = 0
2093
2093
  if len(node.children) == 1:
2094
2094
  child = node.children[0]
2095
- if isinstance(child, (nodes.bullet_list, nodes.enumerated_list)):
2095
+ if isinstance(child, nodes.bullet_list | nodes.enumerated_list):
2096
2096
  done = 1
2097
2097
  if not done:
2098
2098
  self.body.append(r'\end{quote}' + CR)
sphinx/writers/manpage.py CHANGED
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
24
24
  logger = logging.getLogger(__name__)
25
25
 
26
26
 
27
- class ManualPageWriter(Writer):
27
+ class ManualPageWriter(Writer): # type: ignore[misc]
28
28
  def __init__(self, builder: Builder) -> None:
29
29
  super().__init__()
30
30
  self.builder = builder
@@ -70,7 +70,7 @@ class NestedInlineTransform:
70
70
  node.parent.remove(node)
71
71
 
72
72
 
73
- class ManualPageTranslator(SphinxTranslator, BaseTranslator):
73
+ class ManualPageTranslator(SphinxTranslator, BaseTranslator): # type: ignore[misc]
74
74
  """
75
75
  Custom man page translator.
76
76
  """
sphinx/writers/texinfo.py CHANGED
@@ -105,7 +105,7 @@ def smart_capwords(s: str, sep: str | None = None) -> str:
105
105
  return (sep or ' ').join(words)
106
106
 
107
107
 
108
- class TexinfoWriter(writers.Writer):
108
+ class TexinfoWriter(writers.Writer): # type: ignore[misc]
109
109
  """Texinfo writer for generating Texinfo documents."""
110
110
 
111
111
  supported = ('texinfo', 'texi')
@@ -297,7 +297,7 @@ class TexinfoTranslator(SphinxTranslator):
297
297
  # try to find a suitable "Top" node
298
298
  title = self.document.next_node(nodes.title)
299
299
  top = title.parent if title else self.document
300
- if not isinstance(top, (nodes.document, nodes.section)):
300
+ if not isinstance(top, nodes.document | nodes.section):
301
301
  top = self.document
302
302
  if top is not self.document:
303
303
  entries = node_menus[top['node_name']]
@@ -625,7 +625,7 @@ class TexinfoTranslator(SphinxTranslator):
625
625
  parent = node.parent
626
626
  if isinstance(parent, nodes.table):
627
627
  return
628
- if isinstance(parent, (nodes.Admonition, nodes.sidebar, nodes.topic)):
628
+ if isinstance(parent, nodes.Admonition | nodes.sidebar | nodes.topic):
629
629
  raise nodes.SkipNode
630
630
  if not isinstance(parent, nodes.section):
631
631
  logger.warning(__('encountered title node not in section, topic, table, '
@@ -694,7 +694,7 @@ class TexinfoTranslator(SphinxTranslator):
694
694
  def visit_reference(self, node: Element) -> None:
695
695
  # an xref's target is displayed in Info so we ignore a few
696
696
  # cases for the sake of appearance
697
- if isinstance(node.parent, (nodes.title, addnodes.desc_type)):
697
+ if isinstance(node.parent, nodes.title | addnodes.desc_type):
698
698
  return
699
699
  if len(node) != 0 and isinstance(node[0], nodes.image):
700
700
  return
@@ -987,7 +987,7 @@ class TexinfoTranslator(SphinxTranslator):
987
987
  self.add_anchor(id, node)
988
988
  # anchors and indexes need to go in front
989
989
  for n in node[::]:
990
- if isinstance(n, (addnodes.index, nodes.target)):
990
+ if isinstance(n, addnodes.index | nodes.target):
991
991
  n.walkabout(self)
992
992
  node.remove(n)
993
993
  self.body.append('\n%s ' % self.at_item_x)
sphinx/writers/text.py CHANGED
@@ -6,7 +6,7 @@ import os
6
6
  import re
7
7
  import textwrap
8
8
  from collections.abc import Iterable, Iterator, Sequence
9
- from itertools import chain, groupby
9
+ from itertools import chain, groupby, pairwise
10
10
  from typing import TYPE_CHECKING, Any, cast
11
11
 
12
12
  from docutils import nodes, writers
@@ -221,10 +221,10 @@ class Table:
221
221
  tail = "+" if out[-1][0] == "-" else "|"
222
222
  glue = [
223
223
  "+" if left[0] == "-" or right[0] == "-" else "|"
224
- for left, right in zip(out, out[1:])
224
+ for left, right in pairwise(out)
225
225
  ]
226
226
  glue.append(tail)
227
- return head + "".join(chain.from_iterable(zip(out, glue)))
227
+ return head + "".join(chain.from_iterable(zip(out, glue, strict=False)))
228
228
 
229
229
  for lineno, line in enumerate(self.lines):
230
230
  if self.separator and lineno == self.separator:
@@ -356,7 +356,7 @@ def my_wrap(text: str, width: int = MAXWIDTH, **kwargs: Any) -> list[str]:
356
356
  return w.wrap(text)
357
357
 
358
358
 
359
- class TextWriter(writers.Writer):
359
+ class TextWriter(writers.Writer): # type: ignore[misc]
360
360
  supported = ('text',)
361
361
  settings_spec = ('No options here.', '', ())
362
362
  settings_defaults: dict[str, Any] = {}
sphinx/writers/xml.py CHANGED
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
10
10
  from sphinx.builders import Builder
11
11
 
12
12
 
13
- class XMLWriter(BaseXMLWriter):
13
+ class XMLWriter(BaseXMLWriter): # type: ignore[misc]
14
14
  output: str
15
15
 
16
16
  def __init__(self, builder: Builder) -> None:
@@ -29,7 +29,7 @@ class XMLWriter(BaseXMLWriter):
29
29
  return super().translate()
30
30
 
31
31
 
32
- class PseudoXMLWriter(BaseXMLWriter):
32
+ class PseudoXMLWriter(BaseXMLWriter): # type: ignore[misc]
33
33
 
34
34
  supported = ('pprint', 'pformat', 'pseudoxml')
35
35
  """Formats this writer supports."""
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Sphinx
3
- Version: 7.4.6
3
+ Version: 8.0.0rc1
4
4
  Summary: Python documentation generator
5
5
  Author-email: Georg Brandl <georg@python.org>
6
- Requires-Python: >=3.9
6
+ Requires-Python: >=3.10
7
7
  Description-Content-Type: text/x-rst
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
9
  Classifier: Environment :: Console
@@ -18,7 +18,6 @@ Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: Python
19
19
  Classifier: Programming Language :: Python :: 3
20
20
  Classifier: Programming Language :: Python :: 3 :: Only
21
- Classifier: Programming Language :: Python :: 3.9
22
21
  Classifier: Programming Language :: Python :: 3.10
23
22
  Classifier: Programming Language :: Python :: 3.11
24
23
  Classifier: Programming Language :: Python :: 3.12
@@ -57,17 +56,19 @@ Requires-Dist: alabaster~=0.7.14
57
56
  Requires-Dist: imagesize>=1.3
58
57
  Requires-Dist: requests>=2.30.0
59
58
  Requires-Dist: packaging>=23.0
60
- Requires-Dist: importlib-metadata>=6.0; python_version < '3.10'
61
59
  Requires-Dist: tomli>=2; python_version < '3.11'
62
60
  Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
63
61
  Requires-Dist: sphinxcontrib-websupport ; extra == "docs"
64
62
  Requires-Dist: flake8>=6.0 ; extra == "lint"
65
- Requires-Dist: ruff==0.5.2 ; extra == "lint"
66
- Requires-Dist: mypy==1.10.1 ; extra == "lint"
63
+ Requires-Dist: ruff==0.5.4 ; extra == "lint"
64
+ Requires-Dist: mypy==1.11.0 ; extra == "lint"
67
65
  Requires-Dist: sphinx-lint>=0.9 ; extra == "lint"
68
- Requires-Dist: types-docutils==0.21.0.20240711 ; extra == "lint"
66
+ Requires-Dist: types-colorama==0.4.15.20240311 ; extra == "lint"
67
+ Requires-Dist: types-defusedxml==0.7.0.20240218 ; extra == "lint"
68
+ Requires-Dist: types-docutils==0.21.0.20240724 ; extra == "lint"
69
+ Requires-Dist: types-Pillow==10.2.0.20240520 ; extra == "lint"
70
+ Requires-Dist: types-Pygments==2.18.0.20240506 ; extra == "lint"
69
71
  Requires-Dist: types-requests>=2.30.0 ; extra == "lint"
70
- Requires-Dist: importlib-metadata>=6.0 ; extra == "lint"
71
72
  Requires-Dist: tomli>=2 ; extra == "lint"
72
73
  Requires-Dist: pytest>=6.0 ; extra == "lint"
73
74
  Requires-Dist: pytest>=8.0 ; extra == "test"
@@ -134,7 +135,7 @@ Installation
134
135
  The following command installs Sphinx from the `Python Package Index`_. You will
135
136
  need a working installation of Python and pip.
136
137
 
137
- .. code-block:: sh
138
+ .. code-block:: shell
138
139
 
139
140
  pip install -U sphinx
140
141