1"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap Python's typing module."""
2
3from __future__ import annotations
4
5import collections.abc
6import re
7import sys
8import types
9import typing
10from functools import partial
11from typing import TYPE_CHECKING, Any, Callable, cast
12
13import typing_extensions
14from typing_extensions import deprecated, get_args, get_origin
15from typing_inspection import typing_objects
16from typing_inspection.introspection import is_union_origin
17
18from pydantic.version import version_short
19
20from ._namespace_utils import GlobalsNamespace, MappingNamespace, NsResolver, get_module_ns_of
21
22if sys.version_info < (3, 10):
23 NoneType = type(None)
24 EllipsisType = type(Ellipsis)
25else:
26 from types import EllipsisType as EllipsisType
27 from types import NoneType as NoneType
28
29if sys.version_info >= (3, 14):
30 import annotationlib
31
32if TYPE_CHECKING:
33 from pydantic import BaseModel
34
35# As per https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types,
36# always check for both `typing` and `typing_extensions` variants of a typing construct.
37# (this is implemented differently than the suggested approach in the `typing_extensions`
38# docs for performance).
39
40
41_t_annotated = typing.Annotated
42_te_annotated = typing_extensions.Annotated
43
44
45def is_annotated(tp: Any, /) -> bool:
46 """Return whether the provided argument is a `Annotated` special form.
47
48 ```python {test="skip" lint="skip"}
49 is_annotated(Annotated[int, ...])
50 #> True
51 ```
52 """
53 origin = get_origin(tp)
54 return origin is _t_annotated or origin is _te_annotated
55
56
57def annotated_type(tp: Any, /) -> Any | None:
58 """Return the type of the `Annotated` special form, or `None`."""
59 return tp.__origin__ if typing_objects.is_annotated(get_origin(tp)) else None
60
61
62def unpack_type(tp: Any, /) -> Any | None:
63 """Return the type wrapped by the `Unpack` special form, or `None`."""
64 return get_args(tp)[0] if typing_objects.is_unpack(get_origin(tp)) else None
65
66
67def is_hashable(tp: Any, /) -> bool:
68 """Return whether the provided argument is the `Hashable` class.
69
70 ```python {test="skip" lint="skip"}
71 is_hashable(Hashable)
72 #> True
73 ```
74 """
75 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
76 # hence the second check:
77 return tp is collections.abc.Hashable or get_origin(tp) is collections.abc.Hashable
78
79
80def is_callable(tp: Any, /) -> bool:
81 """Return whether the provided argument is a `Callable`, parametrized or not.
82
83 ```python {test="skip" lint="skip"}
84 is_callable(Callable[[int], str])
85 #> True
86 is_callable(typing.Callable)
87 #> True
88 is_callable(collections.abc.Callable)
89 #> True
90 ```
91 """
92 # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
93 # hence the second check:
94 return tp is collections.abc.Callable or get_origin(tp) is collections.abc.Callable
95
96
97_classvar_re = re.compile(r'((\w+\.)?Annotated\[)?(\w+\.)?ClassVar\[')
98
99
100def is_classvar_annotation(tp: Any, /) -> bool:
101 """Return whether the provided argument represents a class variable annotation.
102
103 Although not explicitly stated by the typing specification, `ClassVar` can be used
104 inside `Annotated` and as such, this function checks for this specific scenario.
105
106 Because this function is used to detect class variables before evaluating forward references
107 (or because evaluation failed), we also implement a naive regex match implementation. This is
108 required because class variables are inspected before fields are collected, so we try to be
109 as accurate as possible.
110 """
111 if typing_objects.is_classvar(tp):
112 return True
113
114 origin = get_origin(tp)
115
116 if typing_objects.is_classvar(origin):
117 return True
118
119 if typing_objects.is_annotated(origin):
120 annotated_type = tp.__origin__
121 if typing_objects.is_classvar(annotated_type) or typing_objects.is_classvar(get_origin(annotated_type)):
122 return True
123
124 str_ann: str | None = None
125 if isinstance(tp, typing.ForwardRef):
126 str_ann = tp.__forward_arg__
127 if isinstance(tp, str):
128 str_ann = tp
129
130 if str_ann is not None and _classvar_re.match(str_ann):
131 # stdlib dataclasses do something similar, although a bit more advanced
132 # (see `dataclass._is_type`).
133 return True
134
135 return False
136
137
138_t_final = typing.Final
139_te_final = typing_extensions.Final
140
141
142# TODO implement `is_finalvar_annotation` as Final can be wrapped with other special forms:
143def is_finalvar(tp: Any, /) -> bool:
144 """Return whether the provided argument is a `Final` special form, parametrized or not.
145
146 ```python {test="skip" lint="skip"}
147 is_finalvar(Final[int])
148 #> True
149 is_finalvar(Final)
150 #> True
151 """
152 # Final is not necessarily parametrized:
153 if tp is _t_final or tp is _te_final:
154 return True
155 origin = get_origin(tp)
156 return origin is _t_final or origin is _te_final
157
158
159_NONE_TYPES: tuple[Any, ...] = (None, NoneType, typing.Literal[None], typing_extensions.Literal[None])
160
161
162def is_none_type(tp: Any, /) -> bool:
163 """Return whether the argument represents the `None` type as part of an annotation.
164
165 ```python {test="skip" lint="skip"}
166 is_none_type(None)
167 #> True
168 is_none_type(NoneType)
169 #> True
170 is_none_type(Literal[None])
171 #> True
172 is_none_type(type[None])
173 #> False
174 """
175 return tp in _NONE_TYPES
176
177
178def is_namedtuple(tp: Any, /) -> bool:
179 """Return whether the provided argument is a named tuple class.
180
181 The class can be created using `typing.NamedTuple` or `collections.namedtuple`.
182 Parametrized generic classes are *not* assumed to be named tuples.
183 """
184 from ._utils import lenient_issubclass # circ. import
185
186 return lenient_issubclass(tp, tuple) and hasattr(tp, '_fields')
187
188
189# TODO In 2.12, delete this export. It is currently defined only to not break
190# pydantic-settings which relies on it:
191origin_is_union = is_union_origin
192
193
194def is_generic_alias(tp: Any, /) -> bool:
195 return isinstance(tp, (types.GenericAlias, typing._GenericAlias)) # pyright: ignore[reportAttributeAccessIssue]
196
197
198# TODO: Ideally, we should avoid relying on the private `typing` constructs:
199
200if sys.version_info < (3, 10):
201 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias) # pyright: ignore[reportAttributeAccessIssue]
202else:
203 WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias, types.UnionType) # pyright: ignore[reportAttributeAccessIssue]
204
205
206# Similarly, we shouldn't rely on this `_Final` class, which is even more private than `_GenericAlias`:
207typing_base: Any = typing._Final # pyright: ignore[reportAttributeAccessIssue]
208
209
210### Annotation evaluations functions:
211
212
213def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None:
214 """Fetch the local namespace of the parent frame where this function is called.
215
216 Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace,
217 such as assignments inside a function. Using the standard library tools, it is currently not possible to resolve
218 such annotations:
219
220 ```python {lint="skip" test="skip"}
221 from typing import get_type_hints
222
223 def func() -> None:
224 Alias = int
225
226 class C:
227 a: 'Alias'
228
229 # Raises a `NameError: 'Alias' is not defined`
230 get_type_hints(C)
231 ```
232
233 Pydantic uses this function when a Pydantic model is being defined to fetch the parent frame locals. However,
234 this only allows us to fetch the parent frame namespace and not other parents (e.g. a model defined in a function,
235 itself defined in another function). Inspecting the next outer frames (using `f_back`) is not reliable enough
236 (see https://discuss.python.org/t/20659).
237
238 Because this function is mostly used to better resolve forward annotations, nothing is returned if the parent frame's
239 code object is defined at the module level. In this case, the locals of the frame will be the same as the module
240 globals where the class is defined (see `_namespace_utils.get_module_ns_of`). However, if you still want to fetch
241 the module globals (e.g. when rebuilding a model, where the frame where the rebuild call is performed might contain
242 members that you want to use for forward annotations evaluation), you can use the `force` parameter.
243
244 Args:
245 parent_depth: The depth at which to get the frame. Defaults to 2, meaning the parent frame where this function
246 is called will be used.
247 force: Whether to always return the frame locals, even if the frame's code object is defined at the module level.
248
249 Returns:
250 The locals of the namespace, or `None` if it was skipped as per the described logic.
251 """
252 frame = sys._getframe(parent_depth)
253
254 if frame.f_code.co_name.startswith('<generic parameters of'):
255 # As `parent_frame_namespace` is mostly called in `ModelMetaclass.__new__`,
256 # the parent frame can be the annotation scope if the PEP 695 generic syntax is used.
257 # (see https://docs.python.org/3/reference/executionmodel.html#annotation-scopes,
258 # https://docs.python.org/3/reference/compound_stmts.html#generic-classes).
259 # In this case, the code name is set to `<generic parameters of MyClass>`,
260 # and we need to skip this frame as it is irrelevant.
261 frame = cast(types.FrameType, frame.f_back) # guaranteed to not be `None`
262
263 # note, we don't copy frame.f_locals here (or during the last return call), because we don't expect the namespace to be
264 # modified down the line if this becomes a problem, we could implement some sort of frozen mapping structure to enforce this.
265 if force:
266 return frame.f_locals
267
268 # If either of the following conditions are true, the class is defined at the top module level.
269 # To better understand why we need both of these checks, see
270 # https://github.com/pydantic/pydantic/pull/10113#discussion_r1714981531.
271 if frame.f_back is None or frame.f_code.co_name == '<module>':
272 return None
273
274 return frame.f_locals
275
276
277def _type_convert(arg: Any) -> Any:
278 """Convert `None` to `NoneType` and strings to `ForwardRef` instances.
279
280 This is a backport of the private `typing._type_convert` function. When
281 evaluating a type, `ForwardRef._evaluate` ends up being called, and is
282 responsible for making this conversion. However, we still have to apply
283 it for the first argument passed to our type evaluation functions, similarly
284 to the `typing.get_type_hints` function.
285 """
286 if arg is None:
287 return NoneType
288 if isinstance(arg, str):
289 # Like `typing.get_type_hints`, assume the arg can be in any context,
290 # hence the proper `is_argument` and `is_class` args:
291 return _make_forward_ref(arg, is_argument=False, is_class=True)
292 return arg
293
294
295def safe_get_annotations(cls: type[Any]) -> dict[str, Any]:
296 """Get the annotations for the provided class, accounting for potential deferred forward references.
297
298 Starting with Python 3.14, accessing the `__annotations__` attribute might raise a `NameError` if
299 a referenced symbol isn't defined yet. In this case, we return the annotation in the *forward ref*
300 format.
301 """
302 if sys.version_info >= (3, 14):
303 return annotationlib.get_annotations(cls, format=annotationlib.Format.FORWARDREF)
304 else:
305 return cls.__dict__.get('__annotations__', {})
306
307
308def get_model_type_hints(
309 obj: type[BaseModel],
310 *,
311 ns_resolver: NsResolver | None = None,
312) -> dict[str, tuple[Any, bool]]:
313 """Collect annotations from a Pydantic model class, including those from parent classes.
314
315 Args:
316 obj: The Pydantic model to inspect.
317 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
318
319 Returns:
320 A dictionary mapping annotation names to a two-tuple: the first element is the evaluated
321 type or the original annotation if a `NameError` occurred, the second element is a boolean
322 indicating if whether the evaluation succeeded.
323 """
324 hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {}
325 ns_resolver = ns_resolver or NsResolver()
326
327 for base in reversed(obj.__mro__):
328 # For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
329 # from the ns_resolver, but we want to be able to know which specific field failed
330 # to evaluate:
331 ann = safe_get_annotations(base)
332
333 if not ann:
334 continue
335
336 with ns_resolver.push(base):
337 globalns, localns = ns_resolver.types_namespace
338 for name, value in ann.items():
339 if name.startswith('_'):
340 # For private attributes, we only need the annotation to detect the `ClassVar` special form.
341 # For this reason, we still try to evaluate it, but we also catch any possible exception (on
342 # top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free
343 # to use any kind of forward annotation for private fields (e.g. circular imports, new typing
344 # syntax, etc).
345 try:
346 hints[name] = try_eval_type(value, globalns, localns)
347 except Exception:
348 hints[name] = (value, False)
349 else:
350 hints[name] = try_eval_type(value, globalns, localns)
351 return hints
352
353
354def get_cls_type_hints(
355 obj: type[Any],
356 *,
357 ns_resolver: NsResolver | None = None,
358) -> dict[str, Any]:
359 """Collect annotations from a class, including those from parent classes.
360
361 Args:
362 obj: The class to inspect.
363 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
364 """
365 hints: dict[str, Any] = {}
366 ns_resolver = ns_resolver or NsResolver()
367
368 for base in reversed(obj.__mro__):
369 # For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
370 # from the ns_resolver, but we want to be able to know which specific field failed
371 # to evaluate:
372 ann = safe_get_annotations(base)
373
374 if not ann:
375 continue
376
377 with ns_resolver.push(base):
378 globalns, localns = ns_resolver.types_namespace
379 for name, value in ann.items():
380 hints[name] = eval_type(value, globalns, localns)
381 return hints
382
383
384def try_eval_type(
385 value: Any,
386 globalns: GlobalsNamespace | None = None,
387 localns: MappingNamespace | None = None,
388) -> tuple[Any, bool]:
389 """Try evaluating the annotation using the provided namespaces.
390
391 Args:
392 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
393 of `str`, it will be converted to a `ForwardRef`.
394 localns: The global namespace to use during annotation evaluation.
395 globalns: The local namespace to use during annotation evaluation.
396
397 Returns:
398 A two-tuple containing the possibly evaluated type and a boolean indicating
399 whether the evaluation succeeded or not.
400 """
401 value = _type_convert(value)
402
403 try:
404 return eval_type_backport(value, globalns, localns), True
405 except NameError:
406 return value, False
407
408
409def eval_type(
410 value: Any,
411 globalns: GlobalsNamespace | None = None,
412 localns: MappingNamespace | None = None,
413) -> Any:
414 """Evaluate the annotation using the provided namespaces.
415
416 Args:
417 value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
418 of `str`, it will be converted to a `ForwardRef`.
419 localns: The global namespace to use during annotation evaluation.
420 globalns: The local namespace to use during annotation evaluation.
421 """
422 value = _type_convert(value)
423 return eval_type_backport(value, globalns, localns)
424
425
426@deprecated(
427 '`eval_type_lenient` is deprecated, use `try_eval_type` instead.',
428 category=None,
429)
430def eval_type_lenient(
431 value: Any,
432 globalns: GlobalsNamespace | None = None,
433 localns: MappingNamespace | None = None,
434) -> Any:
435 ev, _ = try_eval_type(value, globalns, localns)
436 return ev
437
438
439def eval_type_backport(
440 value: Any,
441 globalns: GlobalsNamespace | None = None,
442 localns: MappingNamespace | None = None,
443 type_params: tuple[Any, ...] | None = None,
444) -> Any:
445 """An enhanced version of `typing._eval_type` which will fall back to using the `eval_type_backport`
446 package if it's installed to let older Python versions use newer typing constructs.
447
448 Specifically, this transforms `X | Y` into `typing.Union[X, Y]` and `list[X]` into `typing.List[X]`
449 (as well as all the types made generic in PEP 585) if the original syntax is not supported in the
450 current Python version.
451
452 This function will also display a helpful error if the value passed fails to evaluate.
453 """
454 try:
455 return _eval_type_backport(value, globalns, localns, type_params)
456 except TypeError as e:
457 if 'Unable to evaluate type annotation' in str(e):
458 raise
459
460 # If it is a `TypeError` and value isn't a `ForwardRef`, it would have failed during annotation definition.
461 # Thus we assert here for type checking purposes:
462 assert isinstance(value, typing.ForwardRef)
463
464 message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.'
465 if sys.version_info >= (3, 11):
466 e.add_note(message)
467 raise
468 else:
469 raise TypeError(message) from e
470 except RecursionError as e:
471 # TODO ideally recursion errors should be checked in `eval_type` above, but `eval_type_backport`
472 # is used directly in some places.
473 message = (
474 "If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), "
475 'consider using PEP 695 type aliases instead. For more details, refer to the documentation: '
476 f'https://docs.pydantic.dev/{version_short()}/concepts/types/#named-recursive-types'
477 )
478 if sys.version_info >= (3, 11):
479 e.add_note(message)
480 raise
481 else:
482 raise RecursionError(f'{e.args[0]}\n{message}')
483
484
485def _eval_type_backport(
486 value: Any,
487 globalns: GlobalsNamespace | None = None,
488 localns: MappingNamespace | None = None,
489 type_params: tuple[Any, ...] | None = None,
490) -> Any:
491 try:
492 return _eval_type(value, globalns, localns, type_params)
493 except TypeError as e:
494 if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)):
495 raise
496
497 try:
498 from eval_type_backport import eval_type_backport
499 except ImportError:
500 raise TypeError(
501 f'Unable to evaluate type annotation {value.__forward_arg__!r}. If you are making use '
502 'of the new typing syntax (unions using `|` since Python 3.10 or builtins subscripting '
503 'since Python 3.9), you should either replace the use of new syntax with the existing '
504 '`typing` constructs or install the `eval_type_backport` package.'
505 ) from e
506
507 return eval_type_backport(
508 value,
509 globalns,
510 localns, # pyright: ignore[reportArgumentType], waiting on a new `eval_type_backport` release.
511 try_default=False,
512 )
513
514
515def _eval_type(
516 value: Any,
517 globalns: GlobalsNamespace | None = None,
518 localns: MappingNamespace | None = None,
519 type_params: tuple[Any, ...] | None = None,
520) -> Any:
521 if sys.version_info >= (3, 14):
522 # Starting in 3.14, `_eval_type()` does *not* apply `_type_convert()`
523 # anymore. This means the `None` -> `type(None)` conversion does not apply:
524 evaluated = typing._eval_type( # type: ignore
525 value,
526 globalns,
527 localns,
528 type_params=type_params,
529 )
530 if evaluated is None:
531 evaluated = type(None)
532 return evaluated
533 elif sys.version_info >= (3, 13):
534 return typing._eval_type( # type: ignore
535 value, globalns, localns, type_params=type_params
536 )
537 else:
538 return typing._eval_type( # type: ignore
539 value, globalns, localns
540 )
541
542
543def is_backport_fixable_error(e: TypeError) -> bool:
544 msg = str(e)
545
546 return sys.version_info < (3, 10) and msg.startswith('unsupported operand type(s) for |: ')
547
548
549def get_function_type_hints(
550 function: Callable[..., Any],
551 *,
552 include_keys: set[str] | None = None,
553 globalns: GlobalsNamespace | None = None,
554 localns: MappingNamespace | None = None,
555) -> dict[str, Any]:
556 """Return type hints for a function.
557
558 This is similar to the `typing.get_type_hints` function, with a few differences:
559 - Support `functools.partial` by using the underlying `func` attribute.
560 - Do not wrap type annotation of a parameter with `Optional` if it has a default value of `None`
561 (related bug: https://github.com/python/cpython/issues/90353, only fixed in 3.11+).
562 """
563 try:
564 if isinstance(function, partial):
565 annotations = function.func.__annotations__
566 else:
567 annotations = function.__annotations__
568 except AttributeError:
569 # Some functions (e.g. builtins) don't have annotations:
570 return {}
571
572 if globalns is None:
573 globalns = get_module_ns_of(function)
574 type_params: tuple[Any, ...] | None = None
575 if localns is None:
576 # If localns was specified, it is assumed to already contain type params. This is because
577 # Pydantic has more advanced logic to do so (see `_namespace_utils.ns_for_function`).
578 type_params = getattr(function, '__type_params__', ())
579
580 type_hints = {}
581 for name, value in annotations.items():
582 if include_keys is not None and name not in include_keys:
583 continue
584 if value is None:
585 value = NoneType
586 elif isinstance(value, str):
587 value = _make_forward_ref(value)
588
589 type_hints[name] = eval_type_backport(value, globalns, localns, type_params)
590
591 return type_hints
592
593
594# TODO use typing.ForwardRef directly when we stop supporting 3.9:
595if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1):
596
597 def _make_forward_ref(
598 arg: Any,
599 is_argument: bool = True,
600 *,
601 is_class: bool = False,
602 ) -> typing.ForwardRef:
603 """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions.
604 The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below.
605
606 See https://github.com/python/cpython/pull/28560 for some background.
607 The backport happened on 3.9.8, see:
608 https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458,
609 and on 3.10.1 for the 3.10 branch, see:
610 https://github.com/pydantic/pydantic/issues/6912
611
612 Implemented as EAFP with memory.
613 """
614 return typing.ForwardRef(arg, is_argument) # pyright: ignore[reportCallIssue]
615
616else:
617 _make_forward_ref = typing.ForwardRef # pyright: ignore[reportAssignmentType]
618
619
620if sys.version_info >= (3, 10):
621 get_type_hints = typing.get_type_hints
622
623else:
624 """
625 For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to
626 the implementation in CPython 3.10.8.
627 """
628
629 @typing.no_type_check
630 def get_type_hints( # noqa: C901
631 obj: Any,
632 globalns: dict[str, Any] | None = None,
633 localns: dict[str, Any] | None = None,
634 include_extras: bool = False,
635 ) -> dict[str, Any]: # pragma: no cover
636 """Taken verbatim from python 3.10.8 unchanged, except:
637 * type annotations of the function definition above.
638 * prefixing `typing.` where appropriate
639 * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument.
640
641 https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
642
643 DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
644 ======================================================
645
646 Return type hints for an object.
647
648 This is often the same as obj.__annotations__, but it handles
649 forward references encoded as string literals, adds Optional[t] if a
650 default value equal to None is set and recursively replaces all
651 'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
652
653 The argument may be a module, class, method, or function. The annotations
654 are returned as a dictionary. For classes, annotations include also
655 inherited members.
656
657 TypeError is raised if the argument is not of a type that can contain
658 annotations, and an empty dictionary is returned if no annotations are
659 present.
660
661 BEWARE -- the behavior of globalns and localns is counterintuitive
662 (unless you are familiar with how eval() and exec() work). The
663 search order is locals first, then globals.
664
665 - If no dict arguments are passed, an attempt is made to use the
666 globals from obj (or the respective module's globals for classes),
667 and these are also used as the locals. If the object does not appear
668 to have globals, an empty dictionary is used. For classes, the search
669 order is globals first then locals.
670
671 - If one dict argument is passed, it is used for both globals and
672 locals.
673
674 - If two dict arguments are passed, they specify globals and
675 locals, respectively.
676 """
677 if getattr(obj, '__no_type_check__', None):
678 return {}
679 # Classes require a special treatment.
680 if isinstance(obj, type):
681 hints = {}
682 for base in reversed(obj.__mro__):
683 if globalns is None:
684 base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
685 else:
686 base_globals = globalns
687 ann = base.__dict__.get('__annotations__', {})
688 if isinstance(ann, types.GetSetDescriptorType):
689 ann = {}
690 base_locals = dict(vars(base)) if localns is None else localns
691 if localns is None and globalns is None:
692 # This is surprising, but required. Before Python 3.10,
693 # get_type_hints only evaluated the globalns of
694 # a class. To maintain backwards compatibility, we reverse
695 # the globalns and localns order so that eval() looks into
696 # *base_globals* first rather than *base_locals*.
697 # This only affects ForwardRefs.
698 base_globals, base_locals = base_locals, base_globals
699 for name, value in ann.items():
700 if value is None:
701 value = type(None)
702 if isinstance(value, str):
703 value = _make_forward_ref(value, is_argument=False, is_class=True)
704
705 value = eval_type_backport(value, base_globals, base_locals)
706 hints[name] = value
707 if not include_extras and hasattr(typing, '_strip_annotations'):
708 return {
709 k: typing._strip_annotations(t) # type: ignore
710 for k, t in hints.items()
711 }
712 else:
713 return hints
714
715 if globalns is None:
716 if isinstance(obj, types.ModuleType):
717 globalns = obj.__dict__
718 else:
719 nsobj = obj
720 # Find globalns for the unwrapped object.
721 while hasattr(nsobj, '__wrapped__'):
722 nsobj = nsobj.__wrapped__
723 globalns = getattr(nsobj, '__globals__', {})
724 if localns is None:
725 localns = globalns
726 elif localns is None:
727 localns = globalns
728 hints = getattr(obj, '__annotations__', None)
729 if hints is None:
730 # Return empty annotations for something that _could_ have them.
731 if isinstance(obj, typing._allowed_types): # type: ignore
732 return {}
733 else:
734 raise TypeError(f'{obj!r} is not a module, class, method, or function.')
735 defaults = typing._get_defaults(obj) # type: ignore
736 hints = dict(hints)
737 for name, value in hints.items():
738 if value is None:
739 value = type(None)
740 if isinstance(value, str):
741 # class-level forward refs were handled above, this must be either
742 # a module-level annotation or a function argument annotation
743
744 value = _make_forward_ref(
745 value,
746 is_argument=not isinstance(obj, types.ModuleType),
747 is_class=False,
748 )
749 value = eval_type_backport(value, globalns, localns)
750 if name in defaults and defaults[name] is None:
751 value = typing.Optional[value]
752 hints[name] = value
753 return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()} # type: ignore