99import typing
1010import warnings
1111from functools import lru_cache , partial
12- from typing import Any , Callable , Literal , overload
12+ from typing import TYPE_CHECKING , Any , Callable
1313
1414import typing_extensions
1515from typing_extensions import TypeIs , deprecated , get_args , get_origin
2323 from types import EllipsisType as EllipsisType
2424 from types import NoneType as NoneType
2525
26+ if TYPE_CHECKING :
27+ from pydantic import BaseModel
2628
2729# See https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types:
2830
@@ -467,34 +469,57 @@ def _type_convert(arg: Any) -> Any:
467469 return arg
468470
469471
470- @overload
471- def get_cls_type_hints (
472- obj : type [Any ],
473- * ,
474- ns_resolver : NsResolver | None = None ,
475- lenient : Literal [True ],
476- ) -> dict [str , tuple [Any , bool ]]: ...
477- @overload
478- def get_cls_type_hints (
479- obj : type [Any ],
472+ def get_model_type_hints (
473+ obj : type [BaseModel ],
480474 * ,
481475 ns_resolver : NsResolver | None = None ,
482- lenient : Literal [False ] = ...,
483- ) -> dict [str , Any ]: ...
476+ ) -> dict [str , tuple [Any , bool ]]:
477+ """Collect annotations from a Pydantic model class, including those from parent classes.
478+
479+ Args:
480+ obj: The Pydantic model to inspect.
481+ ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
482+
483+ Returns:
484+ A dictionary mapping annotation names to a two-tuple: the first element is the evaluated
485+ type or the original annotation if a `NameError` occurred, the second element is a boolean
486+ indicating if whether the evaluation succeeded.
487+ """
488+ hints : dict [str , Any ] | dict [str , tuple [Any , bool ]] = {}
489+ ns_resolver = ns_resolver or NsResolver ()
490+
491+ for base in reversed (obj .__mro__ ):
492+ ann : dict [str , Any ] | None = base .__dict__ .get ('__annotations__' )
493+ if not ann or isinstance (ann , types .GetSetDescriptorType ):
494+ continue
495+ with ns_resolver .push (base ):
496+ globalns , localns = ns_resolver .types_namespace
497+ for name , value in ann .items ():
498+ if name .startswith ('_' ):
499+ # For private attributes, we only need the annotation to detect the `ClassVar` special form.
500+ # For this reason, we still try to evaluate it, but we also catch any possible exception (on
501+ # top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free
502+ # to use any kind of forward annotation for private fields (e.g. circular imports, new typing
503+ # syntax, etc).
504+ try :
505+ hints [name ] = try_eval_type (value , globalns , localns )
506+ except Exception :
507+ hints [name ] = (value , False )
508+ else :
509+ hints [name ] = try_eval_type (value , globalns , localns )
510+ return hints
511+
512+
484513def get_cls_type_hints (
485514 obj : type [Any ],
486515 * ,
487516 ns_resolver : NsResolver | None = None ,
488- lenient : bool = False ,
489- ) -> dict [str , Any ] | dict [str , tuple [Any , bool ]]:
517+ ) -> dict [str , Any ]:
490518 """Collect annotations from a class, including those from parent classes.
491519
492520 Args:
493521 obj: The class to inspect.
494522 ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
495- lenient: Whether to keep unresolvable annotations as is or re-raise the `NameError` exception.
496- If lenient, an extra boolean flag is set for each annotation value to indicate whether the
497- evaluation succeeded or not. Default: re-raise.
498523 """
499524 hints : dict [str , Any ] | dict [str , tuple [Any , bool ]] = {}
500525 ns_resolver = ns_resolver or NsResolver ()
@@ -506,10 +531,7 @@ def get_cls_type_hints(
506531 with ns_resolver .push (base ):
507532 globalns , localns = ns_resolver .types_namespace
508533 for name , value in ann .items ():
509- if lenient :
510- hints [name ] = try_eval_type (value , globalns , localns )
511- else :
512- hints [name ] = eval_type (value , globalns , localns )
534+ hints [name ] = eval_type (value , globalns , localns )
513535 return hints
514536
515537
0 commit comments