Coverage for pydantic/_internal/_typing_extra.py: 97.06%
176 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +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 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
5import dataclasses 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
6import re 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
7import sys 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
8import types 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
9import typing 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
10import warnings 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
11from collections.abc import Callable 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
12from functools import partial 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
13from types import GetSetDescriptorType 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
14from typing import TYPE_CHECKING, Any, Final 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
16from typing_extensions import Annotated, Literal, TypeAliasType, TypeGuard, deprecated, get_args, get_origin 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
18if TYPE_CHECKING: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
19 from ._dataclasses import StandardDataclass
21try: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
22 from typing import _TypingBase # type: ignore[attr-defined] 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
23except ImportError: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
24 from typing import _Final as _TypingBase # type: ignore[attr-defined] 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
26typing_base = _TypingBase 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
29if sys.version_info < (3, 9): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
30 # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on)
31 TypingGenericAlias = () 1hijkGlm
32else:
33 from typing import GenericAlias as TypingGenericAlias # type: ignore 1bcnouvwxatdepqyzABHIJKLMNOfgrsCDEF
36if sys.version_info < (3, 11): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
37 from typing_extensions import NotRequired, Required 1hibcnoatjkdepqGHIJKLMlmfgrs
38else:
39 from typing import NotRequired, Required # noqa: F401 1uvwxyzABNOCDEF
42if sys.version_info < (3, 10): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
44 def origin_is_union(tp: type[Any] | None) -> bool: 1hibcajkdeGHlmfg
45 return tp is typing.Union 1hibcajkdeGHlmfg
47 WithArgsTypes = (TypingGenericAlias,) 1hibcajkdeGHlmfg
49else:
51 def origin_is_union(tp: type[Any] | None) -> bool: 1nouvwxtpqyzABIJKLMNOrsCDEF
52 return tp is typing.Union or tp is types.UnionType 1nouvwxtpqyzABIJKLMNOrsCDEF
54 WithArgsTypes = typing._GenericAlias, types.GenericAlias, types.UnionType # type: ignore[attr-defined] 1nouvwxtpqyzABIJKLMNOrsCDEF
57if sys.version_info < (3, 10): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
58 NoneType = type(None) 1hibcajkdeGHlmfg
59 EllipsisType = type(Ellipsis) 1hibcajkdeGHlmfg
60else:
61 from types import NoneType as NoneType 1nouvwxtpqyzABIJKLMNOrsCDEF
64LITERAL_TYPES: set[Any] = {Literal} 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
65if hasattr(typing, 'Literal'): 65 ↛ 69line 65 didn't jump to line 69, because the condition on line 65 was always true1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
66 LITERAL_TYPES.add(typing.Literal) # type: ignore 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
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 () 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
70if hasattr(warnings, 'deprecated'): 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
71 DEPRECATED_TYPES = (*DEPRECATED_TYPES, warnings.deprecated) # type: ignore
73NONE_TYPES: tuple[Any, ...] = (None, NoneType, *(tp[None] for tp in LITERAL_TYPES)) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
76TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
79def is_none_type(type_: Any) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
80 return type_ in NONE_TYPES 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
83def is_callable_type(type_: type[Any]) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
84 return type_ is Callable or get_origin(type_) is Callable 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
87def is_literal_type(type_: type[Any]) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
88 return Literal is not None and get_origin(type_) in LITERAL_TYPES 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
91def is_deprecated_instance(instance: Any) -> TypeGuard[deprecated]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
92 return isinstance(instance, DEPRECATED_TYPES) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
95def literal_values(type_: type[Any]) -> tuple[Any, ...]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
96 return get_args(type_) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
99def all_literal_values(type_: type[Any]) -> list[Any]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
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_): 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
105 return [type_] 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
107 values = literal_values(type_) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
108 return list(x for value in values for x in all_literal_values(value)) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
111def is_annotated(ann_type: Any) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
112 from ._utils import lenient_issubclass 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
114 origin = get_origin(ann_type) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
115 return origin is not None and lenient_issubclass(origin, Annotated) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
118def annotated_type(type_: Any) -> Any | None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
119 return get_args(type_)[0] if is_annotated(type_) else None 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
122def is_namedtuple(type_: type[Any]) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
123 """Check if a given class is a named tuple.
124 It can be either a `typing.NamedTuple` or `collections.namedtuple`.
125 """
126 from ._utils import lenient_issubclass 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
128 return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields') 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
131test_new_type = typing.NewType('test_new_type', str) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
134def is_new_type(type_: type[Any]) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
135 """Check whether type_ was created using typing.NewType.
137 Can't use isinstance because it fails <3.10.
138 """
139 return isinstance(type_, test_new_type.__class__) and hasattr(type_, '__supertype__') # type: ignore[arg-type] 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
142def _check_classvar(v: type[Any] | None) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
143 if v is None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
144 return False 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
146 return v.__class__ == typing.ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar' 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
149def is_classvar(ann_type: type[Any]) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
150 if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
151 return True 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
153 # this is an ugly workaround for class vars that contain forward references and are therefore themselves
154 # forward references, see #3679
155 if ann_type.__class__ == typing.ForwardRef and re.match( 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
156 r'(\w+\.)?ClassVar\[',
157 ann_type.__forward_arg__, # type: ignore
158 ):
159 return True 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
161 return False 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
164def _check_finalvar(v: type[Any] | None) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
165 """Check if a given type is a `typing.Final` type."""
166 if v is None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
167 return False 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
169 return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final') 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
172def is_finalvar(ann_type: Any) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
173 return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type)) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
176def parent_frame_namespace(*, parent_depth: int = 2) -> dict[str, Any] | None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
177 """We allow use of items in parent namespace to get around the issue with `get_type_hints` only looking in the
178 global module namespace. See https://github.com/pydantic/pydantic/issues/2678#issuecomment-1008139014 -> Scope
179 and suggestion at the end of the next comment by @gvanrossum.
181 WARNING 1: it matters exactly where this is called. By default, this function will build a namespace from the
182 parent of where it is called.
184 WARNING 2: this only looks in the parent namespace, not other parents since (AFAIK) there's no way to collect a
185 dict of exactly what's in scope. Using `f_back` would work sometimes but would be very wrong and confusing in many
186 other cases. See https://discuss.python.org/t/is-there-a-way-to-access-parent-nested-namespaces/20659.
187 """
188 frame = sys._getframe(parent_depth) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
189 # if f_back is None, it's the global module namespace and we don't need to include it here
190 if frame.f_back is None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
191 return None 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
192 else:
193 return frame.f_locals 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
196def add_module_globals(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
197 module_name = getattr(obj, '__module__', None) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
198 if module_name: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
199 try: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
200 module_globalns = sys.modules[module_name].__dict__ 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
201 except KeyError: 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
202 # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363
203 pass 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
204 else:
205 if globalns: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
206 return {**module_globalns, **globalns} 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
207 else:
208 # copy module globals to make sure it can't be updated later
209 return module_globalns.copy() 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
211 return globalns or {} 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
214def get_cls_types_namespace(cls: type[Any], parent_namespace: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
215 ns = add_module_globals(cls, parent_namespace) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
216 ns[cls.__name__] = cls 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
217 return ns 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
220def get_cls_type_hints_lenient(obj: Any, globalns: dict[str, Any] | None = None) -> dict[str, Any]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
221 """Collect annotations from a class, including those from parent classes.
223 Unlike `typing.get_type_hints`, this function will not error if a forward reference is not resolvable.
224 """
225 hints = {} 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
226 for base in reversed(obj.__mro__): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
227 ann = base.__dict__.get('__annotations__') 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
228 localns = dict(vars(base)) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
229 if ann is not None and ann is not GetSetDescriptorType: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
230 for name, value in ann.items(): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
231 hints[name] = eval_type_lenient(value, globalns, localns) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
232 return hints 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
235def eval_type_lenient(value: Any, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None) -> Any: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
236 """Behaves like typing._eval_type, except it won't raise an error if a forward reference can't be resolved."""
237 if value is None: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
238 value = NoneType 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
239 elif isinstance(value, str): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
240 value = _make_forward_ref(value, is_argument=False, is_class=True) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
242 try: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
243 return eval_type_backport(value, globalns, localns) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
244 except NameError: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
245 # the point of this function is to be tolerant to this case
246 return value 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
249def eval_type_backport( 1hibcnouvwxjkdepqyzABGHIJKLMNOlmfgrsCDEF
250 value: Any, globalns: dict[str, Any] | None = None, localns: dict[str, 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: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
259 return typing._eval_type( # type: ignore 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
260 value, globalns, localns
261 )
262 except TypeError as e: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
263 if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)): 1hibcnoatjkdepqlmfgrs
264 raise 1hibcnoatjkdepqlmfgrs
265 try: 1hibcajkdelmfg
266 from eval_type_backport import eval_type_backport 1hibcajkdelmfg
267 except ImportError: 1hibcajkdelmfg
268 raise TypeError( 1hibcajkdelmfg
269 f'You have a type annotation {value.__forward_arg__!r} '
270 f'which makes use of newer typing features than are supported in your version of Python. '
271 f'To handle this error, you should either remove the use of new syntax '
272 f'or install the `eval_type_backport` package.'
273 ) from e
275 return eval_type_backport(value, globalns, localns, try_default=False) 1hibcajkdelmfg
278def is_backport_fixable_error(e: TypeError) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
279 msg = str(e) 1hibcnoatjkdepqlmfgrs
280 return msg.startswith('unsupported operand type(s) for |: ') or "' object is not subscriptable" in msg 1hibcnoatjkdepqlmfgrs
283def get_function_type_hints( 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
284 function: Callable[..., Any], *, include_keys: set[str] | None = None, types_namespace: dict[str, Any] | None = None
285) -> dict[str, Any]:
286 """Like `typing.get_type_hints`, but doesn't convert `X` to `Optional[X]` if the default value is `None`, also
287 copes with `partial`.
288 """
289 try: 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
290 if isinstance(function, partial): 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
291 annotations = function.func.__annotations__ 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
292 else:
293 annotations = function.__annotations__ 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
294 except AttributeError: 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
295 type_hints = get_type_hints(function) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
296 if isinstance(function, type): 296 ↛ 301line 296 didn't jump to line 301, because the condition on line 296 was always true1hibcnouvwxatjkdepqyzABlmfgrsCDEF
297 # `type[...]` is a callable, which returns an instance of itself.
298 # At some point, we might even look into the return type of `__new__`
299 # if it returns something else.
300 type_hints.setdefault('return', function) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
301 return type_hints 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
303 globalns = add_module_globals(function) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
304 type_hints = {} 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
305 for name, value in annotations.items(): 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
306 if include_keys is not None and name not in include_keys: 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
307 continue 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
308 if value is None: 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
309 value = NoneType 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
310 elif isinstance(value, str): 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
311 value = _make_forward_ref(value) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
313 type_hints[name] = eval_type_backport(value, globalns, types_namespace) 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
315 return type_hints 1hibcnouvwxatjkdepqyzABlmfgrsCDEF
318if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
320 def _make_forward_ref( 1hijkGlm
321 arg: Any,
322 is_argument: bool = True,
323 *,
324 is_class: bool = False,
325 ) -> typing.ForwardRef:
326 """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions.
327 The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below.
329 See https://github.com/python/cpython/pull/28560 for some background.
330 The backport happened on 3.9.8, see:
331 https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458,
332 and on 3.10.1 for the 3.10 branch, see:
333 https://github.com/pydantic/pydantic/issues/6912
335 Implemented as EAFP with memory.
336 """
337 return typing.ForwardRef(arg, is_argument) 1hijkGlm
339else:
340 _make_forward_ref = typing.ForwardRef 1bcnouvwxatdepqyzABHIJKLMNOfgrsCDEF
343if sys.version_info >= (3, 10): 343 ↛ 347line 343 didn't jump to line 347, because the condition on line 343 was always true1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
344 get_type_hints = typing.get_type_hints 1nouvwxtpqyzABIJKLMNOrsCDEF
346else:
347 """
348 For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to
349 the implementation in CPython 3.10.8.
350 """
352 @typing.no_type_check 1hibcajkdeGHlmfg
353 def get_type_hints( # noqa: C901 1hibcjkdeGHlmfg
354 obj: Any, 1a
355 globalns: dict[str, Any] | None = None, 1a
356 localns: dict[str, Any] | None = None, 1a
357 include_extras: bool = False, 1a
358 ) -> dict[str, Any]: # pragma: no cover 1a
359 """Taken verbatim from python 3.10.8 unchanged, except:
360 * type annotations of the function definition above.
361 * prefixing `typing.` where appropriate
362 * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument.
364 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
366 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
367 ======================================================
369 Return type hints for an object.
371 This is often the same as obj.__annotations__, but it handles
372 forward references encoded as string literals, adds Optional[t] if a
373 default value equal to None is set and recursively replaces all
374 'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
376 The argument may be a module, class, method, or function. The annotations
377 are returned as a dictionary. For classes, annotations include also
378 inherited members.
380 TypeError is raised if the argument is not of a type that can contain
381 annotations, and an empty dictionary is returned if no annotations are
382 present.
384 BEWARE -- the behavior of globalns and localns is counterintuitive
385 (unless you are familiar with how eval() and exec() work). The
386 search order is locals first, then globals.
388 - If no dict arguments are passed, an attempt is made to use the
389 globals from obj (or the respective module's globals for classes),
390 and these are also used as the locals. If the object does not appear
391 to have globals, an empty dictionary is used. For classes, the search
392 order is globals first then locals.
394 - If one dict argument is passed, it is used for both globals and
395 locals.
397 - If two dict arguments are passed, they specify globals and
398 locals, respectively.
399 """
400 if getattr(obj, '__no_type_check__', None): 1hibcajkdelmfg
401 return {}
402 # Classes require a special treatment.
403 if isinstance(obj, type): 1hibcajkdelmfg
404 hints = {} 1hibcajkdelmfg
405 for base in reversed(obj.__mro__): 1hibcajkdelmfg
406 if globalns is None: 1hibcajkdelmfg
407 base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {}) 1hibcajkdelmfg
408 else:
409 base_globals = globalns 1hibcajkdelmfg
410 ann = base.__dict__.get('__annotations__', {}) 1hibcajkdelmfg
411 if isinstance(ann, types.GetSetDescriptorType): 1hibcajkdelmfg
412 ann = {}
413 base_locals = dict(vars(base)) if localns is None else localns 1hibcajkdelmfg
414 if localns is None and globalns is None: 1hibcajkdelmfg
415 # This is surprising, but required. Before Python 3.10,
416 # get_type_hints only evaluated the globalns of
417 # a class. To maintain backwards compatibility, we reverse
418 # the globalns and localns order so that eval() looks into
419 # *base_globals* first rather than *base_locals*.
420 # This only affects ForwardRefs.
421 base_globals, base_locals = base_locals, base_globals 1hibcajkdelmfg
422 for name, value in ann.items(): 1hibcajkdelmfg
423 if value is None: 1hibcajkdelmfg
424 value = type(None)
425 if isinstance(value, str): 1hibcajkdelmfg
426 value = _make_forward_ref(value, is_argument=False, is_class=True) 1hibcajkdelmfg
428 value = eval_type_backport(value, base_globals, base_locals) 1hibcajkdelmfg
429 hints[name] = value 1hibcajkdelmfg
430 if not include_extras and hasattr(typing, '_strip_annotations'): 1hibcajkdelmfg
431 return { 1bcadefg
432 k: typing._strip_annotations(t) # type: ignore 1bcadefg
433 for k, t in hints.items() 1bcadefg
434 }
435 else:
436 return hints 1hibcajkdelmfg
438 if globalns is None: 1hibcajkdelmfg
439 if isinstance(obj, types.ModuleType): 1hibcajkdelmfg
440 globalns = obj.__dict__
441 else:
442 nsobj = obj 1hibcajkdelmfg
443 # Find globalns for the unwrapped object.
444 while hasattr(nsobj, '__wrapped__'): 1hibcajkdelmfg
445 nsobj = nsobj.__wrapped__
446 globalns = getattr(nsobj, '__globals__', {}) 1hibcajkdelmfg
447 if localns is None: 1hibcajkdelmfg
448 localns = globalns 1hibcajkdelmfg
449 elif localns is None:
450 localns = globalns
451 hints = getattr(obj, '__annotations__', None) 1hibcajkdelmfg
452 if hints is None: 1hibcajkdelmfg
453 # Return empty annotations for something that _could_ have them.
454 if isinstance(obj, typing._allowed_types): # type: ignore
455 return {}
456 else:
457 raise TypeError(f'{obj!r} is not a module, class, method, ' 'or function.')
458 defaults = typing._get_defaults(obj) # type: ignore 1hibcajkdelmfg
459 hints = dict(hints) 1hibcajkdelmfg
460 for name, value in hints.items(): 1hibcajkdelmfg
461 if value is None: 1hibcajkdelmfg
462 value = type(None)
463 if isinstance(value, str): 1hibcajkdelmfg
464 # class-level forward refs were handled above, this must be either
465 # a module-level annotation or a function argument annotation
467 value = _make_forward_ref( 1hibcajkdelmfg
468 value, 1hibcajkdelmfg
469 is_argument=not isinstance(obj, types.ModuleType), 1hibcajkdelmfg
470 is_class=False, 1hibcajkdelmfg
471 )
472 value = eval_type_backport(value, globalns, localns) 1hibcajkdelmfg
473 if name in defaults and defaults[name] is None: 1hibcajkdelmfg
474 value = typing.Optional[value]
475 hints[name] = value 1hibcajkdelmfg
476 return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore 1hibcajkdelmfg
479def is_dataclass(_cls: type[Any]) -> TypeGuard[type[StandardDataclass]]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
480 # The dataclasses.is_dataclass function doesn't seem to provide TypeGuard functionality,
481 # so I created this convenience function
482 return dataclasses.is_dataclass(_cls) 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
485def origin_is_type_alias_type(origin: Any) -> TypeGuard[TypeAliasType]: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
486 return isinstance(origin, TypeAliasType)
489if sys.version_info >= (3, 10): 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
491 def is_generic_alias(type_: type[Any]) -> bool: 1nouvwxtpqyzABIJKLMNOrsCDEF
492 return isinstance(type_, (types.GenericAlias, typing._GenericAlias)) # type: ignore[attr-defined] 1nouvwxtpqyzABIJKLMNOrsCDEF
494else:
496 def is_generic_alias(type_: type[Any]) -> bool: 1hibcajkdeGHlmfg
497 return isinstance(type_, typing._GenericAlias) # type: ignore 1hibcajkdeGHlmfg
500def is_self_type(tp: Any) -> bool: 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF
501 """Check if a given class is a Self type (from `typing` or `typing_extensions`)"""
502 return isinstance(tp, typing_base) and getattr(tp, '_name', None) == 'Self' 1hibcnouvwxatjkdepqyzABGHIJKLMNOlmfgrsCDEF