_model_construction.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. """Private logic for creating models."""
  2. from __future__ import annotations as _annotations
  3. import builtins
  4. import operator
  5. import sys
  6. import typing
  7. import warnings
  8. import weakref
  9. from abc import ABCMeta
  10. from functools import lru_cache, partial
  11. from types import FunctionType
  12. from typing import Any, Callable, Generic, Literal, NoReturn, cast
  13. from pydantic_core import PydanticUndefined, SchemaSerializer
  14. from typing_extensions import TypeAliasType, dataclass_transform, deprecated, get_args
  15. from ..errors import PydanticUndefinedAnnotation, PydanticUserError
  16. from ..plugin._schema_validator import create_schema_validator
  17. from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20
  18. from ._config import ConfigWrapper
  19. from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function
  20. from ._fields import collect_model_fields, is_valid_field_name, is_valid_privateattr_name
  21. from ._generate_schema import GenerateSchema
  22. from ._generics import PydanticGenericMetadata, get_model_typevars_map
  23. from ._import_utils import import_cached_base_model, import_cached_field_info
  24. from ._mock_val_ser import set_model_mocks
  25. from ._namespace_utils import NsResolver
  26. from ._schema_generation_shared import CallbackGetCoreSchemaHandler
  27. from ._signature import generate_pydantic_signature
  28. from ._typing_extra import (
  29. _make_forward_ref,
  30. eval_type_backport,
  31. is_annotated,
  32. is_classvar_annotation,
  33. parent_frame_namespace,
  34. )
  35. from ._utils import LazyClassAttribute, SafeGetItemProxy
  36. if typing.TYPE_CHECKING:
  37. from ..fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr
  38. from ..fields import Field as PydanticModelField
  39. from ..fields import PrivateAttr as PydanticModelPrivateAttr
  40. from ..main import BaseModel
  41. else:
  42. # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
  43. # and https://youtrack.jetbrains.com/issue/PY-51428
  44. DeprecationWarning = PydanticDeprecatedSince20
  45. PydanticModelField = object()
  46. PydanticModelPrivateAttr = object()
  47. object_setattr = object.__setattr__
  48. class _ModelNamespaceDict(dict):
  49. """A dictionary subclass that intercepts attribute setting on model classes and
  50. warns about overriding of decorators.
  51. """
  52. def __setitem__(self, k: str, v: object) -> None:
  53. existing: Any = self.get(k, None)
  54. if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy):
  55. warnings.warn(f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator')
  56. return super().__setitem__(k, v)
  57. def NoInitField(
  58. *,
  59. init: Literal[False] = False,
  60. ) -> Any:
  61. """Only for typing purposes. Used as default value of `__pydantic_fields_set__`,
  62. `__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when
  63. synthesizing the `__init__` signature.
  64. """
  65. @dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField))
  66. class ModelMetaclass(ABCMeta):
  67. def __new__(
  68. mcs,
  69. cls_name: str,
  70. bases: tuple[type[Any], ...],
  71. namespace: dict[str, Any],
  72. __pydantic_generic_metadata__: PydanticGenericMetadata | None = None,
  73. __pydantic_reset_parent_namespace__: bool = True,
  74. _create_model_module: str | None = None,
  75. **kwargs: Any,
  76. ) -> type:
  77. """Metaclass for creating Pydantic models.
  78. Args:
  79. cls_name: The name of the class to be created.
  80. bases: The base classes of the class to be created.
  81. namespace: The attribute dictionary of the class to be created.
  82. __pydantic_generic_metadata__: Metadata for generic models.
  83. __pydantic_reset_parent_namespace__: Reset parent namespace.
  84. _create_model_module: The module of the class to be created, if created by `create_model`.
  85. **kwargs: Catch-all for any other keyword arguments.
  86. Returns:
  87. The new class created by the metaclass.
  88. """
  89. # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact
  90. # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__`
  91. # call we're in the middle of is for the `BaseModel` class.
  92. if bases:
  93. base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases)
  94. config_wrapper = ConfigWrapper.for_model(bases, namespace, kwargs)
  95. namespace['model_config'] = config_wrapper.config_dict
  96. private_attributes = inspect_namespace(
  97. namespace, config_wrapper.ignored_types, class_vars, base_field_names
  98. )
  99. if private_attributes or base_private_attributes:
  100. original_model_post_init = get_model_post_init(namespace, bases)
  101. if original_model_post_init is not None:
  102. # if there are private_attributes and a model_post_init function, we handle both
  103. def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None:
  104. """We need to both initialize private attributes and call the user-defined model_post_init
  105. method.
  106. """
  107. init_private_attributes(self, context)
  108. original_model_post_init(self, context)
  109. namespace['model_post_init'] = wrapped_model_post_init
  110. else:
  111. namespace['model_post_init'] = init_private_attributes
  112. namespace['__class_vars__'] = class_vars
  113. namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes}
  114. cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs))
  115. BaseModel_ = import_cached_base_model()
  116. mro = cls.__mro__
  117. if Generic in mro and mro.index(Generic) < mro.index(BaseModel_):
  118. warnings.warn(
  119. GenericBeforeBaseModelWarning(
  120. 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) '
  121. 'for pydantic generics to work properly.'
  122. ),
  123. stacklevel=2,
  124. )
  125. cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False)
  126. cls.__pydantic_post_init__ = (
  127. None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init'
  128. )
  129. cls.__pydantic_decorators__ = DecoratorInfos.build(cls)
  130. # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class
  131. if __pydantic_generic_metadata__:
  132. cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__
  133. else:
  134. parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ())
  135. parameters = getattr(cls, '__parameters__', None) or parent_parameters
  136. if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
  137. from ..root_model import RootModelRootType
  138. missing_parameters = tuple(x for x in parameters if x not in parent_parameters)
  139. if RootModelRootType in parent_parameters and RootModelRootType not in parameters:
  140. # This is a special case where the user has subclassed `RootModel`, but has not parametrized
  141. # RootModel with the generic type identifiers being used. Ex:
  142. # class MyModel(RootModel, Generic[T]):
  143. # root: T
  144. # Should instead just be:
  145. # class MyModel(RootModel[T]):
  146. # root: T
  147. parameters_str = ', '.join([x.__name__ for x in missing_parameters])
  148. error_message = (
  149. f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) '
  150. f'{parameters_str} in its parameters. '
  151. f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.'
  152. )
  153. else:
  154. combined_parameters = parent_parameters + missing_parameters
  155. parameters_str = ', '.join([str(x) for x in combined_parameters])
  156. generic_type_label = f'typing.Generic[{parameters_str}]'
  157. error_message = (
  158. f'All parameters must be present on typing.Generic;'
  159. f' you should inherit from {generic_type_label}.'
  160. )
  161. if Generic not in bases: # pragma: no cover
  162. # We raise an error here not because it is desirable, but because some cases are mishandled.
  163. # It would be nice to remove this error and still have things behave as expected, it's just
  164. # challenging because we are using a custom `__class_getitem__` to parametrize generic models,
  165. # and not returning a typing._GenericAlias from it.
  166. bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
  167. error_message += (
  168. f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
  169. )
  170. raise TypeError(error_message)
  171. cls.__pydantic_generic_metadata__ = {
  172. 'origin': None,
  173. 'args': (),
  174. 'parameters': parameters,
  175. }
  176. cls.__pydantic_complete__ = False # Ensure this specific class gets completed
  177. # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
  178. # for attributes not in `new_namespace` (e.g. private attributes)
  179. for name, obj in private_attributes.items():
  180. obj.__set_name__(cls, name)
  181. if __pydantic_reset_parent_namespace__:
  182. cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace())
  183. parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None)
  184. if isinstance(parent_namespace, dict):
  185. parent_namespace = unpack_lenient_weakvaluedict(parent_namespace)
  186. ns_resolver = NsResolver(parent_namespace=parent_namespace)
  187. set_model_fields(cls, bases, config_wrapper, ns_resolver)
  188. if config_wrapper.frozen and '__hash__' not in namespace:
  189. set_default_hash_func(cls, bases)
  190. complete_model_class(
  191. cls,
  192. cls_name,
  193. config_wrapper,
  194. raise_errors=False,
  195. ns_resolver=ns_resolver,
  196. create_model_module=_create_model_module,
  197. )
  198. # If this is placed before the complete_model_class call above,
  199. # the generic computed fields return type is set to PydanticUndefined
  200. cls.__pydantic_computed_fields__ = {
  201. k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()
  202. }
  203. set_deprecated_descriptors(cls)
  204. # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__
  205. # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is
  206. # only hit for _proper_ subclasses of BaseModel
  207. super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc]
  208. return cls
  209. else:
  210. # These are instance variables, but have been assigned to `NoInitField` to trick the type checker.
  211. for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__':
  212. namespace.pop(
  213. instance_slot,
  214. None, # In case the metaclass is used with a class other than `BaseModel`.
  215. )
  216. namespace.get('__annotations__', {}).clear()
  217. return super().__new__(mcs, cls_name, bases, namespace, **kwargs)
  218. if not typing.TYPE_CHECKING: # pragma: no branch
  219. # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
  220. def __getattr__(self, item: str) -> Any:
  221. """This is necessary to keep attribute access working for class attribute access."""
  222. private_attributes = self.__dict__.get('__private_attributes__')
  223. if private_attributes and item in private_attributes:
  224. return private_attributes[item]
  225. raise AttributeError(item)
  226. @classmethod
  227. def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]:
  228. return _ModelNamespaceDict()
  229. def __instancecheck__(self, instance: Any) -> bool:
  230. """Avoid calling ABC _abc_subclasscheck unless we're pretty sure.
  231. See #3829 and python/cpython#92810
  232. """
  233. return hasattr(instance, '__pydantic_validator__') and super().__instancecheck__(instance)
  234. @staticmethod
  235. def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]:
  236. BaseModel = import_cached_base_model()
  237. field_names: set[str] = set()
  238. class_vars: set[str] = set()
  239. private_attributes: dict[str, ModelPrivateAttr] = {}
  240. for base in bases:
  241. if issubclass(base, BaseModel) and base is not BaseModel:
  242. # model_fields might not be defined yet in the case of generics, so we use getattr here:
  243. field_names.update(getattr(base, '__pydantic_fields__', {}).keys())
  244. class_vars.update(base.__class_vars__)
  245. private_attributes.update(base.__private_attributes__)
  246. return field_names, class_vars, private_attributes
  247. @property
  248. @deprecated('The `__fields__` attribute is deprecated, use `model_fields` instead.', category=None)
  249. def __fields__(self) -> dict[str, FieldInfo]:
  250. warnings.warn(
  251. 'The `__fields__` attribute is deprecated, use `model_fields` instead.',
  252. PydanticDeprecatedSince20,
  253. stacklevel=2,
  254. )
  255. return self.model_fields
  256. @property
  257. def model_fields(self) -> dict[str, FieldInfo]:
  258. """Get metadata about the fields defined on the model.
  259. Returns:
  260. A mapping of field names to [`FieldInfo`][pydantic.fields.FieldInfo] objects.
  261. """
  262. return getattr(self, '__pydantic_fields__', {})
  263. @property
  264. def model_computed_fields(self) -> dict[str, ComputedFieldInfo]:
  265. """Get metadata about the computed fields defined on the model.
  266. Returns:
  267. A mapping of computed field names to [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.
  268. """
  269. return getattr(self, '__pydantic_computed_fields__', {})
  270. def __dir__(self) -> list[str]:
  271. attributes = list(super().__dir__())
  272. if '__fields__' in attributes:
  273. attributes.remove('__fields__')
  274. return attributes
  275. def init_private_attributes(self: BaseModel, context: Any, /) -> None:
  276. """This function is meant to behave like a BaseModel method to initialise private attributes.
  277. It takes context as an argument since that's what pydantic-core passes when calling it.
  278. Args:
  279. self: The BaseModel instance.
  280. context: The context.
  281. """
  282. if getattr(self, '__pydantic_private__', None) is None:
  283. pydantic_private = {}
  284. for name, private_attr in self.__private_attributes__.items():
  285. default = private_attr.get_default()
  286. if default is not PydanticUndefined:
  287. pydantic_private[name] = default
  288. object_setattr(self, '__pydantic_private__', pydantic_private)
  289. def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None:
  290. """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined."""
  291. if 'model_post_init' in namespace:
  292. return namespace['model_post_init']
  293. BaseModel = import_cached_base_model()
  294. model_post_init = get_attribute_from_bases(bases, 'model_post_init')
  295. if model_post_init is not BaseModel.model_post_init:
  296. return model_post_init
  297. def inspect_namespace( # noqa C901
  298. namespace: dict[str, Any],
  299. ignored_types: tuple[type[Any], ...],
  300. base_class_vars: set[str],
  301. base_class_fields: set[str],
  302. ) -> dict[str, ModelPrivateAttr]:
  303. """Iterate over the namespace and:
  304. * gather private attributes
  305. * check for items which look like fields but are not (e.g. have no annotation) and warn.
  306. Args:
  307. namespace: The attribute dictionary of the class to be created.
  308. ignored_types: A tuple of ignore types.
  309. base_class_vars: A set of base class class variables.
  310. base_class_fields: A set of base class fields.
  311. Returns:
  312. A dict contains private attributes info.
  313. Raises:
  314. TypeError: If there is a `__root__` field in model.
  315. NameError: If private attribute name is invalid.
  316. PydanticUserError:
  317. - If a field does not have a type annotation.
  318. - If a field on base class was overridden by a non-annotated attribute.
  319. """
  320. from ..fields import ModelPrivateAttr, PrivateAttr
  321. FieldInfo = import_cached_field_info()
  322. all_ignored_types = ignored_types + default_ignored_types()
  323. private_attributes: dict[str, ModelPrivateAttr] = {}
  324. raw_annotations = namespace.get('__annotations__', {})
  325. if '__root__' in raw_annotations or '__root__' in namespace:
  326. raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'")
  327. ignored_names: set[str] = set()
  328. for var_name, value in list(namespace.items()):
  329. if var_name == 'model_config' or var_name == '__pydantic_extra__':
  330. continue
  331. elif (
  332. isinstance(value, type)
  333. and value.__module__ == namespace['__module__']
  334. and '__qualname__' in namespace
  335. and value.__qualname__.startswith(namespace['__qualname__'])
  336. ):
  337. # `value` is a nested type defined in this namespace; don't error
  338. continue
  339. elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools':
  340. ignored_names.add(var_name)
  341. continue
  342. elif isinstance(value, ModelPrivateAttr):
  343. if var_name.startswith('__'):
  344. raise NameError(
  345. 'Private attributes must not use dunder names;'
  346. f' use a single underscore prefix instead of {var_name!r}.'
  347. )
  348. elif is_valid_field_name(var_name):
  349. raise NameError(
  350. 'Private attributes must not use valid field names;'
  351. f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.'
  352. )
  353. private_attributes[var_name] = value
  354. del namespace[var_name]
  355. elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name):
  356. suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name
  357. raise NameError(
  358. f'Fields must not use names with leading underscores;'
  359. f' e.g., use {suggested_name!r} instead of {var_name!r}.'
  360. )
  361. elif var_name.startswith('__'):
  362. continue
  363. elif is_valid_privateattr_name(var_name):
  364. if var_name not in raw_annotations or not is_classvar_annotation(raw_annotations[var_name]):
  365. private_attributes[var_name] = cast(ModelPrivateAttr, PrivateAttr(default=value))
  366. del namespace[var_name]
  367. elif var_name in base_class_vars:
  368. continue
  369. elif var_name not in raw_annotations:
  370. if var_name in base_class_fields:
  371. raise PydanticUserError(
  372. f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. '
  373. f'All field definitions, including overrides, require a type annotation.',
  374. code='model-field-overridden',
  375. )
  376. elif isinstance(value, FieldInfo):
  377. raise PydanticUserError(
  378. f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation'
  379. )
  380. else:
  381. raise PydanticUserError(
  382. f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a '
  383. f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this '
  384. f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.",
  385. code='model-field-missing-annotation',
  386. )
  387. for ann_name, ann_type in raw_annotations.items():
  388. if (
  389. is_valid_privateattr_name(ann_name)
  390. and ann_name not in private_attributes
  391. and ann_name not in ignored_names
  392. # This condition can be a false negative when `ann_type` is stringified,
  393. # but it is handled in most cases in `set_model_fields`:
  394. and not is_classvar_annotation(ann_type)
  395. and ann_type not in all_ignored_types
  396. and getattr(ann_type, '__module__', None) != 'functools'
  397. ):
  398. if isinstance(ann_type, str):
  399. # Walking up the frames to get the module namespace where the model is defined
  400. # (as the model class wasn't created yet, we unfortunately can't use `cls.__module__`):
  401. frame = sys._getframe(2)
  402. if frame is not None:
  403. try:
  404. ann_type = eval_type_backport(
  405. _make_forward_ref(ann_type, is_argument=False, is_class=True),
  406. globalns=frame.f_globals,
  407. localns=frame.f_locals,
  408. )
  409. except (NameError, TypeError):
  410. pass
  411. if is_annotated(ann_type):
  412. _, *metadata = get_args(ann_type)
  413. private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None)
  414. if private_attr is not None:
  415. private_attributes[ann_name] = private_attr
  416. continue
  417. private_attributes[ann_name] = PrivateAttr()
  418. return private_attributes
  419. def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None:
  420. base_hash_func = get_attribute_from_bases(bases, '__hash__')
  421. new_hash_func = make_hash_func(cls)
  422. if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__:
  423. # If `__hash__` is some default, we generate a hash function.
  424. # It will be `None` if not overridden from BaseModel.
  425. # It may be `object.__hash__` if there is another
  426. # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`).
  427. # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model.
  428. # In the last case we still need a new hash function to account for new `model_fields`.
  429. cls.__hash__ = new_hash_func
  430. def make_hash_func(cls: type[BaseModel]) -> Any:
  431. getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0
  432. def hash_func(self: Any) -> int:
  433. try:
  434. return hash(getter(self.__dict__))
  435. except KeyError:
  436. # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain
  437. # all model fields, which is how we can get here.
  438. # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys,
  439. # and wrapping it in a `try` doesn't slow things down much in the common case.
  440. return hash(getter(SafeGetItemProxy(self.__dict__)))
  441. return hash_func
  442. def set_model_fields(
  443. cls: type[BaseModel],
  444. bases: tuple[type[Any], ...],
  445. config_wrapper: ConfigWrapper,
  446. ns_resolver: NsResolver | None,
  447. ) -> None:
  448. """Collect and set `cls.__pydantic_fields__` and `cls.__class_vars__`.
  449. Args:
  450. cls: BaseModel or dataclass.
  451. bases: Parents of the class, generally `cls.__bases__`.
  452. config_wrapper: The config wrapper instance.
  453. ns_resolver: Namespace resolver to use when getting model annotations.
  454. """
  455. typevars_map = get_model_typevars_map(cls)
  456. fields, class_vars = collect_model_fields(cls, bases, config_wrapper, ns_resolver, typevars_map=typevars_map)
  457. cls.__pydantic_fields__ = fields
  458. cls.__class_vars__.update(class_vars)
  459. for k in class_vars:
  460. # Class vars should not be private attributes
  461. # We remove them _here_ and not earlier because we rely on inspecting the class to determine its classvars,
  462. # but private attributes are determined by inspecting the namespace _prior_ to class creation.
  463. # In the case that a classvar with a leading-'_' is defined via a ForwardRef (e.g., when using
  464. # `__future__.annotations`), we want to remove the private attribute which was detected _before_ we knew it
  465. # evaluated to a classvar
  466. value = cls.__private_attributes__.pop(k, None)
  467. if value is not None and value.default is not PydanticUndefined:
  468. setattr(cls, k, value.default)
  469. def complete_model_class(
  470. cls: type[BaseModel],
  471. cls_name: str,
  472. config_wrapper: ConfigWrapper,
  473. *,
  474. raise_errors: bool = True,
  475. ns_resolver: NsResolver | None = None,
  476. create_model_module: str | None = None,
  477. ) -> bool:
  478. """Finish building a model class.
  479. This logic must be called after class has been created since validation functions must be bound
  480. and `get_type_hints` requires a class object.
  481. Args:
  482. cls: BaseModel or dataclass.
  483. cls_name: The model or dataclass name.
  484. config_wrapper: The config wrapper instance.
  485. raise_errors: Whether to raise errors.
  486. ns_resolver: The namespace resolver instance to use during schema building.
  487. create_model_module: The module of the class to be created, if created by `create_model`.
  488. Returns:
  489. `True` if the model is successfully completed, else `False`.
  490. Raises:
  491. PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs in`__get_pydantic_core_schema__`
  492. and `raise_errors=True`.
  493. """
  494. if config_wrapper.defer_build:
  495. set_model_mocks(cls, cls_name)
  496. return False
  497. typevars_map = get_model_typevars_map(cls)
  498. gen_schema = GenerateSchema(
  499. config_wrapper,
  500. ns_resolver,
  501. typevars_map,
  502. )
  503. handler = CallbackGetCoreSchemaHandler(
  504. partial(gen_schema.generate_schema, from_dunder_get_core_schema=False),
  505. gen_schema,
  506. ref_mode='unpack',
  507. )
  508. try:
  509. schema = cls.__get_pydantic_core_schema__(cls, handler)
  510. except PydanticUndefinedAnnotation as e:
  511. if raise_errors:
  512. raise
  513. set_model_mocks(cls, cls_name, f'`{e.name}`')
  514. return False
  515. core_config = config_wrapper.core_config(title=cls.__name__)
  516. try:
  517. schema = gen_schema.clean_schema(schema)
  518. except gen_schema.CollectedInvalid:
  519. set_model_mocks(cls, cls_name)
  520. return False
  521. # debug(schema)
  522. cls.__pydantic_core_schema__ = schema
  523. cls.__pydantic_validator__ = create_schema_validator(
  524. schema,
  525. cls,
  526. create_model_module or cls.__module__,
  527. cls.__qualname__,
  528. 'create_model' if create_model_module else 'BaseModel',
  529. core_config,
  530. config_wrapper.plugin_settings,
  531. )
  532. cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config)
  533. cls.__pydantic_complete__ = True
  534. # set __signature__ attr only for model class, but not for its instances
  535. # (because instances can define `__call__`, and `inspect.signature` shouldn't
  536. # use the `__signature__` attribute and instead generate from `__call__`).
  537. cls.__signature__ = LazyClassAttribute(
  538. '__signature__',
  539. partial(
  540. generate_pydantic_signature,
  541. init=cls.__init__,
  542. fields=cls.__pydantic_fields__,
  543. populate_by_name=config_wrapper.populate_by_name,
  544. extra=config_wrapper.extra,
  545. ),
  546. )
  547. return True
  548. def set_deprecated_descriptors(cls: type[BaseModel]) -> None:
  549. """Set data descriptors on the class for deprecated fields."""
  550. for field, field_info in cls.__pydantic_fields__.items():
  551. if (msg := field_info.deprecation_message) is not None:
  552. desc = _DeprecatedFieldDescriptor(msg)
  553. desc.__set_name__(cls, field)
  554. setattr(cls, field, desc)
  555. for field, computed_field_info in cls.__pydantic_computed_fields__.items():
  556. if (
  557. (msg := computed_field_info.deprecation_message) is not None
  558. # Avoid having two warnings emitted:
  559. and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__')
  560. ):
  561. desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property)
  562. desc.__set_name__(cls, field)
  563. setattr(cls, field, desc)
  564. class _DeprecatedFieldDescriptor:
  565. """Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.
  566. Attributes:
  567. msg: The deprecation message to be emitted.
  568. wrapped_property: The property instance if the deprecated field is a computed field, or `None`.
  569. field_name: The name of the field being deprecated.
  570. """
  571. field_name: str
  572. def __init__(self, msg: str, wrapped_property: property | None = None) -> None:
  573. self.msg = msg
  574. self.wrapped_property = wrapped_property
  575. def __set_name__(self, cls: type[BaseModel], name: str) -> None:
  576. self.field_name = name
  577. def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
  578. if obj is None:
  579. if self.wrapped_property is not None:
  580. return self.wrapped_property.__get__(None, obj_type)
  581. raise AttributeError(self.field_name)
  582. warnings.warn(self.msg, builtins.DeprecationWarning, stacklevel=2)
  583. if self.wrapped_property is not None:
  584. return self.wrapped_property.__get__(obj, obj_type)
  585. return obj.__dict__[self.field_name]
  586. # Defined to make it a data descriptor and take precedence over the instance's dictionary.
  587. # Note that it will not be called when setting a value on a model instance
  588. # as `BaseModel.__setattr__` is defined and takes priority.
  589. def __set__(self, obj: Any, value: Any) -> NoReturn:
  590. raise AttributeError(self.field_name)
  591. class _PydanticWeakRef:
  592. """Wrapper for `weakref.ref` that enables `pickle` serialization.
  593. Cloudpickle fails to serialize `weakref.ref` objects due to an arcane error related
  594. to abstract base classes (`abc.ABC`). This class works around the issue by wrapping
  595. `weakref.ref` instead of subclassing it.
  596. See https://github.com/pydantic/pydantic/issues/6763 for context.
  597. Semantics:
  598. - If not pickled, behaves the same as a `weakref.ref`.
  599. - If pickled along with the referenced object, the same `weakref.ref` behavior
  600. will be maintained between them after unpickling.
  601. - If pickled without the referenced object, after unpickling the underlying
  602. reference will be cleared (`__call__` will always return `None`).
  603. """
  604. def __init__(self, obj: Any):
  605. if obj is None:
  606. # The object will be `None` upon deserialization if the serialized weakref
  607. # had lost its underlying object.
  608. self._wr = None
  609. else:
  610. self._wr = weakref.ref(obj)
  611. def __call__(self) -> Any:
  612. if self._wr is None:
  613. return None
  614. else:
  615. return self._wr()
  616. def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]:
  617. return _PydanticWeakRef, (self(),)
  618. def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
  619. """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs.
  620. We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values
  621. in a WeakValueDictionary.
  622. The `unpack_lenient_weakvaluedict` function can be used to reverse this operation.
  623. """
  624. if d is None:
  625. return None
  626. result = {}
  627. for k, v in d.items():
  628. try:
  629. proxy = _PydanticWeakRef(v)
  630. except TypeError:
  631. proxy = v
  632. result[k] = proxy
  633. return result
  634. def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
  635. """Inverts the transform performed by `build_lenient_weakvaluedict`."""
  636. if d is None:
  637. return None
  638. result = {}
  639. for k, v in d.items():
  640. if isinstance(v, _PydanticWeakRef):
  641. v = v()
  642. if v is not None:
  643. result[k] = v
  644. else:
  645. result[k] = v
  646. return result
  647. @lru_cache(maxsize=None)
  648. def default_ignored_types() -> tuple[type[Any], ...]:
  649. from ..fields import ComputedFieldInfo
  650. ignored_types = [
  651. FunctionType,
  652. property,
  653. classmethod,
  654. staticmethod,
  655. PydanticDescriptorProxy,
  656. ComputedFieldInfo,
  657. TypeAliasType, # from `typing_extensions`
  658. ]
  659. if sys.version_info >= (3, 12):
  660. ignored_types.append(typing.TypeAliasType)
  661. return tuple(ignored_types)