Skip to content

Commit bd5a300

Browse files
committed
Merge branch 'dependabot/pip/types-docutils-0.21.0.20250516' of github.com:sphinx-doc/sphinx into dependabot/pip/types-docutils-0.21.0.20250516
2 parents 5390e21 + fb95c57 commit bd5a300

File tree

22 files changed

+158
-109
lines changed

22 files changed

+158
-109
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@ Features added
2222
* #13497: Support C domain objects in the table of contents.
2323
* #13535: html search: Update to the latest version of Snowball (v3.0.1).
2424
Patch by Adam Turner.
25+
* #13704: autodoc: Detect :py:func:`typing_extensions.overload <typing.overload>`
26+
and :py:func:`~typing.final` decorators.
27+
Patch by Spencer Brown.
2528

2629
Bugs fixed
2730
----------
2831

2932
* #13369: Correctly parse and cross-reference unpacked type annotations.
3033
Patch by Alicia Garcia-Raboso.
34+
* #13528: Add tilde ``~`` prefix support for :rst:role:`py:deco`.
35+
Patch by Shengyu Zhang and Adam Turner.
3136

3237
Testing
3338
-------

doc/extdev/event_callbacks.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ Below is an overview of the core event that happens during a build.
7070
14. apply post-transforms (by priority): docutils.document -> docutils.document
7171
15. event.doctree-resolved(app, doctree, docname)
7272
- In the event that any reference nodes fail to resolve, the following may emit:
73-
- event.missing-reference(env, node, contnode)
74-
- event.warn-missing-reference(domain, node)
73+
- event.missing-reference(app, env, node, contnode)
74+
- event.warn-missing-reference(app, domain, node)
7575
7676
16. Generate output files
7777
17. event.build-finished(app, exception)

pyproject.toml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ lint = [
9797
"sphinx-lint>=0.9",
9898
"types-colorama==0.4.15.20240311",
9999
"types-defusedxml==0.7.0.20250516",
100-
"types-docutils==0.21.0.20250516",
100+
"types-docutils==0.21.0.20250523",
101101
"types-Pillow==10.2.0.20240822",
102102
"types-Pygments==2.19.0.20250516",
103103
"types-requests==2.32.0.20250515", # align with requests
@@ -165,7 +165,7 @@ type-stubs = [
165165
# align with versions used elsewhere
166166
"types-colorama==0.4.15.20240311",
167167
"types-defusedxml==0.7.0.20250516",
168-
"types-docutils==0.21.0.20250516",
168+
"types-docutils==0.21.0.20250523",
169169
"types-Pillow==10.2.0.20240822",
170170
"types-Pygments==2.19.0.20250516",
171171
"types-requests==2.32.0.20250515",
@@ -283,14 +283,10 @@ module = [
283283
"tests.test_theming.test_templating",
284284
"tests.test_theming.test_theming",
285285
# tests/test_transforms
286-
"tests.test_transforms.test_transforms_move_module_targets",
287286
"tests.test_transforms.test_transforms_post_transforms_images",
288287
"tests.test_transforms.test_transforms_reorder_nodes",
289288
# tests/test_util
290-
"tests.test_util.test_util",
291-
"tests.test_util.test_util_display",
292289
"tests.test_util.test_util_docutils",
293-
"tests.test_util.test_util_inventory",
294290
# tests/test_writers
295291
"tests.test_writers.test_docutilsconf",
296292
]
@@ -333,13 +329,9 @@ module = [
333329
"tests.test_extensions.test_ext_napoleon_docstring",
334330
# tests/test_intl
335331
"tests.test_intl.test_intl",
336-
# tests/test_pycode
337-
"tests.test_pycode.test_pycode",
338-
"tests.test_pycode.test_pycode_ast",
339332
# tests/test_transforms
340333
"tests.test_transforms.test_transforms_post_transforms",
341334
# tests/test_util
342-
"tests.test_util.test_util_fileutil",
343335
"tests.test_util.test_util_i18n",
344336
"tests.test_util.test_util_inspect",
345337
"tests.test_util.test_util_logging",

sphinx/directives/other.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from pathlib import Path
66
from typing import TYPE_CHECKING, cast
77

8-
import docutils
98
from docutils import nodes
109
from docutils.parsers.rst import directives
1110
from docutils.parsers.rst.directives.misc import Class
@@ -22,15 +21,14 @@
2221

2322
if TYPE_CHECKING:
2423
from collections.abc import Sequence
25-
from typing import Any, ClassVar, Final
24+
from typing import Any, ClassVar
2625

2726
from docutils.nodes import Element, Node
2827

2928
from sphinx.application import Sphinx
3029
from sphinx.util.typing import ExtensionMetadata, OptionSpec
3130

3231

33-
DU_22_PLUS: Final = docutils.__version_info__ >= (0, 22, 0, 'alpha', 0)
3432
glob_re = re.compile(r'.*[*?\[].*')
3533
logger = logging.getLogger(__name__)
3634

@@ -332,14 +330,6 @@ def run(self) -> list[Node]:
332330
surrounding_section_level = memo.section_level
333331
memo.title_styles = []
334332
memo.section_level = 0
335-
if DU_22_PLUS:
336-
# https://github.com/sphinx-doc/sphinx/issues/13539
337-
# https://sourceforge.net/p/docutils/code/10093/
338-
# https://sourceforge.net/p/docutils/patches/213/
339-
surrounding_section_parents = memo.section_parents
340-
memo.section_parents = []
341-
else:
342-
surrounding_section_parents = []
343333
try:
344334
self.state.nested_parse(
345335
self.content, self.content_offset, node, match_titles=True
@@ -375,8 +365,6 @@ def run(self) -> list[Node]:
375365
return []
376366
finally:
377367
memo.title_styles = surrounding_title_styles
378-
if DU_22_PLUS:
379-
memo.section_parents = surrounding_section_parents
380368
memo.section_level = surrounding_section_level
381369

382370

sphinx/domains/python/__init__.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from collections.abc import Iterable, Iterator, Sequence, Set
3030
from typing import Any, ClassVar
3131

32-
from docutils.nodes import Element, Node, TextElement
32+
from docutils.nodes import Element, Node
3333

3434
from sphinx.addnodes import desc_signature, pending_xref
3535
from sphinx.application import Sphinx
@@ -594,23 +594,17 @@ def process_link(
594594

595595

596596
class _PyDecoXRefRole(PyXRefRole):
597-
def __init__(
597+
def process_link(
598598
self,
599-
fix_parens: bool = False,
600-
lowercase: bool = False,
601-
nodeclass: type[Element] | None = None,
602-
innernodeclass: type[TextElement] | None = None,
603-
warn_dangling: bool = False,
604-
) -> None:
605-
super().__init__(
606-
fix_parens=True,
607-
lowercase=lowercase,
608-
nodeclass=nodeclass,
609-
innernodeclass=innernodeclass,
610-
warn_dangling=warn_dangling,
599+
env: BuildEnvironment,
600+
refnode: Element,
601+
has_explicit_title: bool,
602+
title: str,
603+
target: str,
604+
) -> tuple[str, str]:
605+
title, target = super().process_link(
606+
env, refnode, has_explicit_title, title, target
611607
)
612-
613-
def update_title_and_target(self, title: str, target: str) -> tuple[str, str]:
614608
return f'@{title}', target
615609

616610

sphinx/pycode/parser.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,9 @@ def __init__(self, buffers: list[str], encoding: str) -> None:
247247
self.deforders: dict[str, int] = {}
248248
self.finals: list[str] = []
249249
self.overloads: dict[str, list[Signature]] = {}
250-
self.typing: str | None = None
251-
self.typing_final: str | None = None
252-
self.typing_overload: str | None = None
250+
self.typing_mods: set[str] = set()
251+
self.typing_final_names: set[str] = set()
252+
self.typing_overload_names: set[str] = set()
253253
super().__init__()
254254

255255
def get_qualname_for(self, name: str) -> list[str] | None:
@@ -295,11 +295,8 @@ def add_variable_annotation(self, name: str, annotation: ast.AST) -> None:
295295
self.annotations[basename, name] = ast_unparse(annotation)
296296

297297
def is_final(self, decorators: list[ast.expr]) -> bool:
298-
final = []
299-
if self.typing:
300-
final.append('%s.final' % self.typing)
301-
if self.typing_final:
302-
final.append(self.typing_final)
298+
final = {f'{modname}.final' for modname in self.typing_mods}
299+
final |= self.typing_final_names
303300

304301
for decorator in decorators:
305302
try:
@@ -311,11 +308,8 @@ def is_final(self, decorators: list[ast.expr]) -> bool:
311308
return False
312309

313310
def is_overload(self, decorators: list[ast.expr]) -> bool:
314-
overload = []
315-
if self.typing:
316-
overload.append('%s.overload' % self.typing)
317-
if self.typing_overload:
318-
overload.append(self.typing_overload)
311+
overload = {f'{modname}.overload' for modname in self.typing_mods}
312+
overload |= self.typing_overload_names
319313

320314
for decorator in decorators:
321315
try:
@@ -348,22 +342,24 @@ def visit_Import(self, node: ast.Import) -> None:
348342
for name in node.names:
349343
self.add_entry(name.asname or name.name)
350344

351-
if name.name == 'typing':
352-
self.typing = name.asname or name.name
353-
elif name.name == 'typing.final':
354-
self.typing_final = name.asname or name.name
355-
elif name.name == 'typing.overload':
356-
self.typing_overload = name.asname or name.name
345+
if name.name in {'typing', 'typing_extensions'}:
346+
self.typing_mods.add(name.asname or name.name)
347+
elif name.name in {'typing.final', 'typing_extensions.final'}:
348+
self.typing_final_names.add(name.asname or name.name)
349+
elif name.name in {'typing.overload', 'typing_extensions.overload'}:
350+
self.typing_overload_names.add(name.asname or name.name)
357351

358352
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
359353
"""Handles Import node and record the order of definitions."""
360354
for name in node.names:
361355
self.add_entry(name.asname or name.name)
362356

363-
if node.module == 'typing' and name.name == 'final':
364-
self.typing_final = name.asname or name.name
365-
elif node.module == 'typing' and name.name == 'overload':
366-
self.typing_overload = name.asname or name.name
357+
if node.module not in {'typing', 'typing_extensions'}:
358+
continue
359+
if name.name == 'final':
360+
self.typing_final_names.add(name.asname or name.name)
361+
elif name.name == 'overload':
362+
self.typing_overload_names.add(name.asname or name.name)
367363

368364
def visit_Assign(self, node: ast.Assign) -> None:
369365
"""Handles Assign node and pick up a variable comment."""

sphinx/themes/basic/static/language_data.js.jinja

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/*
22
* This script contains the language-specific data used by searchtools.js,
3-
* namely the list of stopwords, stemmer, scorer and splitter.
3+
* namely the set of stopwords, stemmer, scorer and splitter.
44
*/
55

6-
var stopwords = {{ search_language_stop_words }};
6+
const stopwords = new Set({{ search_language_stop_words }});
7+
window.stopwords = stopwords; // Export to global scope
78

89
{% if search_language_stemming_code %}
9-
/* Non-minified version is copied as a separate JS file, if available */
10+
/* Non-minified versions are copied as separate JavaScript files, if available */
1011
{{ search_language_stemming_code|safe }}
1112
{% endif -%}
1213

sphinx/themes/basic/static/searchtools.js

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,8 @@ const Search = {
287287
const queryTermLower = queryTerm.toLowerCase();
288288

289289
// maybe skip this "word"
290-
// stopwords array is from language_data.js
291-
if (
292-
stopwords.indexOf(queryTermLower) !== -1 ||
293-
queryTerm.match(/^\d+$/)
294-
)
290+
// stopwords set is from language_data.js
291+
if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/))
295292
return;
296293

297294
// stem the word

sphinx/util/parsing.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@
55
import contextlib
66
from typing import TYPE_CHECKING
77

8-
import docutils
98
from docutils.nodes import Element
109
from docutils.statemachine import StringList, string2lines
1110

1211
if TYPE_CHECKING:
1312
from collections.abc import Iterator
14-
from typing import Final
1513

1614
from docutils.nodes import Node
1715
from docutils.parsers.rst.states import RSTState
1816

19-
DU_22_PLUS: Final = docutils.__version_info__ >= (0, 22, 0, 'alpha', 0)
20-
2117

2218
def nested_parse_to_nodes(
2319
state: RSTState,
@@ -79,23 +75,15 @@ def _fresh_title_style_context(state: RSTState) -> Iterator[None]:
7975
memo = state.memo
8076
surrounding_title_styles: list[str | tuple[str, str]] = memo.title_styles
8177
surrounding_section_level: int = memo.section_level
82-
if DU_22_PLUS:
83-
surrounding_section_parents = memo.section_parents
84-
else:
85-
surrounding_section_parents = []
8678
# clear current title styles
8779
memo.title_styles = []
8880
memo.section_level = 0
89-
if DU_22_PLUS:
90-
memo.section_parents = []
9181
try:
9282
yield
9383
finally:
9484
# reset title styles
9585
memo.title_styles = surrounding_title_styles
9686
memo.section_level = surrounding_section_level
97-
if DU_22_PLUS:
98-
memo.section_parents = surrounding_section_parents
9987

10088

10189
def _text_to_string_list(

tests/js/language_data.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
* namely the list of stopwords, stemmer, scorer and splitter.
44
*/
55

6-
var stopwords = [];
6+
const stopwords = new Set([]);
7+
window.stopwords = stopwords; // Export to global scope
78

89

9-
/* Non-minified version is copied as a separate JS file, if available */
10+
/* Non-minified versions are copied as separate JavaScript files, if available */
1011

1112
/**
1213
* Dummy stemmer for languages without stemming rules.

tests/roots/test-ext-autodoc/target/final.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import typing
44
from typing import final
55

6+
import typing_extensions
7+
from typing_extensions import final as final_ext # noqa: UP035
8+
69

710
@typing.final
811
class Class:
@@ -14,3 +17,11 @@ def meth1(self):
1417

1518
def meth2(self):
1619
"""docstring"""
20+
21+
@final_ext
22+
def meth3(self):
23+
"""docstring"""
24+
25+
@typing_extensions.final
26+
def meth4(self):
27+
"""docstring"""
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import typing
2+
from typing import TYPE_CHECKING, overload
3+
4+
import typing_extensions
5+
from typing_extensions import overload as over_ext # noqa: UP035
6+
7+
8+
@overload
9+
def test(x: int) -> int: ...
10+
@typing.overload
11+
def test(x: list[int]) -> list[int]: ...
12+
@over_ext
13+
def test(x: str) -> str: ...
14+
@typing_extensions.overload
15+
def test(x: float) -> float: ...
16+
def test(x):
17+
"""Documentation."""
18+
return x

tests/test_domains/test_domain_py.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,3 +1791,18 @@ def test_pep_695_and_pep_696_whitespaces_in_default(app, tp_list, tptext):
17911791
text = f'.. py:function:: f{tp_list}() -> Annotated[T, Qux[int]()]'
17921792
doctree = restructuredtext.parse(app, text)
17931793
assert doctree.astext() == f'\n\nf{tptext}() -> Annotated[T, Qux[int]()]\n\n'
1794+
1795+
1796+
def test_deco_role(app):
1797+
text = """\
1798+
.. py:decorator:: foo.bar
1799+
:no-contents-entry:
1800+
:no-index-entry:
1801+
:no-typesetting:
1802+
"""
1803+
1804+
doctree = restructuredtext.parse(app, text + '\n:py:deco:`foo.bar`')
1805+
assert doctree.astext() == '\n\n\n\n@foo.bar'
1806+
1807+
doctree = restructuredtext.parse(app, text + '\n:py:deco:`~foo.bar`')
1808+
assert doctree.astext() == '\n\n\n\n@bar'

0 commit comments

Comments
 (0)