Skip to content

Commit 84269fa

Browse files
authored
Merge branch 'master' into autosummary-short-signature
2 parents f1017ef + 60f88cc commit 84269fa

File tree

5 files changed

+120
-167
lines changed

5 files changed

+120
-167
lines changed

CHANGES.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ Features added
4747
* #13065: Enable colour by default in when running on CI.
4848
Patch by Adam Turner.
4949
* Allow supressing warnings from the :rst:dir:`toctree` directive when a glob
50-
pattern doesn't match any documents, via the new ``toc.glob_not_matching``
50+
pattern doesn't match any documents, via the new ``toc.empty_glob``
5151
warning sub-type.
5252
Patch by Slawek Figiel.
53-
* #9732: Add the new ``autodoc.mock_objects`` warnings sub-type.
53+
* #9732: Add the new ``autodoc.mocked_object`` warnings sub-type.
5454
Patch by Cyril Roelandt.
5555
* #7630, #4824: autodoc: Use :file:`.pyi` type stub files
5656
to auto-document native modules.
@@ -70,6 +70,9 @@ Features added
7070
which defaults to ``True`` for backwards compatibility.
7171
The default will change to ``False`` in Sphinx 10.
7272
Patch by Adam Turner.
73+
* #9732: Add the new ``ref.any`` warnings sub-type
74+
to allow suppressing the ambiguous 'any' cross-reference warning.
75+
Patch by Simão Afonso and Adam Turner.
7376

7477
Bugs fixed
7578
----------

doc/usage/configuration.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,7 @@ Options for warning control
13841384
* ``misc.copy_overwrite``
13851385
* ``misc.highlighting_failure``
13861386
* ``ref.citation``
1387+
* ``ref.any``
13871388
* ``ref.doc``
13881389
* ``ref.footnote``
13891390
* ``ref.keyword``
@@ -1487,6 +1488,9 @@ Options for warning control
14871488
.. versionadded:: 8.2
14881489
Added ``autodoc.mocked_object``
14891490

1491+
.. versionadded:: 8.2
1492+
Added ``ref.any``
1493+
14901494

14911495
Builder options
14921496
===============

sphinx/transforms/post_transforms/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ def stringify(name: str, node: Element) -> str:
188188
target,
189189
candidates,
190190
location=node,
191+
type='ref',
192+
subtype='any',
191193
)
192194
res_role, newnode = results[0]
193195
# Override "any" class with the actual role type to get the styling

sphinx/util/typing.py

Lines changed: 75 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,10 @@
22

33
from __future__ import annotations
44

5-
import contextvars
6-
import ctypes
75
import dataclasses
8-
import io
9-
import json
10-
import lzma
11-
import multiprocessing
12-
import pathlib
13-
import pickle # NoQA: S403
14-
import struct
156
import sys
167
import types
178
import typing
18-
import weakref
19-
import zipfile
209
from collections.abc import Callable, Sequence
2110
from typing import TYPE_CHECKING
2211

@@ -48,82 +37,82 @@
4837

4938

5039
# classes that have an incorrect .__module__ attribute
51-
_INVALID_BUILTIN_CLASSES: Final[Mapping[object, str]] = {
52-
# types in 'contextvars' with <type>.__module__ == '_contextvars':
53-
contextvars.Context: 'contextvars.Context',
54-
contextvars.ContextVar: 'contextvars.ContextVar',
55-
contextvars.Token: 'contextvars.Token',
56-
# types in 'ctypes' with <type>.__module__ == '_ctypes':
57-
ctypes.Array: 'ctypes.Array',
58-
ctypes.Structure: 'ctypes.Structure',
59-
ctypes.Union: 'ctypes.Union',
60-
# types in 'io' with <type>.__module__ == '_io':
61-
io.FileIO: 'io.FileIO',
62-
io.BytesIO: 'io.BytesIO',
63-
io.StringIO: 'io.StringIO',
64-
io.BufferedReader: 'io.BufferedReader',
65-
io.BufferedWriter: 'io.BufferedWriter',
66-
io.BufferedRWPair: 'io.BufferedRWPair',
67-
io.BufferedRandom: 'io.BufferedRandom',
68-
io.TextIOWrapper: 'io.TextIOWrapper',
69-
# types in 'json' with <type>.__module__ == 'json.{decoder,encoder}':
70-
json.JSONDecoder: 'json.JSONDecoder',
71-
json.JSONEncoder: 'json.JSONEncoder',
72-
# types in 'lzma' with <type>.__module__ == '_lzma':
73-
lzma.LZMACompressor: 'lzma.LZMACompressor',
74-
lzma.LZMADecompressor: 'lzma.LZMADecompressor',
75-
# types in 'multiprocessing' with <type>.__module__ == 'multiprocessing.context':
76-
multiprocessing.Process: 'multiprocessing.Process',
77-
# types in 'pathlib' with <type>.__module__ == 'pathlib._local':
78-
pathlib.Path: 'pathlib.Path',
79-
pathlib.PosixPath: 'pathlib.PosixPath',
80-
pathlib.PurePath: 'pathlib.PurePath',
81-
pathlib.PurePosixPath: 'pathlib.PurePosixPath',
82-
pathlib.PureWindowsPath: 'pathlib.PureWindowsPath',
83-
pathlib.WindowsPath: 'pathlib.WindowsPath',
84-
# types in 'pickle' with <type>.__module__ == 'pickle':
85-
pickle.Pickler: 'pickle.Pickler',
86-
pickle.Unpickler: 'pickle.Unpickler', # NoQA: S301
87-
# types in 'struct' with <type>.__module__ == '_struct':
88-
struct.Struct: 'struct.Struct',
89-
# types in 'types' with <type>.__module__ == 'builtins':
90-
types.AsyncGeneratorType: 'types.AsyncGeneratorType',
91-
types.BuiltinFunctionType: 'types.BuiltinFunctionType',
92-
types.BuiltinMethodType: 'types.BuiltinMethodType',
93-
types.CellType: 'types.CellType',
94-
types.ClassMethodDescriptorType: 'types.ClassMethodDescriptorType',
95-
types.CodeType: 'types.CodeType',
96-
types.CoroutineType: 'types.CoroutineType',
97-
types.EllipsisType: 'types.EllipsisType',
98-
types.FrameType: 'types.FrameType',
99-
types.FunctionType: 'types.FunctionType',
100-
types.GeneratorType: 'types.GeneratorType',
101-
types.GetSetDescriptorType: 'types.GetSetDescriptorType',
102-
types.LambdaType: 'types.LambdaType',
103-
types.MappingProxyType: 'types.MappingProxyType',
104-
types.MemberDescriptorType: 'types.MemberDescriptorType',
105-
types.MethodDescriptorType: 'types.MethodDescriptorType',
106-
types.MethodType: 'types.MethodType',
107-
types.MethodWrapperType: 'types.MethodWrapperType',
108-
types.ModuleType: 'types.ModuleType',
109-
types.NoneType: 'types.NoneType',
110-
types.NotImplementedType: 'types.NotImplementedType',
111-
types.TracebackType: 'types.TracebackType',
112-
types.WrapperDescriptorType: 'types.WrapperDescriptorType',
113-
# types in 'weakref' with <type>.__module__ == '_weakrefset':
114-
weakref.WeakSet: 'weakref.WeakSet',
115-
# types in 'zipfile' with <type>.__module__ == 'zipfile._path':
116-
zipfile.Path: 'zipfile.Path',
117-
zipfile.CompleteDirs: 'zipfile.CompleteDirs',
40+
# Map of (__module__, __qualname__) to the correct fully-qualified name
41+
_INVALID_BUILTIN_CLASSES: Final[Mapping[tuple[str, str], str]] = {
42+
# types from 'contextvars'
43+
('_contextvars', 'Context'): 'contextvars.Context',
44+
('_contextvars', 'ContextVar'): 'contextvars.ContextVar',
45+
('_contextvars', 'Token'): 'contextvars.Token',
46+
# types from 'ctypes':
47+
('_ctypes', 'Array'): 'ctypes.Array',
48+
('_ctypes', 'Structure'): 'ctypes.Structure',
49+
('_ctypes', 'Union'): 'ctypes.Union',
50+
# types from 'io':
51+
('_io', 'BufferedRandom'): 'io.BufferedRandom',
52+
('_io', 'BufferedReader'): 'io.BufferedReader',
53+
('_io', 'BufferedRWPair'): 'io.BufferedRWPair',
54+
('_io', 'BufferedWriter'): 'io.BufferedWriter',
55+
('_io', 'BytesIO'): 'io.BytesIO',
56+
('_io', 'FileIO'): 'io.FileIO',
57+
('_io', 'StringIO'): 'io.StringIO',
58+
('_io', 'TextIOWrapper'): 'io.TextIOWrapper',
59+
# types from 'json':
60+
('json.decoder', 'JSONDecoder'): 'json.JSONDecoder',
61+
('json.encoder', 'JSONEncoder'): 'json.JSONEncoder',
62+
# types from 'lzma':
63+
('_lzma', 'LZMACompressor'): 'lzma.LZMACompressor',
64+
('_lzma', 'LZMADecompressor'): 'lzma.LZMADecompressor',
65+
# types from 'multiprocessing':
66+
('multiprocessing.context', 'Process'): 'multiprocessing.Process',
67+
# types from 'pathlib':
68+
('pathlib._local', 'Path'): 'pathlib.Path',
69+
('pathlib._local', 'PosixPath'): 'pathlib.PosixPath',
70+
('pathlib._local', 'PurePath'): 'pathlib.PurePath',
71+
('pathlib._local', 'PurePosixPath'): 'pathlib.PurePosixPath',
72+
('pathlib._local', 'PureWindowsPath'): 'pathlib.PureWindowsPath',
73+
('pathlib._local', 'WindowsPath'): 'pathlib.WindowsPath',
74+
# types from 'pickle':
75+
('_pickle', 'Pickler'): 'pickle.Pickler',
76+
('_pickle', 'Unpickler'): 'pickle.Unpickler',
77+
# types from 'struct':
78+
('_struct', 'Struct'): 'struct.Struct',
79+
# types from 'types':
80+
('builtins', 'async_generator'): 'types.AsyncGeneratorType',
81+
('builtins', 'builtin_function_or_method'): 'types.BuiltinMethodType',
82+
('builtins', 'cell'): 'types.CellType',
83+
('builtins', 'classmethod_descriptor'): 'types.ClassMethodDescriptorType',
84+
('builtins', 'code'): 'types.CodeType',
85+
('builtins', 'coroutine'): 'types.CoroutineType',
86+
('builtins', 'ellipsis'): 'types.EllipsisType',
87+
('builtins', 'frame'): 'types.FrameType',
88+
('builtins', 'function'): 'types.LambdaType',
89+
('builtins', 'generator'): 'types.GeneratorType',
90+
('builtins', 'getset_descriptor'): 'types.GetSetDescriptorType',
91+
('builtins', 'mappingproxy'): 'types.MappingProxyType',
92+
('builtins', 'member_descriptor'): 'types.MemberDescriptorType',
93+
('builtins', 'method'): 'types.MethodType',
94+
('builtins', 'method-wrapper'): 'types.MethodWrapperType',
95+
('builtins', 'method_descriptor'): 'types.MethodDescriptorType',
96+
('builtins', 'module'): 'types.ModuleType',
97+
('builtins', 'NoneType'): 'types.NoneType',
98+
('builtins', 'NotImplementedType'): 'types.NotImplementedType',
99+
('builtins', 'traceback'): 'types.TracebackType',
100+
('builtins', 'wrapper_descriptor'): 'types.WrapperDescriptorType',
101+
# types from 'weakref':
102+
('_weakrefset', 'WeakSet'): 'weakref.WeakSet',
103+
# types from 'zipfile':
104+
('zipfile._path', 'CompleteDirs'): 'zipfile.CompleteDirs',
105+
('zipfile._path', 'Path'): 'zipfile.Path',
118106
}
119107

120108

121-
def is_invalid_builtin_class(obj: Any) -> bool:
109+
def is_invalid_builtin_class(obj: Any) -> str:
122110
"""Check *obj* is an invalid built-in class."""
123111
try:
124-
return obj in _INVALID_BUILTIN_CLASSES
125-
except TypeError: # unhashable type
126-
return False
112+
key = obj.__module__, obj.__qualname__
113+
except AttributeError: # non-standard type
114+
return ''
115+
return _INVALID_BUILTIN_CLASSES.get(key, '')
127116

128117

129118
# Text like nodes which are initialized with text and rawsource
@@ -279,11 +268,11 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s
279268
return f':py:class:`{module_prefix}{cls.__name__}`'
280269
elif ismock(cls):
281270
return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`'
282-
elif is_invalid_builtin_class(cls):
271+
elif fixed_cls := is_invalid_builtin_class(cls):
283272
# The above predicate never raises TypeError but should not be
284273
# evaluated before determining whether *cls* is a mocked object
285274
# or not; instead of two try-except blocks, we keep it here.
286-
return f':py:class:`{module_prefix}{_INVALID_BUILTIN_CLASSES[cls]}`'
275+
return f':py:class:`{module_prefix}{fixed_cls}`'
287276
elif _is_annotated_form(cls):
288277
args = restify(cls.__args__[0], mode)
289278
meta_args = []
@@ -460,8 +449,8 @@ def stringify_annotation(
460449
return module_prefix + annotation_name
461450
elif ismock(annotation):
462451
return module_prefix + f'{annotation_module}.{annotation_name}'
463-
elif is_invalid_builtin_class(annotation):
464-
return module_prefix + _INVALID_BUILTIN_CLASSES[annotation]
452+
elif fixed_annotation := is_invalid_builtin_class(annotation):
453+
return module_prefix + fixed_annotation
465454
elif _is_annotated_form(annotation): # for py310+
466455
pass
467456
elif annotation_module == 'builtins' and annotation_qualname:

tests/test_util/test_util_typing.py

Lines changed: 34 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def test_is_invalid_builtin_class():
141141
# if these tests start failing, it means that the __module__
142142
# of one of these classes has changed, and _INVALID_BUILTIN_CLASSES
143143
# in sphinx.util.typing needs to be updated.
144-
assert _INVALID_BUILTIN_CLASSES.keys() == {
144+
invalid_types = (
145145
# contextvars
146146
Context,
147147
ContextVar,
@@ -167,13 +167,6 @@ def test_is_invalid_builtin_class():
167167
LZMADecompressor,
168168
# multiprocessing
169169
Process,
170-
# pathlib
171-
Path,
172-
PosixPath,
173-
PurePath,
174-
PurePosixPath,
175-
PureWindowsPath,
176-
WindowsPath,
177170
# pickle
178171
Pickler,
179172
Unpickler,
@@ -205,78 +198,40 @@ def test_is_invalid_builtin_class():
205198
WrapperDescriptorType,
206199
# weakref
207200
WeakSet,
208-
# zipfile
209-
zipfile.Path,
210-
zipfile.CompleteDirs,
211-
}
212-
# contextvars
213-
assert Context.__module__ == '_contextvars'
214-
assert ContextVar.__module__ == '_contextvars'
215-
assert Token.__module__ == '_contextvars'
216-
# ctypes
217-
assert ctypes.Array.__module__ == '_ctypes'
218-
assert ctypes.Structure.__module__ == '_ctypes'
219-
assert ctypes.Union.__module__ == '_ctypes'
220-
# io
221-
assert FileIO.__module__ == '_io'
222-
assert BytesIO.__module__ == '_io'
223-
assert StringIO.__module__ == '_io'
224-
assert BufferedReader.__module__ == '_io'
225-
assert BufferedWriter.__module__ == '_io'
226-
assert BufferedRWPair.__module__ == '_io'
227-
assert BufferedRandom.__module__ == '_io'
228-
assert TextIOWrapper.__module__ == '_io'
229-
# json
230-
assert JSONDecoder.__module__ == 'json.decoder'
231-
assert JSONEncoder.__module__ == 'json.encoder'
232-
# lzma
233-
assert LZMACompressor.__module__ == '_lzma'
234-
assert LZMADecompressor.__module__ == '_lzma'
235-
# multiprocessing
236-
assert Process.__module__ == 'multiprocessing.context'
237-
if sys.version_info[:2] >= (3, 13):
238-
# pathlib
239-
assert Path.__module__ == 'pathlib._local'
240-
assert PosixPath.__module__ == 'pathlib._local'
241-
assert PurePath.__module__ == 'pathlib._local'
242-
assert PurePosixPath.__module__ == 'pathlib._local'
243-
assert PureWindowsPath.__module__ == 'pathlib._local'
244-
assert WindowsPath.__module__ == 'pathlib._local'
245-
# pickle
246-
assert Pickler.__module__ == '_pickle'
247-
assert Unpickler.__module__ == '_pickle'
248-
# struct
249-
assert Struct.__module__ == '_struct'
250-
# types
251-
assert AsyncGeneratorType.__module__ == 'builtins'
252-
assert BuiltinFunctionType.__module__ == 'builtins'
253-
assert BuiltinMethodType.__module__ == 'builtins'
254-
assert CellType.__module__ == 'builtins'
255-
assert ClassMethodDescriptorType.__module__ == 'builtins'
256-
assert CodeType.__module__ == 'builtins'
257-
assert CoroutineType.__module__ == 'builtins'
258-
assert EllipsisType.__module__ == 'builtins'
259-
assert FrameType.__module__ == 'builtins'
260-
assert FunctionType.__module__ == 'builtins'
261-
assert GeneratorType.__module__ == 'builtins'
262-
assert GetSetDescriptorType.__module__ == 'builtins'
263-
assert LambdaType.__module__ == 'builtins'
264-
assert MappingProxyType.__module__ == 'builtins'
265-
assert MemberDescriptorType.__module__ == 'builtins'
266-
assert MethodDescriptorType.__module__ == 'builtins'
267-
assert MethodType.__module__ == 'builtins'
268-
assert MethodWrapperType.__module__ == 'builtins'
269-
assert ModuleType.__module__ == 'builtins'
270-
assert NoneType.__module__ == 'builtins'
271-
assert NotImplementedType.__module__ == 'builtins'
272-
assert TracebackType.__module__ == 'builtins'
273-
assert WrapperDescriptorType.__module__ == 'builtins'
274-
# weakref
275-
assert WeakSet.__module__ == '_weakrefset'
201+
)
276202
if sys.version_info[:2] >= (3, 12):
277-
# zipfile
278-
assert zipfile.Path.__module__ == 'zipfile._path'
279-
assert zipfile.CompleteDirs.__module__ == 'zipfile._path'
203+
invalid_types += (
204+
# zipfile
205+
zipfile.Path,
206+
zipfile.CompleteDirs,
207+
)
208+
if sys.version_info[:2] >= (3, 13):
209+
invalid_types += (
210+
# pathlib
211+
Path,
212+
PosixPath,
213+
PurePath,
214+
PurePosixPath,
215+
PureWindowsPath,
216+
WindowsPath,
217+
)
218+
219+
invalid_names = {(cls.__module__, cls.__qualname__) for cls in invalid_types}
220+
if sys.version_info[:2] < (3, 13):
221+
invalid_names |= {
222+
('pathlib._local', 'Path'),
223+
('pathlib._local', 'PosixPath'),
224+
('pathlib._local', 'PurePath'),
225+
('pathlib._local', 'PurePosixPath'),
226+
('pathlib._local', 'PureWindowsPath'),
227+
('pathlib._local', 'WindowsPath'),
228+
}
229+
if sys.version_info[:2] < (3, 12):
230+
invalid_names |= {
231+
('zipfile._path', 'Path'),
232+
('zipfile._path', 'CompleteDirs'),
233+
}
234+
assert _INVALID_BUILTIN_CLASSES.keys() == invalid_names
280235

281236

282237
def test_restify_type_hints_containers():

0 commit comments

Comments
 (0)