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
« 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."""
3from __future__ import annotations as _annotations 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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
16from typing_extensions import Annotated, Literal, TypeAliasType, TypeGuard, deprecated, get_args, get_origin 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
18if TYPE_CHECKING: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
19 from ._dataclasses import StandardDataclass
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
26typing_base = _TypingBase 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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
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
42if sys.version_info < (3, 10): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
44 def origin_is_union(tp: type[Any] | None) -> bool: 1hibcajkdeMNlmfg
45 return tp is typing.Union 1hibcajkdeMNlmfg
47 WithArgsTypes = (TypingGenericAlias,) 1hibcajkdeMNlmfg
49else:
51 def origin_is_union(tp: type[Any] | None) -> bool: 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz
52 return tp is typing.Union or tp is types.UnionType 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz
54 WithArgsTypes = typing._GenericAlias, types.GenericAlias, types.UnionType # type: ignore[attr-defined] 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz
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
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
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
73NONE_TYPES: tuple[Any, ...] = (None, NoneType, *(tp[None] for tp in LITERAL_TYPES)) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
76TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
79def is_none_type(type_: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
80 return type_ in NONE_TYPES 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
83def is_callable_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
84 return type_ is Callable or get_origin(type_) is Callable 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
87def is_literal_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
88 return Literal is not None and get_origin(type_) in LITERAL_TYPES 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
91def is_deprecated_instance(instance: Any) -> TypeGuard[deprecated]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
92 return isinstance(instance, DEPRECATED_TYPES) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
95def literal_values(type_: type[Any]) -> tuple[Any, ...]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
96 return get_args(type_) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
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
107 values = literal_values(type_) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
108 return list(x for value in values for x in all_literal_values(value)) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
111def is_annotated(ann_type: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
112 return get_origin(ann_type) is Annotated 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
115def annotated_type(type_: Any) -> Any | None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
116 return get_args(type_)[0] if is_annotated(type_) else None 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
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
125 return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
128test_new_type = typing.NewType('test_new_type', str) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
131def is_new_type(type_: type[Any]) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
132 """Check whether type_ was created using typing.NewType.
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
139def _check_classvar(v: type[Any] | None) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
140 if v is None: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
141 return False 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
143 return v.__class__ == typing.ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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
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
158 return False 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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
166 return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
169def is_finalvar(ann_type: Any) -> bool: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
170 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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.
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.
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
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
208 return globalns or {} 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
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
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.
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
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
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
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
280 return eval_type_backport(value, globalns, localns, try_default=False) 1hibcajkdelmfg
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
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
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
319 type_hints[name] = eval_type_backport(value, globalns, types_namespace, type_params) 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
321 return type_hints 1hibcnoABCDuvatjkdepqEFGHwxlmfgrsIJKLyz
324if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
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.
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
341 Implemented as EAFP with memory.
342 """
343 return typing.ForwardRef(arg, is_argument) 1hijkMlm
345else:
346 _make_forward_ref = typing.ForwardRef 1bcnoABCDuvatdepqEFGHwxNPQRSTUVOfgrsIJKLyz
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
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 """
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.
370 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
372 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
373 ======================================================
375 Return type hints for an object.
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').
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.
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.
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.
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.
400 - If one dict argument is passed, it is used for both globals and
401 locals.
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
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
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
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
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
491def origin_is_type_alias_type(origin: Any) -> TypeGuard[TypeAliasType]: 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
492 return isinstance(origin, TypeAliasType)
495if sys.version_info >= (3, 10): 1hibcnoABCDuvatjkdepqEFGHwxMNPQRSTUVOlmfgrsIJKLyz
497 def is_generic_alias(type_: type[Any]) -> bool: 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz
498 return isinstance(type_, (types.GenericAlias, typing._GenericAlias)) # type: ignore[attr-defined] 1noABCDuvtpqEFGHwxPQRSTUVOrsIJKLyz
500else:
502 def is_generic_alias(type_: type[Any]) -> bool: 1hibcajkdeMNlmfg
503 return isinstance(type_, typing._GenericAlias) # type: ignore 1hibcajkdeMNlmfg
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