Coverage for pydantic/_internal/_typing_extra.py: 96.90%
319 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-13 19:35 +0000
1"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap Python's typing module."""
3from __future__ import annotations 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
5import collections.abc 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
6import re 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
7import sys 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
8import types 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
9import typing 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
10import warnings 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
11from functools import partial 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
12from typing import TYPE_CHECKING, Any, Callable, cast 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
13from zoneinfo import ZoneInfo 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
15import typing_extensions 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
16from typing_extensions import ParamSpec, TypeAliasType, TypeIs, deprecated, get_args, get_origin 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
18from pydantic.version import version_short 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
20from ._namespace_utils import GlobalsNamespace, MappingNamespace, NsResolver, get_module_ns_of 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
22if sys.version_info < (3, 10): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
23 NoneType = type(None) 1abgcdMef
24 EllipsisType = type(Ellipsis) 1abgFcdMef
25else:
26 from types import EllipsisType as EllipsisType 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
27 from types import NoneType as NoneType 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
29if TYPE_CHECKING: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
30 from pydantic import BaseModel
32# As per https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types,
33# always check for both `typing` and `typing_extensions` variants of a typing construct.
34# (this is implemented differently than the suggested approach in the `typing_extensions`
35# docs for performance).
37_t_any = typing.Any 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
38_te_any = typing_extensions.Any 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
41def is_any(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
42 """Return whether the provided argument is the `Any` special form.
44 ```python {test="skip" lint="skip"}
45 is_any(Any)
46 #> True
47 ```
48 """
49 return tp is _t_any or tp is _te_any 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
52_t_union = typing.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
53_te_union = typing_extensions.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
56def is_union(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
57 """Return whether the provided argument is a `Union` special form.
59 ```python {test="skip" lint="skip"}
60 is_union(Union[int, str])
61 #> True
62 is_union(int | str)
63 #> False
64 ```
65 """
66 origin = get_origin(tp) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
67 return origin is _t_union or origin is _te_union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
70_t_literal = typing.Literal 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
71_te_literal = typing_extensions.Literal 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
74def is_literal(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
75 """Return whether the provided argument is a `Literal` special form.
77 ```python {test="skip" lint="skip"}
78 is_literal(Literal[42])
79 #> True
80 ```
81 """
82 origin = get_origin(tp) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
83 return origin is _t_literal or origin is _te_literal 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
86def literal_values(tp: Any, /) -> list[Any]: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
87 """Return the values contained in the provided `Literal` special form.
89 If one of the literal values is a PEP 695 type alias, recursively parse
90 the type alias' `__value__` to unpack literal values as well. This function
91 *doesn't* check that the type alias is referencing a `Literal` special form,
92 so unexpected values could be unpacked.
93 """
94 if not is_literal(tp): 1abzAtunohigFcdBCvwpqjkefDExyrslm
95 # Note: we could also check for generic aliases with a type alias as an origin.
96 # However, it is very unlikely that this happens as type variables can't appear in
97 # `Literal` forms, so the only valid (but unnecessary) use case would be something like:
98 # `type Test[T] = Literal['whatever']` (and then use `Test[SomeType]`).
99 if is_type_alias_type(tp): 1abzAtunohigFcdBCvwpqjkefDExyrslm
100 # Note: accessing `__value__` could raise a `NameError`, but we just let
101 # the exception be raised as there's not much we can do if this happens.
102 return literal_values(tp.__value__) 1abzAtunohigFcdBCvwpqjkefDExyrslm
104 return [tp] 1abzAtunohigFcdBCvwpqjkefDExyrslm
106 values = get_args(tp) 1abzAtunohigFcdBCvwpqjkefDExyrslm
107 return [x for value in values for x in literal_values(value)] 1abzAtunohigFcdBCvwpqjkefDExyrslm
110_t_annotated = typing.Annotated 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
111_te_annotated = typing_extensions.Annotated 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
114def is_annotated(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
115 """Return whether the provided argument is a `Annotated` special form.
117 ```python {test="skip" lint="skip"}
118 is_annotated(Annotated[int, ...])
119 #> True
120 ```
121 """
122 origin = get_origin(tp) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
123 return origin is _t_annotated or origin is _te_annotated 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
126def annotated_type(tp: Any, /) -> Any | None: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
127 """Return the type of the `Annotated` special form, or `None`."""
128 return get_args(tp)[0] if is_annotated(tp) else None 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
131def unpack_annotated(annotation: Any, /) -> tuple[Any, list[Any]]: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
132 """Unpack the annotation if it is wrapped with the `Annotated` type qualifier.
134 This function also unpacks PEP 695 type aliases if necessary (and also generic
135 aliases with a PEP 695 type alias origin). However, it does *not* try to evaluate
136 forward references, so users should make sure the type alias' `__value__` does not
137 contain unresolvable forward references.
139 Example:
140 ```python {test="skip" lint="skip"}
141 from typing import Annotated
143 type InnerList[T] = Annotated[list[T], 'meta_1']
144 type MyList[T] = Annotated[InnerList[T], 'meta_2']
145 type MyIntList = MyList[int]
147 _unpack_annotated(MyList)
148 #> (list[T], ['meta_1', 'meta_2'])
149 _unpack_annotated(MyList[int])
150 #> (list[int], ['meta_1', 'meta_2'])
151 _unpack_annotated(MyIntList)
152 #> (list[int], ['meta_1', 'meta_2'])
153 ```
155 Returns:
156 A two-tuple, the first element is the annotated type and the second element
157 is a list containing the annotated metadata. If the annotation wasn't
158 wrapped with `Annotated` in the first place, it is returned as is and the
159 metadata list is empty.
160 """
161 if is_annotated(annotation): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
162 typ, *metadata = get_args(annotation) 1abzAtunohigFcdBCvwpqjkefDExyrslm
163 # The annotated type might be a PEP 695 type alias, so we need to recursively
164 # unpack it. Note that we could make an optimization here: the following next
165 # call to `_unpack_annotated` could omit the `is_annotated` check, because Python
166 # already flattens `Annotated[Annotated[<type>, ...], ...]` forms. However, we would
167 # need to "re-enable" the check for further recursive calls.
168 typ, sub_meta = unpack_annotated(typ) 1abzAtunohigFcdBCvwpqjkefDExyrslm
169 metadata = sub_meta + metadata 1abzAtunohigFcdBCvwpqjkefDExyrslm
170 return typ, metadata 1abzAtunohigFcdBCvwpqjkefDExyrslm
171 elif is_type_alias_type(annotation): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
172 try: 1abzAtunohigFcdBCvwpqjkefDExyrslm
173 value = annotation.__value__ 1abzAtunohigFcdBCvwpqjkefDExyrslm
174 except NameError:
175 # The type alias value contains an unresolvable reference. Note that even if it
176 # resolves successfully, it might contain string annotations, and because of design
177 # limitations we don't evaluate the type (we don't have access to a `NsResolver` instance).
178 pass
179 else:
180 typ, metadata = unpack_annotated(value) 1abzAtunohigFcdBCvwpqjkefDExyrslm
181 if metadata: 1abzAtunohigFcdBCvwpqjkefDExyrslm
182 # Having metadata means the type alias' `__value__` was an `Annotated` form
183 # (or, recursively, a type alias to an `Annotated` form). It is important to
184 # check for this as we don't want to unpack "normal" type aliases (e.g. `type MyInt = int`).
185 return typ, metadata 1abzAtunohigFcdBCvwpqjkefDExyrslm
186 return annotation, [] 1abzAtunohigFcdBCvwpqjkefDExyrslm
187 elif is_generic_alias(annotation): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
188 # When parametrized, a PEP 695 type alias becomes a generic alias
189 # (e.g. with `type MyList[T] = Annotated[list[T], ...]`, `MyList[int]`
190 # is a generic alias).
191 origin = get_origin(annotation) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
192 if is_type_alias_type(origin): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
193 try: 1abzAtunohigFcdBCvwpqjkefDExyrslm
194 value = origin.__value__ 1abzAtunohigFcdBCvwpqjkefDExyrslm
195 except NameError:
196 pass
197 else:
198 # Circular import (note that these two functions should probably be defined in `_typing_extra`):
199 from ._generics import get_standard_typevars_map, replace_types 1abzAtunohigFcdBCvwpqjkefDExyrslm
201 # While Python already handles type variable replacement for simple `Annotated` forms,
202 # we need to manually apply the same logic for PEP 695 type aliases:
203 # - With `MyList = Annotated[list[T], ...]`, `MyList[int] == Annotated[list[int], ...]`
204 # - With `type MyList = Annotated[list[T], ...]`, `MyList[int].__value__ == Annotated[list[T], ...]`.
205 value = replace_types(value, get_standard_typevars_map(annotation)) 1abzAtunohigFcdBCvwpqjkefDExyrslm
206 typ, metadata = unpack_annotated(value) 1abzAtunohigFcdBCvwpqjkefDExyrslm
207 if metadata: 1abzAtunohigFcdBCvwpqjkefDExyrslm
208 return typ, metadata 1abzAtunohigFcdBCvwpqjkefDExyrslm
209 return annotation, [] 1abzAtunohigFcdBCvwpqjkefDExyrslm
211 return annotation, [] 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
214_te_unpack = typing_extensions.Unpack 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
215_te_self = typing_extensions.Self 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
216_te_required = typing_extensions.Required 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
217_te_notrequired = typing_extensions.NotRequired 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
218_te_never = typing_extensions.Never 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
220if sys.version_info >= (3, 11): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
221 _t_unpack = typing.Unpack 1tunohivwpqjkKHIJGxyrslm
222 _t_self = typing.Self 1tunohivwpqjkKHIJGxyrslm
223 _t_required = typing.Required 1tunohivwpqjkKHIJGxyrslm
224 _t_notrequired = typing.NotRequired 1tunohivwpqjkKHIJGxyrslm
225 _t_never = typing.Never 1tunohivwpqjkKHIJGxyrslm
227 def is_unpack(tp: Any, /) -> bool: 1tunohivwpqjkKHIJGxyrslm
228 """Return whether the provided argument is a `Unpack` special form.
230 ```python {test="skip" lint="skip"}
231 is_unpack(Unpack[Ts])
232 #> True
233 ```
234 """
235 origin = get_origin(tp) 1tunohivwpqjkxyrslm
236 return origin is _t_unpack or origin is _te_unpack 1tunohivwpqjkxyrslm
238 def is_self(tp: Any, /) -> bool: 1tunohivwpqjkKHIJGxyrslm
239 """Return whether the provided argument is the `Self` special form.
241 ```python {test="skip" lint="skip"}
242 is_self(Self)
243 #> True
244 ```
245 """
246 return tp is _t_self or tp is _te_self 1tunohivwpqjkKHIJGxyrslm
248 def is_required(tp: Any, /) -> bool: 1tunohivwpqjkKHIJGxyrslm
249 """Return whether the provided argument is a `Required` special form.
251 ```python {test="skip" lint="skip"}
252 is_required(Required[int])
253 #> True
254 """
255 origin = get_origin(tp) 1tunohivwpqjkxyrslm
256 return origin is _t_required or origin is _te_required 1tunohivwpqjkxyrslm
258 def is_not_required(tp: Any, /) -> bool: 1tunohivwpqjkKHIJGxyrslm
259 """Return whether the provided argument is a `NotRequired` special form.
261 ```python {test="skip" lint="skip"}
262 is_required(Required[int])
263 #> True
264 """
265 origin = get_origin(tp) 1tunohivwpqjkxyrslm
266 return origin is _t_notrequired or origin is _te_notrequired 1tunohivwpqjkxyrslm
268 def is_never(tp: Any, /) -> bool: 1tunohivwpqjkKHIJGxyrslm
269 """Return whether the provided argument is the `Never` special form.
271 ```python {test="skip" lint="skip"}
272 is_never(Never)
273 #> True
274 ```
275 """
276 return tp is _t_never or tp is _te_never 1tunohivwpqjkxyrslm
278else:
280 def is_unpack(tp: Any, /) -> bool: 1abzAgFcdBCMLefDE
281 """Return whether the provided argument is a `Unpack` special form.
283 ```python {test="skip" lint="skip"}
284 is_unpack(Unpack[Ts])
285 #> True
286 ```
287 """
288 origin = get_origin(tp) 1abzAgFcdBCefDE
289 return origin is _te_unpack 1abzAgFcdBCefDE
291 def is_self(tp: Any, /) -> bool: 1abzAgFcdBCMLefDE
292 """Return whether the provided argument is the `Self` special form.
294 ```python {test="skip" lint="skip"}
295 is_self(Self)
296 #> True
297 ```
298 """
299 return tp is _te_self 1abzAgFcdBCMLefDE
301 def is_required(tp: Any, /) -> bool: 1abzAgFcdBCMLefDE
302 """Return whether the provided argument is a `Required` special form.
304 ```python {test="skip" lint="skip"}
305 is_required(Required[int])
306 #> True
307 """
308 origin = get_origin(tp) 1abzAgFcdBCefDE
309 return origin is _te_required 1abzAgFcdBCefDE
311 def is_not_required(tp: Any, /) -> bool: 1abzAgFcdBCMLefDE
312 """Return whether the provided argument is a `NotRequired` special form.
314 ```python {test="skip" lint="skip"}
315 is_required(Required[int])
316 #> True
317 """
318 origin = get_origin(tp) 1abzAgFcdBCefDE
319 return origin is _te_notrequired 1abzAgFcdBCefDE
321 def is_never(tp: Any, /) -> bool: 1abzAgFcdBCMLefDE
322 """Return whether the provided argument is the `Never` special form.
324 ```python {test="skip" lint="skip"}
325 is_never(Never)
326 #> True
327 ```
328 """
329 return tp is _te_never 1abzAgFcdBCefDE
332def unpack_type(tp: Any, /) -> Any | None: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
333 """Return the type wrapped by the `Unpack` special form, or `None`."""
334 return get_args(tp)[0] if is_unpack(tp) else None 1abzAtunohigFcdBCvwpqjkefDExyrslm
337def is_new_type(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
338 """Return whether the provided argument is a `NewType`.
340 ```python {test="skip" lint="skip"}
341 is_new_type(NewType('MyInt', int))
342 #> True
343 ```
344 """
345 if sys.version_info < (3, 10): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
346 # On Python < 3.10, `typing.NewType` is a function
347 return hasattr(tp, '__supertype__') 1abgcdMef
348 else:
349 return type(tp) is typing.NewType or type(tp) is typing_extensions.NewType 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
352def is_hashable(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
353 """Return whether the provided argument is the `Hashable` class.
355 ```python {test="skip" lint="skip"}
356 is_hashable(Hashable)
357 #> True
358 ```
359 """
360 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
361 # hence the second check:
362 return tp is collections.abc.Hashable or get_origin(tp) is collections.abc.Hashable 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
365def is_callable(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
366 """Return whether the provided argument is a `Callable`, parametrized or not.
368 ```python {test="skip" lint="skip"}
369 is_callable(Callable[[int], str])
370 #> True
371 is_callable(typing.Callable)
372 #> True
373 is_callable(collections.abc.Callable)
374 #> True
375 ```
376 """
377 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
378 # hence the second check:
379 return tp is collections.abc.Callable or get_origin(tp) is collections.abc.Callable 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
382_PARAMSPEC_TYPES: tuple[type[ParamSpec], ...] = (typing_extensions.ParamSpec,) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
383if sys.version_info >= (3, 10): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
384 _PARAMSPEC_TYPES = (*_PARAMSPEC_TYPES, typing.ParamSpec) # pyright: ignore[reportAssignmentType] 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
387def is_paramspec(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
388 """Return whether the provided argument is a `ParamSpec`.
390 ```python {test="skip" lint="skip"}
391 P = ParamSpec('P')
392 is_paramspec(P)
393 #> True
394 ```
395 """
396 return isinstance(tp, _PARAMSPEC_TYPES)
399_TYPE_ALIAS_TYPES: tuple[type[TypeAliasType], ...] = (typing_extensions.TypeAliasType,) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
400if sys.version_info >= (3, 12): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
401 _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) 1nohipqjkHIJGrslm
403_IS_PY310 = sys.version_info[:2] == (3, 10) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
406def is_type_alias_type(tp: Any, /) -> TypeIs[TypeAliasType]: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
407 """Return whether the provided argument is an instance of `TypeAliasType`.
409 ```python {test="skip" lint="skip"}
410 type Int = int
411 is_type_alias_type(Int)
412 #> True
413 Str = TypeAliasType('Str', str)
414 is_type_alias_type(Str)
415 #> True
416 ```
417 """
418 if _IS_PY310: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
419 # Parametrized PEP 695 type aliases are instances of `types.GenericAlias` in typing_extensions>=4.13.0.
420 # On Python 3.10, with `Alias[int]` being such an instance of `GenericAlias`,
421 # `isinstance(Alias[int], TypeAliasType)` returns `True`.
422 # See https://github.com/python/cpython/issues/89828.
423 return type(tp) is not types.GenericAlias and isinstance(tp, _TYPE_ALIAS_TYPES) 1zAFBCLDE
424 else:
425 return isinstance(tp, _TYPE_ALIAS_TYPES) 1abtunohigcdvwpqjkMKHIJGefxyrslm
428_t_classvar = typing.ClassVar 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
429_te_classvar = typing_extensions.ClassVar 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
432def is_classvar(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
433 """Return whether the provided argument is a `ClassVar` special form, parametrized or not.
435 Note that in most cases, you will want to use the `is_classvar_annotation` function,
436 which is used to check if an annotation (in the context of a Pydantic model or dataclass)
437 should be treated as being a class variable.
439 ```python {test="skip" lint="skip"}
440 is_classvar(ClassVar[int])
441 #> True
442 is_classvar(ClassVar)
443 #> True
444 """
445 # ClassVar is not necessarily parametrized:
446 if tp is _t_classvar or tp is _te_classvar: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
447 return True 1abzAtunohigFcdBCvwpqjkefDExyrslm
448 origin = get_origin(tp) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
449 return origin is _t_classvar or origin is _te_classvar 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
452_classvar_re = re.compile(r'((\w+\.)?Annotated\[)?(\w+\.)?ClassVar\[') 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
455def is_classvar_annotation(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
456 """Return whether the provided argument represents a class variable annotation.
458 Although not explicitly stated by the typing specification, `ClassVar` can be used
459 inside `Annotated` and as such, this function checks for this specific scenario.
461 Because this function is used to detect class variables before evaluating forward references
462 (or because evaluation failed), we also implement a naive regex match implementation. This is
463 required because class variables are inspected before fields are collected, so we try to be
464 as accurate as possible.
465 """
466 if is_classvar(tp) or (anntp := annotated_type(tp)) is not None and is_classvar(anntp): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
467 return True 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
469 str_ann: str | None = None 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
470 if isinstance(tp, typing.ForwardRef): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
471 str_ann = tp.__forward_arg__ 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
472 if isinstance(tp, str): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
473 str_ann = tp 1abzAtunohigFcdBCvwpqjkefDExyrslm
475 if str_ann is not None and _classvar_re.match(str_ann): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
476 # stdlib dataclasses do something similar, although a bit more advanced
477 # (see `dataclass._is_type`).
478 return True 1abzAtunohigFcdBCvwpqjkefDExyrslm
480 return False 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
483_t_final = typing.Final 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
484_te_final = typing_extensions.Final 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
487# TODO implement `is_finalvar_annotation` as Final can be wrapped with other special forms:
488def is_finalvar(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
489 """Return whether the provided argument is a `Final` special form, parametrized or not.
491 ```python {test="skip" lint="skip"}
492 is_finalvar(Final[int])
493 #> True
494 is_finalvar(Final)
495 #> True
496 """
497 # Final is not necessarily parametrized:
498 if tp is _t_final or tp is _te_final: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
499 return True 1abzAtunohigFcdBCvwpqjkefDExyrslm
500 origin = get_origin(tp) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
501 return origin is _t_final or origin is _te_final 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
504_t_noreturn = typing.NoReturn 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
505_te_noreturn = typing_extensions.NoReturn 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
508def is_no_return(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
509 """Return whether the provided argument is the `NoReturn` special form.
511 ```python {test="skip" lint="skip"}
512 is_no_return(NoReturn)
513 #> True
514 ```
515 """
516 return tp is _t_noreturn or tp is _te_noreturn 1abzAtunohigFcdBCvwpqjkefDExyrslm
519_DEPRECATED_TYPES: tuple[type[deprecated], ...] = (typing_extensions.deprecated,) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
520if hasattr(warnings, 'deprecated'): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
521 _DEPRECATED_TYPES = (*_DEPRECATED_TYPES, warnings.deprecated) # pyright: ignore[reportAttributeAccessIssue] 1hijkGlm
524def is_deprecated_instance(obj: Any, /) -> TypeIs[deprecated]: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
525 """Return whether the argument is an instance of the `warnings.deprecated` class or the `typing_extensions` backport."""
526 return isinstance(obj, _DEPRECATED_TYPES) 1abzAtunohigFcdBCvwpqjkefDExyrslm
529_NONE_TYPES: tuple[Any, ...] = (None, NoneType, typing.Literal[None], typing_extensions.Literal[None]) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
532def is_none_type(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
533 """Return whether the argument represents the `None` type as part of an annotation.
535 ```python {test="skip" lint="skip"}
536 is_none_type(None)
537 #> True
538 is_none_type(NoneType)
539 #> True
540 is_none_type(Literal[None])
541 #> True
542 is_none_type(type[None])
543 #> False
544 """
545 return tp in _NONE_TYPES 1abzAtunohigFcdBCvwpqjkefDExyrslm
548def is_namedtuple(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
549 """Return whether the provided argument is a named tuple class.
551 The class can be created using `typing.NamedTuple` or `collections.namedtuple`.
552 Parametrized generic classes are *not* assumed to be named tuples.
553 """
554 from ._utils import lenient_issubclass # circ. import 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
556 return lenient_issubclass(tp, tuple) and hasattr(tp, '_fields') 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
559def is_zoneinfo_type(tp: Any, /) -> TypeIs[type[ZoneInfo]]: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
560 """Return whether the provided argument is the `zoneinfo.ZoneInfo` type."""
561 return tp is ZoneInfo 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
564_t_union = typing.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
565_te_union = typing_extensions.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
567_t_union = typing.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
568_te_union = typing_extensions.Union 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
570if sys.version_info < (3, 10): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
572 def origin_is_union(tp: Any, /) -> bool: 1abgcdMef
573 """Return whether the provided argument is the `Union` special form."""
574 return tp is _t_union or tp is _te_union 1abgcdMef
576else:
578 def origin_is_union(tp: Any, /) -> bool: 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
579 """Return whether the provided argument is the `Union` special form or the `UnionType`."""
580 return tp is _t_union or tp is _te_union or tp is types.UnionType 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
583def is_generic_alias(tp: Any, /) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
584 return isinstance(tp, (types.GenericAlias, typing._GenericAlias)) # pyright: ignore[reportAttributeAccessIssue] 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
587# TODO: Ideally, we should avoid relying on the private `typing` constructs:
589if sys.version_info < (3, 10): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
590 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # pyright: ignore[reportAttributeAccessIssue] 1abgcdMef
591else:
592 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias, types.UnionType) # pyright: ignore[reportAttributeAccessIssue] 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
595# Similarly, we shouldn't rely on this `_Final` class, which is even more private than `_GenericAlias`:
596typing_base: Any = typing._Final # pyright: ignore[reportAttributeAccessIssue] 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
599### Annotation evaluations functions:
602def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
603 """Fetch the local namespace of the parent frame where this function is called.
605 Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace,
606 such as assignments inside a function. Using the standard library tools, it is currently not possible to resolve
607 such annotations:
609 ```python {lint="skip" test="skip"}
610 from typing import get_type_hints
612 def func() -> None:
613 Alias = int
615 class C:
616 a: 'Alias'
618 # Raises a `NameError: 'Alias' is not defined`
619 get_type_hints(C)
620 ```
622 Pydantic uses this function when a Pydantic model is being defined to fetch the parent frame locals. However,
623 this only allows us to fetch the parent frame namespace and not other parents (e.g. a model defined in a function,
624 itself defined in another function). Inspecting the next outer frames (using `f_back`) is not reliable enough
625 (see https://discuss.python.org/t/20659).
627 Because this function is mostly used to better resolve forward annotations, nothing is returned if the parent frame's
628 code object is defined at the module level. In this case, the locals of the frame will be the same as the module
629 globals where the class is defined (see `_namespace_utils.get_module_ns_of`). However, if you still want to fetch
630 the module globals (e.g. when rebuilding a model, where the frame where the rebuild call is performed might contain
631 members that you want to use for forward annotations evaluation), you can use the `force` parameter.
633 Args:
634 parent_depth: The depth at which to get the frame. Defaults to 2, meaning the parent frame where this function
635 is called will be used.
636 force: Whether to always return the frame locals, even if the frame's code object is defined at the module level.
638 Returns:
639 The locals of the namespace, or `None` if it was skipped as per the described logic.
640 """
641 frame = sys._getframe(parent_depth) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
643 if frame.f_code.co_name.startswith('<generic parameters of'): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
644 # As `parent_frame_namespace` is mostly called in `ModelMetaclass.__new__`,
645 # the parent frame can be the annotation scope if the PEP 695 generic syntax is used.
646 # (see https://docs.python.org/3/reference/executionmodel.html#annotation-scopes,
647 # https://docs.python.org/3/reference/compound_stmts.html#generic-classes).
648 # In this case, the code name is set to `<generic parameters of MyClass>`,
649 # and we need to skip this frame as it is irrelevant.
650 frame = cast(types.FrameType, frame.f_back) # guaranteed to not be `None` 1nohipqjkrslm
652 # note, we don't copy frame.f_locals here (or during the last return call), because we don't expect the namespace to be
653 # modified down the line if this becomes a problem, we could implement some sort of frozen mapping structure to enforce this.
654 if force: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
655 return frame.f_locals 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
657 # If either of the following conditions are true, the class is defined at the top module level.
658 # To better understand why we need both of these checks, see
659 # https://github.com/pydantic/pydantic/pull/10113#discussion_r1714981531.
660 if frame.f_back is None or frame.f_code.co_name == '<module>': 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
661 return None 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
663 return frame.f_locals 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
666def _type_convert(arg: Any) -> Any: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
667 """Convert `None` to `NoneType` and strings to `ForwardRef` instances.
669 This is a backport of the private `typing._type_convert` function. When
670 evaluating a type, `ForwardRef._evaluate` ends up being called, and is
671 responsible for making this conversion. However, we still have to apply
672 it for the first argument passed to our type evaluation functions, similarly
673 to the `typing.get_type_hints` function.
674 """
675 if arg is None: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
676 return NoneType 1abzAtunohigFcdBCvwpqjkefDExyrslm
677 if isinstance(arg, str): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
678 # Like `typing.get_type_hints`, assume the arg can be in any context,
679 # hence the proper `is_argument` and `is_class` args:
680 return _make_forward_ref(arg, is_argument=False, is_class=True) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
681 return arg 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
684def get_model_type_hints( 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
685 obj: type[BaseModel],
686 *,
687 ns_resolver: NsResolver | None = None,
688) -> dict[str, tuple[Any, bool]]:
689 """Collect annotations from a Pydantic model class, including those from parent classes.
691 Args:
692 obj: The Pydantic model to inspect.
693 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
695 Returns:
696 A dictionary mapping annotation names to a two-tuple: the first element is the evaluated
697 type or the original annotation if a `NameError` occurred, the second element is a boolean
698 indicating if whether the evaluation succeeded.
699 """
700 hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {} 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
701 ns_resolver = ns_resolver or NsResolver() 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
703 for base in reversed(obj.__mro__): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
704 ann: dict[str, Any] | None = base.__dict__.get('__annotations__') 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
705 if not ann or isinstance(ann, types.GetSetDescriptorType): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
706 continue 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
707 with ns_resolver.push(base): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
708 globalns, localns = ns_resolver.types_namespace 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
709 for name, value in ann.items(): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
710 if name.startswith('_'): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
711 # For private attributes, we only need the annotation to detect the `ClassVar` special form.
712 # For this reason, we still try to evaluate it, but we also catch any possible exception (on
713 # top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free
714 # to use any kind of forward annotation for private fields (e.g. circular imports, new typing
715 # syntax, etc).
716 try: 1abzAtunohigFcdBCvwpqjkefDExyrslm
717 hints[name] = try_eval_type(value, globalns, localns) 1abzAtunohigFcdBCvwpqjkefDExyrslm
718 except Exception: 1abzAtunohigFcdBCvwpqjkefDExyrslm
719 hints[name] = (value, False) 1abzAtunohigFcdBCvwpqjkefDExyrslm
720 else:
721 hints[name] = try_eval_type(value, globalns, localns) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
722 return hints 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
725def get_cls_type_hints( 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
726 obj: type[Any],
727 *,
728 ns_resolver: NsResolver | None = None,
729) -> dict[str, Any]:
730 """Collect annotations from a class, including those from parent classes.
732 Args:
733 obj: The class to inspect.
734 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
735 """
736 hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {} 1abzAtunohigFcdBCvwpqjkefDExyrslm
737 ns_resolver = ns_resolver or NsResolver() 1abzAtunohigFcdBCvwpqjkefDExyrslm
739 for base in reversed(obj.__mro__): 1abzAtunohigFcdBCvwpqjkefDExyrslm
740 ann: dict[str, Any] | None = base.__dict__.get('__annotations__') 1abzAtunohigFcdBCvwpqjkefDExyrslm
741 if not ann or isinstance(ann, types.GetSetDescriptorType): 1abzAtunohigFcdBCvwpqjkefDExyrslm
742 continue 1zAtunohiFBCvwpqjkDExyrslm
743 with ns_resolver.push(base): 1abzAtunohigFcdBCvwpqjkefDExyrslm
744 globalns, localns = ns_resolver.types_namespace 1abzAtunohigFcdBCvwpqjkefDExyrslm
745 for name, value in ann.items(): 1abzAtunohigFcdBCvwpqjkefDExyrslm
746 hints[name] = eval_type(value, globalns, localns) 1abzAtunohigFcdBCvwpqjkefDExyrslm
747 return hints 1abzAtunohigFcdBCvwpqjkefDExyrslm
750def try_eval_type( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
751 value: Any,
752 globalns: GlobalsNamespace | None = None,
753 localns: MappingNamespace | None = None,
754) -> tuple[Any, bool]:
755 """Try evaluating the annotation using the provided namespaces.
757 Args:
758 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
759 of `str`, it will be converted to a `ForwardRef`.
760 localns: The global namespace to use during annotation evaluation.
761 globalns: The local namespace to use during annotation evaluation.
763 Returns:
764 A two-tuple containing the possibly evaluated type and a boolean indicating
765 whether the evaluation succeeded or not.
766 """
767 value = _type_convert(value) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
769 try: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
770 return eval_type_backport(value, globalns, localns), True 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
771 except NameError: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
772 return value, False 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
775def eval_type( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
776 value: Any,
777 globalns: GlobalsNamespace | None = None,
778 localns: MappingNamespace | None = None,
779) -> Any:
780 """Evaluate the annotation using the provided namespaces.
782 Args:
783 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
784 of `str`, it will be converted to a `ForwardRef`.
785 localns: The global namespace to use during annotation evaluation.
786 globalns: The local namespace to use during annotation evaluation.
787 """
788 value = _type_convert(value) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
789 return eval_type_backport(value, globalns, localns) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
792@deprecated( 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
793 '`eval_type_lenient` is deprecated, use `try_eval_type` instead.',
794 category=None,
795)
796def eval_type_lenient( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
797 value: Any,
798 globalns: GlobalsNamespace | None = None,
799 localns: MappingNamespace | None = None,
800) -> Any:
801 ev, _ = try_eval_type(value, globalns, localns)
802 return ev
805def eval_type_backport( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
806 value: Any,
807 globalns: GlobalsNamespace | None = None,
808 localns: MappingNamespace | None = None,
809 type_params: tuple[Any, ...] | None = None,
810) -> Any:
811 """An enhanced version of `typing._eval_type` which will fall back to using the `eval_type_backport`
812 package if it's installed to let older Python versions use newer typing constructs.
814 Specifically, this transforms `X | Y` into `typing.Union[X, Y]` and `list[X]` into `typing.List[X]`
815 (as well as all the types made generic in PEP 585) if the original syntax is not supported in the
816 current Python version.
818 This function will also display a helpful error if the value passed fails to evaluate.
819 """
820 try: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
821 return _eval_type_backport(value, globalns, localns, type_params) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
822 except TypeError as e: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
823 if 'Unable to evaluate type annotation' in str(e): 1abzAtunohigFcdBCvwpqjkefDExyrslm
824 raise 1abgcdef
826 # If it is a `TypeError` and value isn't a `ForwardRef`, it would have failed during annotation definition.
827 # Thus we assert here for type checking purposes:
828 assert isinstance(value, typing.ForwardRef) 1abzAtunohigFcdBCvwpqjkefDExyrslm
830 message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.' 1abzAtunohigFcdBCvwpqjkefDExyrslm
831 if sys.version_info >= (3, 11): 1abzAtunohigFcdBCvwpqjkefDExyrslm
832 e.add_note(message) 1tunohivwpqjkxyrslm
833 raise 1tunohivwpqjkxyrslm
834 else:
835 raise TypeError(message) from e 1abzAgFcdBCefDE
836 except RecursionError as e: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
837 # TODO ideally recursion errors should be checked in `eval_type` above, but `eval_type_backport`
838 # is used directly in some places.
839 message = ( 1abzAtunohicdBCvwpqjkefDExyrslm
840 "If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), "
841 'consider using PEP 695 type aliases instead. For more details, refer to the documentation: '
842 f'https://docs.pydantic.dev/{version_short()}/concepts/types/#named-recursive-types'
843 )
844 if sys.version_info >= (3, 11): 1abzAtunohicdBCvwpqjkefDExyrslm
845 e.add_note(message) 1tunohivwpqjkxyrslm
846 raise 1tunohivwpqjkxyrslm
847 else:
848 raise RecursionError(f'{e.args[0]}\n{message}') 1abzAcdBCefDE
851def _eval_type_backport( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
852 value: Any,
853 globalns: GlobalsNamespace | None = None,
854 localns: MappingNamespace | None = None,
855 type_params: tuple[Any, ...] | None = None,
856) -> Any:
857 try: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
858 return _eval_type(value, globalns, localns, type_params) 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
859 except TypeError as e: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
860 if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)): 1abzAtunohigFcdBCvwpqjkefDExyrslm
861 raise 1abzAtunohigFcdBCvwpqjkefDExyrslm
863 try: 1abgcdef
864 from eval_type_backport import eval_type_backport 1abgcdef
865 except ImportError: 1abgcdef
866 raise TypeError( 1abgcdef
867 f'Unable to evaluate type annotation {value.__forward_arg__!r}. If you are making use '
868 'of the new typing syntax (unions using `|` since Python 3.10 or builtins subscripting '
869 'since Python 3.9), you should either replace the use of new syntax with the existing '
870 '`typing` constructs or install the `eval_type_backport` package.'
871 ) from e
873 return eval_type_backport( 1abgcdef
874 value,
875 globalns,
876 localns, # pyright: ignore[reportArgumentType], waiting on a new `eval_type_backport` release.
877 try_default=False,
878 )
881def _eval_type( 1abzAtunohicdBCvwpqjkMLKHIJGefDExyrslm
882 value: Any,
883 globalns: GlobalsNamespace | None = None,
884 localns: MappingNamespace | None = None,
885 type_params: tuple[Any, ...] | None = None,
886) -> Any:
887 if sys.version_info >= (3, 13): 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
888 return typing._eval_type( # type: ignore 1hijkGlm
889 value, globalns, localns, type_params=type_params
890 )
891 else:
892 return typing._eval_type( # type: ignore 1abzAtunogFcdBCvwpqMLKHIJefDExyrs
893 value, globalns, localns
894 )
897def is_backport_fixable_error(e: TypeError) -> bool: 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
898 msg = str(e) 1abzAtunohigFcdBCvwpqjkefDExyrslm
900 return sys.version_info < (3, 10) and msg.startswith('unsupported operand type(s) for |: ') 1abzAtunohigFcdBCvwpqjkefDExyrslm
903def get_function_type_hints( 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
904 function: Callable[..., Any],
905 *,
906 include_keys: set[str] | None = None,
907 globalns: GlobalsNamespace | None = None,
908 localns: MappingNamespace | None = None,
909) -> dict[str, Any]:
910 """Return type hints for a function.
912 This is similar to the `typing.get_type_hints` function, with a few differences:
913 - Support `functools.partial` by using the underlying `func` attribute.
914 - If `function` happens to be a built-in type (e.g. `int`), assume it doesn't have annotations
915 but specify the `return` key as being the actual type.
916 - Do not wrap type annotation of a parameter with `Optional` if it has a default value of `None`
917 (related bug: https://github.com/python/cpython/issues/90353, only fixed in 3.11+).
918 """
919 try: 1abzAtunohigFcdBCvwpqjkefDExyrslm
920 if isinstance(function, partial): 1abzAtunohigFcdBCvwpqjkefDExyrslm
921 annotations = function.func.__annotations__ 1abzAtunohigFcdBCvwpqjkefDExyrslm
922 else:
923 annotations = function.__annotations__ 1abzAtunohigFcdBCvwpqjkefDExyrslm
924 except AttributeError: 1abzAtunohigFcdBCvwpqjkefDExyrslm
925 type_hints = get_type_hints(function) 1abzAtunohigFcdBCvwpqjkefDExyrslm
926 if isinstance(function, type): 926 ↛ 931line 926 didn't jump to line 931 because the condition on line 926 was always true1abzAtunohigFcdBCvwpqjkefDExyrslm
927 # `type[...]` is a callable, which returns an instance of itself.
928 # At some point, we might even look into the return type of `__new__`
929 # if it returns something else.
930 type_hints.setdefault('return', function) 1abzAtunohigFcdBCvwpqjkefDExyrslm
931 return type_hints 1abzAtunohigFcdBCvwpqjkefDExyrslm
933 if globalns is None: 1abzAtunohigFcdBCvwpqjkefDExyrslm
934 globalns = get_module_ns_of(function) 1abzAtunohigFcdBCvwpqjkefDExyrslm
935 type_params: tuple[Any, ...] | None = None 1abzAtunohigFcdBCvwpqjkefDExyrslm
936 if localns is None: 1abzAtunohigFcdBCvwpqjkefDExyrslm
937 # If localns was specified, it is assumed to already contain type params. This is because
938 # Pydantic has more advanced logic to do so (see `_namespace_utils.ns_for_function`).
939 type_params = getattr(function, '__type_params__', ()) 1abzAtunohigFcdBCvwpqjkefDExyrslm
941 type_hints = {} 1abzAtunohigFcdBCvwpqjkefDExyrslm
942 for name, value in annotations.items(): 1abzAtunohigFcdBCvwpqjkefDExyrslm
943 if include_keys is not None and name not in include_keys: 1abzAtunohigFcdBCvwpqjkefDExyrslm
944 continue 1abzAtunohigFcdBCvwpqjkefDExyrslm
945 if value is None: 1abzAtunohigFcdBCvwpqjkefDExyrslm
946 value = NoneType 1abzAtunohigFcdBCvwpqjkefDExyrslm
947 elif isinstance(value, str): 1abzAtunohigFcdBCvwpqjkefDExyrslm
948 value = _make_forward_ref(value) 1abzAtunohigFcdBCvwpqjkefDExyrslm
950 type_hints[name] = eval_type_backport(value, globalns, localns, type_params) 1abzAtunohigFcdBCvwpqjkefDExyrslm
952 return type_hints 1abzAtunohigFcdBCvwpqjkefDExyrslm
955if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1): 955 ↛ 957line 955 didn't jump to line 957 because the condition on line 955 was never true1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
957 def _make_forward_ref(
958 arg: Any,
959 is_argument: bool = True,
960 *,
961 is_class: bool = False,
962 ) -> typing.ForwardRef:
963 """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions.
964 The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below.
966 See https://github.com/python/cpython/pull/28560 for some background.
967 The backport happened on 3.9.8, see:
968 https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458,
969 and on 3.10.1 for the 3.10 branch, see:
970 https://github.com/pydantic/pydantic/issues/6912
972 Implemented as EAFP with memory.
973 """
974 return typing.ForwardRef(arg, is_argument)
976else:
977 _make_forward_ref = typing.ForwardRef 1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
980if sys.version_info >= (3, 10): 980 ↛ 984line 980 didn't jump to line 984 because the condition on line 980 was always true1abzAtunohigFcdBCvwpqjkMLKHIJGefDExyrslm
981 get_type_hints = typing.get_type_hints 1zAtunohiFBCvwpqjkLKHIJGDExyrslm
983else:
984 """
985 For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to
986 the implementation in CPython 3.10.8.
987 """
989 @typing.no_type_check 1abgcdMef
990 def get_type_hints( # noqa: C901 1abcdMef
991 obj: Any, 1g
992 globalns: dict[str, Any] | None = None, 1g
993 localns: dict[str, Any] | None = None, 1g
994 include_extras: bool = False, 1g
995 ) -> dict[str, Any]: # pragma: no cover 1g
996 """Taken verbatim from python 3.10.8 unchanged, except:
997 * type annotations of the function definition above.
998 * prefixing `typing.` where appropriate
999 * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument.
1001 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
1003 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
1004 ======================================================
1006 Return type hints for an object.
1008 This is often the same as obj.__annotations__, but it handles
1009 forward references encoded as string literals, adds Optional[t] if a
1010 default value equal to None is set and recursively replaces all
1011 'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
1013 The argument may be a module, class, method, or function. The annotations
1014 are returned as a dictionary. For classes, annotations include also
1015 inherited members.
1017 TypeError is raised if the argument is not of a type that can contain
1018 annotations, and an empty dictionary is returned if no annotations are
1019 present.
1021 BEWARE -- the behavior of globalns and localns is counterintuitive
1022 (unless you are familiar with how eval() and exec() work). The
1023 search order is locals first, then globals.
1025 - If no dict arguments are passed, an attempt is made to use the
1026 globals from obj (or the respective module's globals for classes),
1027 and these are also used as the locals. If the object does not appear
1028 to have globals, an empty dictionary is used. For classes, the search
1029 order is globals first then locals.
1031 - If one dict argument is passed, it is used for both globals and
1032 locals.
1034 - If two dict arguments are passed, they specify globals and
1035 locals, respectively.
1036 """
1037 if getattr(obj, '__no_type_check__', None): 1abgcdef
1038 return {}
1039 # Classes require a special treatment.
1040 if isinstance(obj, type): 1abgcdef
1041 hints = {} 1abgcdef
1042 for base in reversed(obj.__mro__): 1abgcdef
1043 if globalns is None: 1abgcdef
1044 base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {}) 1abgcdef
1045 else:
1046 base_globals = globalns
1047 ann = base.__dict__.get('__annotations__', {}) 1abgcdef
1048 if isinstance(ann, types.GetSetDescriptorType): 1abgcdef
1049 ann = {}
1050 base_locals = dict(vars(base)) if localns is None else localns 1abgcdef
1051 if localns is None and globalns is None: 1abgcdef
1052 # This is surprising, but required. Before Python 3.10,
1053 # get_type_hints only evaluated the globalns of
1054 # a class. To maintain backwards compatibility, we reverse
1055 # the globalns and localns order so that eval() looks into
1056 # *base_globals* first rather than *base_locals*.
1057 # This only affects ForwardRefs.
1058 base_globals, base_locals = base_locals, base_globals 1abgcdef
1059 for name, value in ann.items(): 1abgcdef
1060 if value is None: 1abgcdef
1061 value = type(None)
1062 if isinstance(value, str): 1abgcdef
1063 value = _make_forward_ref(value, is_argument=False, is_class=True) 1abgcdef
1065 value = eval_type_backport(value, base_globals, base_locals) 1abgcdef
1066 hints[name] = value 1abgcdef
1067 if not include_extras and hasattr(typing, '_strip_annotations'): 1abgcdef
1068 return { 1abgcdef
1069 k: typing._strip_annotations(t) # type: ignore 1abgcdef
1070 for k, t in hints.items() 1abgcdef
1071 }
1072 else:
1073 return hints
1075 if globalns is None: 1abgcdef
1076 if isinstance(obj, types.ModuleType): 1abgcdef
1077 globalns = obj.__dict__
1078 else:
1079 nsobj = obj 1abgcdef
1080 # Find globalns for the unwrapped object.
1081 while hasattr(nsobj, '__wrapped__'): 1abgcdef
1082 nsobj = nsobj.__wrapped__
1083 globalns = getattr(nsobj, '__globals__', {}) 1abgcdef
1084 if localns is None: 1abgcdef
1085 localns = globalns 1abgcdef
1086 elif localns is None:
1087 localns = globalns
1088 hints = getattr(obj, '__annotations__', None) 1abgcdef
1089 if hints is None: 1abgcdef
1090 # Return empty annotations for something that _could_ have them.
1091 if isinstance(obj, typing._allowed_types): # type: ignore
1092 return {}
1093 else:
1094 raise TypeError(f'{obj!r} is not a module, class, method, or function.')
1095 defaults = typing._get_defaults(obj) # type: ignore 1abgcdef
1096 hints = dict(hints) 1abgcdef
1097 for name, value in hints.items(): 1abgcdef
1098 if value is None: 1abgcdef
1099 value = type(None)
1100 if isinstance(value, str): 1abgcdef
1101 # class-level forward refs were handled above, this must be either
1102 # a module-level annotation or a function argument annotation
1104 value = _make_forward_ref( 1abgcdef
1105 value, 1abgcdef
1106 is_argument=not isinstance(obj, types.ModuleType), 1abgcdef
1107 is_class=False, 1abgcdef
1108 )
1109 value = eval_type_backport(value, globalns, localns) 1abgcdef
1110 if name in defaults and defaults[name] is None: 1abgcdef
1111 value = typing.Optional[value]
1112 hints[name] = value 1abgcdef
1113 return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore 1abgcdef