Coverage for pydantic/_internal/_typing_extra.py: 97.93%

177 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-03 19:29 +0000

1"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap python's typing module.""" 

2 

3from __future__ import annotations as _annotations 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

4 

5import dataclasses 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

6import re 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

7import sys 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

8import types 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

9import typing 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

10import warnings 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

11from collections.abc import Callable 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

12from functools import partial 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

13from types import GetSetDescriptorType 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

14from typing import TYPE_CHECKING, Any, Final 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

15 

16from typing_extensions import Annotated, Literal, TypeAliasType, TypeGuard, deprecated, get_args, get_origin 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

17 

18if TYPE_CHECKING: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

19 from ._dataclasses import StandardDataclass 

20 

21try: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

22 from typing import _TypingBase # type: ignore[attr-defined] 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

23except ImportError: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

24 from typing import _Final as _TypingBase # type: ignore[attr-defined] 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

25 

26typing_base = _TypingBase 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

27 

28 

29if sys.version_info < (3, 9): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

30 # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on) 

31 TypingGenericAlias = () 1hijkMlm

32else: 

33 from typing import GenericAlias as TypingGenericAlias # type: ignore 1bcnoABCDuvatdepqEFGHwxNPQRSTUVOfgrsIJKLyz

34 

35 

36if sys.version_info < (3, 11): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

37 from typing_extensions import NotRequired, Required 1hibcnoatjkdepqMNPQRSTlmfgrs

38else: 

39 from typing import NotRequired, Required # noqa: F401 1ABCDuvEFGHwxUVOIJKLyz

40 

41 

42if sys.version_info < (3, 10): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

43 

44 def origin_is_union(tp: type[Any] | None) -> bool: 1hibcajkdeMNlmfg

45 return tp is typing.Union 1hibcajkdeMNlmfg

46 

47 WithArgsTypes = (TypingGenericAlias,) 1hibcajkdeMNlmfg

48 

49else: 

50 

51 def origin_is_union(tp: type[Any] | None) -> bool: 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

52 return tp is typing.Union or tp is types.UnionType 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

53 

54 WithArgsTypes = typing._GenericAlias, types.GenericAlias, types.UnionType # type: ignore[attr-defined] 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

55 

56 

57if sys.version_info < (3, 10): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

58 NoneType = type(None) 1hibcajkdeMNlmfg

59 EllipsisType = type(Ellipsis) 1hibcajkdeMNlmfg

60else: 

61 from types import NoneType as NoneType 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

62 

63 

64LITERAL_TYPES: set[Any] = {Literal} 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

65if hasattr(typing, 'Literal'): 65 ↛ 69line 65 didn't jump to line 69 because the condition on line 65 was always true1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

66 LITERAL_TYPES.add(typing.Literal) # type: ignore 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

67 

68# Check if `deprecated` is a type to prevent errors when using typing_extensions < 4.9.0 

69DEPRECATED_TYPES: tuple[Any, ...] = (deprecated,) if isinstance(deprecated, type) else () 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

70if hasattr(warnings, 'deprecated'): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

71 DEPRECATED_TYPES = (*DEPRECATED_TYPES, warnings.deprecated) # type: ignore 1uvwxOyz

72 

73NONE_TYPES: tuple[Any, ...] = (None, NoneType, *(tp[None] for tp in LITERAL_TYPES)) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

74 

75 

76TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

77 

78 

79def is_none_type(type_: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

80 return type_ in NONE_TYPES 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

81 

82 

83def is_callable_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

84 return type_ is Callable or get_origin(type_) is Callable 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

85 

86 

87def is_literal_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

88 return Literal is not None and get_origin(type_) in LITERAL_TYPES 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

89 

90 

91def is_deprecated_instance(instance: Any) -> TypeGuard[deprecated]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

92 return isinstance(instance, DEPRECATED_TYPES) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

93 

94 

95def literal_values(type_: type[Any]) -> tuple[Any, ...]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

96 return get_args(type_) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

97 

98 

99def all_literal_values(type_: type[Any]) -> list[Any]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

100 """This method is used to retrieve all Literal values as 

101 Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586) 

102 e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]`. 

103 """ 

104 if not is_literal_type(type_): 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

105 return [type_] 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

106 

107 values = literal_values(type_) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

108 return list(x for value in values for x in all_literal_values(value)) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

109 

110 

111def is_annotated(ann_type: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

112 return get_origin(ann_type) is Annotated 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

113 

114 

115def annotated_type(type_: Any) -> Any | None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

116 return get_args(type_)[0] if is_annotated(type_) else None 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

117 

118 

119def is_namedtuple(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

120 """Check if a given class is a named tuple. 

121 It can be either a `typing.NamedTuple` or `collections.namedtuple`. 

122 """ 

123 from ._utils import lenient_issubclass 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

124 

125 return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

126 

127 

128test_new_type = typing.NewType('test_new_type', str) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

129 

130 

131def is_new_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

132 """Check whether type_ was created using typing.NewType. 

133 

134 Can't use isinstance because it fails <3.10. 

135 """ 

136 return isinstance(type_, test_new_type.__class__) and hasattr(type_, '__supertype__') # type: ignore[arg-type] 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

137 

138 

139def _check_classvar(v: type[Any] | None) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

140 if v is None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

141 return False 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

142 

143 return v.__class__ == typing.ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

144 

145 

146def is_classvar(ann_type: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

147 if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

148 return True 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

149 

150 # this is an ugly workaround for class vars that contain forward references and are therefore themselves 

151 # forward references, see #3679 

152 if ann_type.__class__ == typing.ForwardRef and re.match( 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

153 r'(\w+\.)?ClassVar\[', 

154 ann_type.__forward_arg__, # type: ignore 

155 ): 

156 return True 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

157 

158 return False 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

159 

160 

161def _check_finalvar(v: type[Any] | None) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

162 """Check if a given type is a `typing.Final` type.""" 

163 if v is None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

164 return False 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

165 

166 return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

167 

168 

169def is_finalvar(ann_type: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

170 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

171 

172 

173def parent_frame_namespace(*, parent_depth: int = 2) -> dict[str, Any] | None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

174 """We allow use of items in parent namespace to get around the issue with `get_type_hints` only looking in the 

175 global module namespace. See https://github.com/pydantic/pydantic/issues/2678#issuecomment-1008139014 -> Scope 

176 and suggestion at the end of the next comment by @gvanrossum. 

177 

178 WARNING 1: it matters exactly where this is called. By default, this function will build a namespace from the 

179 parent of where it is called. 

180 

181 WARNING 2: this only looks in the parent namespace, not other parents since (AFAIK) there's no way to collect a 

182 dict of exactly what's in scope. Using `f_back` would work sometimes but would be very wrong and confusing in many 

183 other cases. See https://discuss.python.org/t/is-there-a-way-to-access-parent-nested-namespaces/20659. 

184 """ 

185 frame = sys._getframe(parent_depth) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

186 # if f_back is None, it's the global module namespace and we don't need to include it here 

187 if frame.f_back is None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

188 return None 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

189 else: 

190 return frame.f_locals 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

191 

192 

193def add_module_globals(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

194 module_name = getattr(obj, '__module__', None) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

195 if module_name: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

196 try: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

197 module_globalns = sys.modules[module_name].__dict__ 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

198 except KeyError: 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

199 # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363 

200 pass 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

201 else: 

202 if globalns: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

203 return {**module_globalns, **globalns} 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

204 else: 

205 # copy module globals to make sure it can't be updated later 

206 return module_globalns.copy() 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

207 

208 return globalns or {} 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

209 

210 

211def get_cls_types_namespace(cls: type[Any], parent_namespace: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

212 ns = add_module_globals(cls, parent_namespace) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

213 ns[cls.__name__] = cls 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

214 return ns 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

215 

216 

217def get_cls_type_hints_lenient(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

218 """Collect annotations from a class, including those from parent classes. 

219 

220 Unlike `typing.get_type_hints`, this function will not error if a forward reference is not resolvable. 

221 """ 

222 hints = {} 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

223 for base in reversed(obj.__mro__): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

224 ann = base.__dict__.get('__annotations__') 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

225 localns = dict(vars(base)) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

226 if ann is not None and ann is not GetSetDescriptorType: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

227 for name, value in ann.items(): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

228 hints[name] = eval_type_lenient(value, globalns, localns) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

229 return hints 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

230 

231 

232def eval_type_lenient(value: Any, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None) -> Any: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

233 """Behaves like typing._eval_type, except it won't raise an error if a forward reference can't be resolved.""" 

234 if value is None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

235 value = NoneType 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

236 elif isinstance(value, str): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

237 value = _make_forward_ref(value, is_argument=False, is_class=True) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

238 

239 try: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

240 return eval_type_backport(value, globalns, localns) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

241 except NameError: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

242 # the point of this function is to be tolerant to this case 

243 return value 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

244 

245 

246def eval_type_backport( 1hibcnoABCDuvjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

247 value: Any, 

248 globalns: dict[str, Any] | None = None, 

249 localns: dict[str, Any] | None = None, 

250 type_params: tuple[Any] | None = None, 

251) -> Any: 

252 """Like `typing._eval_type`, but falls back to the `eval_type_backport` package if it's 

253 installed to let older Python versions use newer typing features. 

254 Specifically, this transforms `X | Y` into `typing.Union[X, Y]` 

255 and `list[X]` into `typing.List[X]` etc. (for all the types made generic in PEP 585) 

256 if the original syntax is not supported in the current Python version. 

257 """ 

258 try: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

259 if sys.version_info >= (3, 13): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

260 return typing._eval_type( # type: ignore 1uvwxOyz

261 value, globalns, localns, type_params=type_params 

262 ) 

263 else: 

264 return typing._eval_type( # type: ignore 1hibcnoABCDatjkdepqEFGHMNPQRSTUVlmfgrsIJKL

265 value, globalns, localns 

266 ) 

267 except TypeError as e: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

268 if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)): 1hibcnoatjkdepqlmfgrs

269 raise 1hibcnoatjkdepqlmfgrs

270 try: 1hibcajkdelmfg

271 from eval_type_backport import eval_type_backport 1hibcajkdelmfg

272 except ImportError: 1hibcajkdelmfg

273 raise TypeError( 1hibcajkdelmfg

274 f'You have a type annotation {value.__forward_arg__!r} ' 

275 f'which makes use of newer typing features than are supported in your version of Python. ' 

276 f'To handle this error, you should either remove the use of new syntax ' 

277 f'or install the `eval_type_backport` package.' 

278 ) from e 

279 

280 return eval_type_backport(value, globalns, localns, try_default=False) 1hibcajkdelmfg

281 

282 

283def is_backport_fixable_error(e: TypeError) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

284 msg = str(e) 1hibcnoatjkdepqlmfgrs

285 return msg.startswith('unsupported operand type(s) for |: ') or "' object is not subscriptable" in msg 1hibcnoatjkdepqlmfgrs

286 

287 

288def get_function_type_hints( 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

289 function: Callable[..., Any], *, include_keys: set[str] | None = None, types_namespace: dict[str, Any] | None = None 

290) -> dict[str, Any]: 

291 """Like `typing.get_type_hints`, but doesn't convert `X` to `Optional[X]` if the default value is `None`, also 

292 copes with `partial`. 

293 """ 

294 try: 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

295 if isinstance(function, partial): 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

296 annotations = function.func.__annotations__ 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

297 else: 

298 annotations = function.__annotations__ 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

299 except AttributeError: 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

300 type_hints = get_type_hints(function) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

301 if isinstance(function, type): 301 ↛ 306line 301 didn't jump to line 306 because the condition on line 301 was always true1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

302 # `type[...]` is a callable, which returns an instance of itself. 

303 # At some point, we might even look into the return type of `__new__` 

304 # if it returns something else. 

305 type_hints.setdefault('return', function) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

306 return type_hints 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

307 

308 globalns = add_module_globals(function) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

309 type_hints = {} 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

310 type_params: tuple[Any] = getattr(function, '__type_params__', ()) # type: ignore 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

311 for name, value in annotations.items(): 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

312 if include_keys is not None and name not in include_keys: 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

313 continue 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

314 if value is None: 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

315 value = NoneType 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

316 elif isinstance(value, str): 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

317 value = _make_forward_ref(value) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

318 

319 type_hints[name] = eval_type_backport(value, globalns, types_namespace, type_params) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

320 

321 return type_hints 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz

322 

323 

324if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

325 

326 def _make_forward_ref( 1hijkMlm

327 arg: Any, 

328 is_argument: bool = True, 

329 *, 

330 is_class: bool = False, 

331 ) -> typing.ForwardRef: 

332 """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions. 

333 The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below. 

334 

335 See https://github.com/python/cpython/pull/28560 for some background. 

336 The backport happened on 3.9.8, see: 

337 https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458, 

338 and on 3.10.1 for the 3.10 branch, see: 

339 https://github.com/pydantic/pydantic/issues/6912 

340 

341 Implemented as EAFP with memory. 

342 """ 

343 return typing.ForwardRef(arg, is_argument) 1hijkMlm

344 

345else: 

346 _make_forward_ref = typing.ForwardRef 1bcnoABCDuvatdepqEFGHwxNPQRSTUVOfgrsIJKLyz

347 

348 

349if sys.version_info >= (3, 10): 349 ↛ 353line 349 didn't jump to line 353 because the condition on line 349 was always true1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

350 get_type_hints = typing.get_type_hints 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

351 

352else: 

353 """ 

354 For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to 

355 the implementation in CPython 3.10.8. 

356 """ 

357 

358 @typing.no_type_check 1hibcajkdeMNlmfg

359 def get_type_hints( # noqa: C901 1hibcjkdeMNlmfg

360 obj: Any, 1a

361 globalns: dict[str, Any] | None = None, 1a

362 localns: dict[str, Any] | None = None, 1a

363 include_extras: bool = False, 1a

364 ) -> dict[str, Any]: # pragma: no cover 1a

365 """Taken verbatim from python 3.10.8 unchanged, except: 

366 * type annotations of the function definition above. 

367 * prefixing `typing.` where appropriate 

368 * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument. 

369 

370 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875 

371 

372 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY. 

373 ====================================================== 

374 

375 Return type hints for an object. 

376 

377 This is often the same as obj.__annotations__, but it handles 

378 forward references encoded as string literals, adds Optional[t] if a 

379 default value equal to None is set and recursively replaces all 

380 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). 

381 

382 The argument may be a module, class, method, or function. The annotations 

383 are returned as a dictionary. For classes, annotations include also 

384 inherited members. 

385 

386 TypeError is raised if the argument is not of a type that can contain 

387 annotations, and an empty dictionary is returned if no annotations are 

388 present. 

389 

390 BEWARE -- the behavior of globalns and localns is counterintuitive 

391 (unless you are familiar with how eval() and exec() work). The 

392 search order is locals first, then globals. 

393 

394 - If no dict arguments are passed, an attempt is made to use the 

395 globals from obj (or the respective module's globals for classes), 

396 and these are also used as the locals. If the object does not appear 

397 to have globals, an empty dictionary is used. For classes, the search 

398 order is globals first then locals. 

399 

400 - If one dict argument is passed, it is used for both globals and 

401 locals. 

402 

403 - If two dict arguments are passed, they specify globals and 

404 locals, respectively. 

405 """ 

406 if getattr(obj, '__no_type_check__', None): 1hibcajkdelmfg

407 return {} 

408 # Classes require a special treatment. 

409 if isinstance(obj, type): 1hibcajkdelmfg

410 hints = {} 1hibcajkdelmfg

411 for base in reversed(obj.__mro__): 1hibcajkdelmfg

412 if globalns is None: 1hibcajkdelmfg

413 base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {}) 1hibcajkdelmfg

414 else: 

415 base_globals = globalns 1hibcajkdelmfg

416 ann = base.__dict__.get('__annotations__', {}) 1hibcajkdelmfg

417 if isinstance(ann, types.GetSetDescriptorType): 1hibcajkdelmfg

418 ann = {} 

419 base_locals = dict(vars(base)) if localns is None else localns 1hibcajkdelmfg

420 if localns is None and globalns is None: 1hibcajkdelmfg

421 # This is surprising, but required. Before Python 3.10, 

422 # get_type_hints only evaluated the globalns of 

423 # a class. To maintain backwards compatibility, we reverse 

424 # the globalns and localns order so that eval() looks into 

425 # *base_globals* first rather than *base_locals*. 

426 # This only affects ForwardRefs. 

427 base_globals, base_locals = base_locals, base_globals 1hibcajkdelmfg

428 for name, value in ann.items(): 1hibcajkdelmfg

429 if value is None: 1hibcajkdelmfg

430 value = type(None) 

431 if isinstance(value, str): 1hibcajkdelmfg

432 value = _make_forward_ref(value, is_argument=False, is_class=True) 1hibcajkdelmfg

433 

434 value = eval_type_backport(value, base_globals, base_locals) 1hibcajkdelmfg

435 hints[name] = value 1hibcajkdelmfg

436 if not include_extras and hasattr(typing, '_strip_annotations'): 1hibcajkdelmfg

437 return { 1bcadefg

438 k: typing._strip_annotations(t) # type: ignore 1bcadefg

439 for k, t in hints.items() 1bcadefg

440 } 

441 else: 

442 return hints 1hibcajkdelmfg

443 

444 if globalns is None: 1hibcajkdelmfg

445 if isinstance(obj, types.ModuleType): 1hibcajkdelmfg

446 globalns = obj.__dict__ 

447 else: 

448 nsobj = obj 1hibcajkdelmfg

449 # Find globalns for the unwrapped object. 

450 while hasattr(nsobj, '__wrapped__'): 1hibcajkdelmfg

451 nsobj = nsobj.__wrapped__ 

452 globalns = getattr(nsobj, '__globals__', {}) 1hibcajkdelmfg

453 if localns is None: 1hibcajkdelmfg

454 localns = globalns 1hibcajkdelmfg

455 elif localns is None: 

456 localns = globalns 

457 hints = getattr(obj, '__annotations__', None) 1hibcajkdelmfg

458 if hints is None: 1hibcajkdelmfg

459 # Return empty annotations for something that _could_ have them. 

460 if isinstance(obj, typing._allowed_types): # type: ignore 

461 return {} 

462 else: 

463 raise TypeError(f'{obj!r} is not a module, class, method, ' 'or function.') 

464 defaults = typing._get_defaults(obj) # type: ignore 1hibcajkdelmfg

465 hints = dict(hints) 1hibcajkdelmfg

466 for name, value in hints.items(): 1hibcajkdelmfg

467 if value is None: 1hibcajkdelmfg

468 value = type(None) 

469 if isinstance(value, str): 1hibcajkdelmfg

470 # class-level forward refs were handled above, this must be either 

471 # a module-level annotation or a function argument annotation 

472 

473 value = _make_forward_ref( 1hibcajkdelmfg

474 value, 1hibcajkdelmfg

475 is_argument=not isinstance(obj, types.ModuleType), 1hibcajkdelmfg

476 is_class=False, 1hibcajkdelmfg

477 ) 

478 value = eval_type_backport(value, globalns, localns) 1hibcajkdelmfg

479 if name in defaults and defaults[name] is None: 1hibcajkdelmfg

480 value = typing.Optional[value] 

481 hints[name] = value 1hibcajkdelmfg

482 return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore 1hibcajkdelmfg

483 

484 

485def is_dataclass(_cls: type[Any]) -> TypeGuard[type[StandardDataclass]]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

486 # The dataclasses.is_dataclass function doesn't seem to provide TypeGuard functionality, 

487 # so I created this convenience function 

488 return dataclasses.is_dataclass(_cls) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

489 

490 

491def origin_is_type_alias_type(origin: Any) -> TypeGuard[TypeAliasType]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

492 return isinstance(origin, TypeAliasType) 

493 

494 

495if sys.version_info >= (3, 10): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

496 

497 def is_generic_alias(type_: type[Any]) -> bool: 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

498 return isinstance(type_, (types.GenericAlias, typing._GenericAlias)) # type: ignore[attr-defined] 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz

499 

500else: 

501 

502 def is_generic_alias(type_: type[Any]) -> bool: 1hibcajkdeMNlmfg

503 return isinstance(type_, typing._GenericAlias) # type: ignore 1hibcajkdeMNlmfg

504 

505 

506def is_self_type(tp: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz

507 """Check if a given class is a Self type (from `typing` or `typing_extensions`)""" 

508 return isinstance(tp, typing_base) and getattr(tp, '_name', None) == 'Self' 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz