|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | | -import contextvars |
6 | | -import ctypes |
7 | 5 | 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 |
15 | 6 | import sys |
16 | 7 | import types |
17 | 8 | import typing |
18 | | -import weakref |
19 | | -import zipfile |
20 | 9 | from collections.abc import Callable, Sequence |
21 | 10 | from typing import TYPE_CHECKING |
22 | 11 |
|
|
48 | 37 |
|
49 | 38 |
|
50 | 39 | # 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', |
118 | 106 | } |
119 | 107 |
|
120 | 108 |
|
121 | | -def is_invalid_builtin_class(obj: Any) -> bool: |
| 109 | +def is_invalid_builtin_class(obj: Any) -> str: |
122 | 110 | """Check *obj* is an invalid built-in class.""" |
123 | 111 | 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, '') |
127 | 116 |
|
128 | 117 |
|
129 | 118 | # 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 |
279 | 268 | return f':py:class:`{module_prefix}{cls.__name__}`' |
280 | 269 | elif ismock(cls): |
281 | 270 | 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): |
283 | 272 | # The above predicate never raises TypeError but should not be |
284 | 273 | # evaluated before determining whether *cls* is a mocked object |
285 | 274 | # 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}`' |
287 | 276 | elif _is_annotated_form(cls): |
288 | 277 | args = restify(cls.__args__[0], mode) |
289 | 278 | meta_args = [] |
@@ -460,8 +449,8 @@ def stringify_annotation( |
460 | 449 | return module_prefix + annotation_name |
461 | 450 | elif ismock(annotation): |
462 | 451 | 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 |
465 | 454 | elif _is_annotated_form(annotation): # for py310+ |
466 | 455 | pass |
467 | 456 | elif annotation_module == 'builtins' and annotation_qualname: |
|
0 commit comments