1"""Private logic for creating models."""
2
3from __future__ import annotations as _annotations
4
5import operator
6import sys
7import typing
8import warnings
9import weakref
10from abc import ABCMeta
11from collections.abc import Callable, MutableMapping
12from functools import cache, partial, wraps
13from types import FunctionType
14from typing import TYPE_CHECKING, Any, Generic, Literal, NoReturn, TypeVar, cast
15
16from pydantic_core import PydanticUndefined, SchemaSerializer
17from typing_extensions import (
18 TypeAliasType,
19 dataclass_transform,
20 deprecated,
21)
22
23from ..errors import PydanticUndefinedAnnotation, PydanticUserError
24from ..plugin._schema_validator import create_schema_validator
25from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20
26from ._config import ConfigWrapper
27from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function
28from ._fields import (
29 ModelNamespaceInfo,
30 collect_model_fields,
31 is_valid_field_name,
32 is_valid_privateattr_name,
33 rebuild_model_fields,
34)
35from ._generate_schema import GenerateSchema, InvalidSchemaError
36from ._generics import PydanticGenericMetadata, get_model_typevars_map
37from ._import_utils import import_cached_base_model, import_cached_field_info
38from ._mock_val_ser import set_model_mocks
39from ._namespace_utils import NsResolver
40from ._signature import generate_pydantic_signature
41from ._typing_extra import parent_frame_namespace
42from ._utils import LazyClassAttribute, SafeGetItemProxy
43
44if TYPE_CHECKING:
45 from ..fields import Field as PydanticModelField
46 from ..fields import FieldInfo, ModelPrivateAttr
47 from ..fields import PrivateAttr as PydanticModelPrivateAttr
48 from ..main import BaseModel
49 from ._fields import PydanticExtraInfo
50else:
51 PydanticModelField = object()
52 PydanticModelPrivateAttr = object()
53
54object_setattr = object.__setattr__
55
56
57class _ModelNamespaceDict(dict):
58 """A dictionary subclass that intercepts attribute setting on model classes and
59 warns about overriding of decorators.
60 """
61
62 def __setitem__(self, k: str, v: object) -> None:
63 existing: Any = self.get(k, None)
64 if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy):
65 warnings.warn(
66 f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator',
67 stacklevel=2,
68 )
69
70 return super().__setitem__(k, v)
71
72
73def NoInitField(
74 *,
75 init: Literal[False] = False,
76) -> Any:
77 """Only for typing purposes. Used as default value of `__pydantic_fields_set__`,
78 `__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when
79 synthesizing the `__init__` signature.
80 """
81
82
83# For ModelMetaclass.register():
84_T = TypeVar('_T')
85
86
87@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField))
88class ModelMetaclass(ABCMeta):
89 def __new__(
90 mcs,
91 cls_name: str,
92 bases: tuple[type[Any], ...],
93 namespace: dict[str, Any],
94 __pydantic_generic_metadata__: PydanticGenericMetadata | None = None,
95 __pydantic_reset_parent_namespace__: bool = True,
96 _create_model_module: str | None = None,
97 **kwargs: Any,
98 ) -> type:
99 """Metaclass for creating Pydantic models.
100
101 Args:
102 cls_name: The name of the class to be created.
103 bases: The base classes of the class to be created.
104 namespace: The attribute dictionary of the class to be created.
105 __pydantic_generic_metadata__: Metadata for generic models.
106 __pydantic_reset_parent_namespace__: Reset parent namespace.
107 _create_model_module: The module of the class to be created, if created by `create_model`.
108 **kwargs: Catch-all for any other keyword arguments.
109
110 Returns:
111 The new class created by the metaclass.
112 """
113 # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact
114 # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__`
115 # call we're in the middle of is for the `BaseModel` class.
116 if bases:
117 base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases)
118
119 config_wrapper = ConfigWrapper.for_model(bases, namespace, kwargs)
120 namespace_info = inspect_namespace(
121 namespace,
122 config_wrapper.ignored_types,
123 base_class_fields=base_field_names,
124 base_class_vars=class_vars,
125 )
126 private_attributes = namespace_info.private_attributes
127 # Must be done after `inspect_namespace()` is called, as it checks for the
128 # user-assigned `model_config`:
129 namespace['model_config'] = config_wrapper.config_dict
130 namespace['__class_vars__'] = class_vars
131 namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes}
132
133 cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs))
134 BaseModel_ = import_cached_base_model()
135
136 mro = cls.__mro__
137 if Generic in mro and mro.index(Generic) < mro.index(BaseModel_):
138 warnings.warn(
139 GenericBeforeBaseModelWarning(
140 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) '
141 'for pydantic generics to work properly.'
142 ),
143 stacklevel=2,
144 )
145
146 cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False)
147
148 cls.__pydantic_setattr_handlers__ = {}
149
150 cls.__pydantic_decorators__ = DecoratorInfos.build(cls, replace_wrapped_methods=True)
151 cls.__pydantic_decorators__.update_from_config(config_wrapper)
152
153 # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class
154 if __pydantic_generic_metadata__:
155 cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__
156 else:
157 parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ())
158 parameters = getattr(cls, '__parameters__', None) or parent_parameters
159 if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
160 from ..root_model import RootModelRootType
161
162 missing_parameters = tuple(x for x in parameters if x not in parent_parameters)
163 if RootModelRootType in parent_parameters and RootModelRootType not in parameters:
164 # This is a special case where the user has subclassed RootModel, but has not parameterized
165 # RootModel with the generic type identifiers being used. Ex:
166 # class MyModel(RootModel, Generic[T]):
167 # root: T
168 # Should instead just be:
169 # class MyModel(RootModel[T]):
170 # root: T
171 parameters_str = ', '.join([x.__name__ for x in missing_parameters])
172 error_message = (
173 f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) '
174 f'{parameters_str} in its parameters. '
175 f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.'
176 )
177 else:
178 combined_parameters = parent_parameters + missing_parameters
179 parameters_str = ', '.join([str(x) for x in combined_parameters])
180 generic_type_label = f'typing.Generic[{parameters_str}]'
181 error_message = (
182 f'All parameters must be present on typing.Generic;'
183 f' you should inherit from {generic_type_label}.'
184 )
185 if Generic not in bases: # pragma: no cover
186 # We raise an error here not because it is desirable, but because some cases are mishandled.
187 # It would be nice to remove this error and still have things behave as expected, it's just
188 # challenging because we are using a custom `__class_getitem__` to parametrize generic models,
189 # and not returning a typing._GenericAlias from it.
190 bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
191 error_message += (
192 f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
193 )
194 raise TypeError(error_message)
195
196 cls.__pydantic_generic_metadata__ = {
197 'origin': None,
198 'args': (),
199 'parameters': parameters,
200 }
201
202 cls.__pydantic_complete__ = False # Ensure this specific class gets completed
203
204 # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
205 # for attributes not in `new_namespace` (e.g. private attributes)
206 for name, obj in private_attributes.items():
207 obj.__set_name__(cls, name)
208
209 if __pydantic_reset_parent_namespace__:
210 cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace())
211 parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None)
212 if isinstance(parent_namespace, dict):
213 parent_namespace = unpack_lenient_weakvaluedict(parent_namespace)
214
215 ns_resolver = NsResolver(parent_namespace=parent_namespace)
216
217 set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver, namespace_info=namespace_info)
218
219 # This can only be done after fields collection, as private attributes may be
220 # discovered from the (evaluated) class annotations:
221 if cls.__private_attributes__:
222 original_model_post_init = get_model_post_init(namespace, bases)
223 if original_model_post_init is not None:
224 # if there are private attributes and a model_post_init function, we handle both
225
226 @wraps(original_model_post_init)
227 def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None:
228 """We need to both initialize private attributes and call the user-defined model_post_init
229 method.
230 """
231 init_private_attributes(self, context)
232 original_model_post_init(self, context)
233
234 cls.model_post_init = wrapped_model_post_init # pyright: ignore[reportAttributeAccessIssue]
235 else:
236 cls.model_post_init = init_private_attributes # pyright: ignore[reportAttributeAccessIssue]
237
238 cls.__pydantic_post_init__ = (
239 None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init'
240 )
241
242 # This is also set in `complete_model_class()`, after schema gen because they are recreated.
243 # We set them here as well for backwards compatibility:
244 cls.__pydantic_computed_fields__ = {
245 k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()
246 }
247
248 if config_wrapper.defer_build:
249 set_model_mocks(cls)
250 else:
251 # Any operation that requires accessing the field infos instances should be put inside
252 # `complete_model_class()`:
253 complete_model_class(
254 cls,
255 config_wrapper,
256 ns_resolver,
257 raise_errors=False,
258 create_model_module=_create_model_module,
259 )
260
261 if config_wrapper.frozen and '__hash__' not in namespace:
262 set_default_hash_func(cls, bases)
263
264 # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__
265 # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is
266 # only hit for _proper_ subclasses of BaseModel
267 super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc]
268 return cls
269 else:
270 # These are instance variables, but have been assigned to `NoInitField` to trick the type checker.
271 for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__':
272 namespace.pop(
273 instance_slot,
274 None, # In case the metaclass is used with a class other than `BaseModel`.
275 )
276 namespace.get('__annotations__', {}).clear()
277 return super().__new__(mcs, cls_name, bases, namespace, **kwargs)
278
279 if not TYPE_CHECKING: # pragma: no branch
280 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
281
282 def __getattr__(self, item: str) -> Any:
283 """This is necessary to keep attribute access working for class attribute access."""
284 private_attributes = self.__dict__.get('__private_attributes__')
285 if private_attributes and item in private_attributes:
286 return private_attributes[item]
287 raise AttributeError(item)
288
289 @classmethod
290 def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]:
291 return _ModelNamespaceDict()
292
293 # Due to performance and memory issues, in the ABCMeta.__subclasscheck__ implementation, we don't support
294 # registered virtual subclasses. See https://github.com/python/cpython/issues/92810#issuecomment-2762454345.
295 # This may change once CPython is fixed (possibly in 3.15), in which case we should conditionally
296 # define `register()`.
297 def register(self, subclass: type[_T]) -> type[_T]:
298 warnings.warn(
299 f"For performance reasons, virtual subclasses registered using '{self.__qualname__}.register()' "
300 "are not supported in 'isinstance()' and 'issubclass()' checks.",
301 stacklevel=2,
302 )
303 return super().register(subclass)
304
305 __instancecheck__ = type.__instancecheck__ # pyright: ignore[reportAssignmentType]
306 __subclasscheck__ = type.__subclasscheck__ # pyright: ignore[reportAssignmentType]
307
308 @staticmethod
309 def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]:
310 BaseModel = import_cached_base_model()
311
312 field_names: set[str] = set()
313 class_vars: set[str] = set()
314 private_attributes: dict[str, ModelPrivateAttr] = {}
315 for base in bases:
316 if issubclass(base, BaseModel) and base is not BaseModel:
317 # model_fields might not be defined yet in the case of generics, so we use getattr here:
318 field_names.update(getattr(base, '__pydantic_fields__', {}).keys())
319 class_vars.update(base.__class_vars__)
320 private_attributes.update(base.__private_attributes__)
321 return field_names, class_vars, private_attributes
322
323 @property
324 @deprecated(
325 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None
326 )
327 def __fields__(self) -> dict[str, FieldInfo]:
328 warnings.warn(
329 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.',
330 PydanticDeprecatedSince20,
331 stacklevel=2,
332 )
333 return getattr(self, '__pydantic_fields__', {})
334
335 @property
336 def __pydantic_fields_complete__(self) -> bool:
337 """Whether the fields were successfully collected (i.e. type hints were successfully resolved).
338
339 This is a private attribute, not meant to be used outside Pydantic.
340 """
341 if '__pydantic_fields__' not in self.__dict__:
342 return False
343
344 field_infos = cast('dict[str, FieldInfo]', self.__pydantic_fields__) # pyright: ignore[reportAttributeAccessIssue]
345
346 pydantic_extra_info = cast('PydanticExtraInfo | None', self.__pydantic_extra_info__) # pyright: ignore[reportAttributeAccessIssue]
347 if pydantic_extra_info is not None:
348 extra_complete = pydantic_extra_info.complete
349 else:
350 extra_complete = True
351
352 return all(field_info._complete for field_info in field_infos.values()) and extra_complete
353
354 def __dir__(self) -> list[str]:
355 attributes = list(super().__dir__())
356 if '__fields__' in attributes:
357 attributes.remove('__fields__')
358 return attributes
359
360
361def init_private_attributes(self: BaseModel, context: Any, /) -> None:
362 """This function is meant to behave like a BaseModel method to initialize private attributes.
363
364 It takes context as an argument since that's what pydantic-core passes when calling it.
365
366 Args:
367 self: The BaseModel instance.
368 context: The context.
369 """
370 if getattr(self, '__pydantic_private__', None) is None:
371 pydantic_private = {}
372 for name, private_attr in self.__private_attributes__.items():
373 # Avoid needlessly creating a new dict for the validated data:
374 if private_attr.default_factory_takes_validated_data:
375 default = private_attr.get_default(
376 call_default_factory=True, validated_data={**self.__dict__, **pydantic_private}
377 )
378 else:
379 default = private_attr.get_default(call_default_factory=True)
380 if default is not PydanticUndefined:
381 pydantic_private[name] = default
382 object_setattr(self, '__pydantic_private__', pydantic_private)
383
384
385def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None:
386 """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined."""
387 if 'model_post_init' in namespace:
388 return namespace['model_post_init']
389
390 BaseModel = import_cached_base_model()
391
392 model_post_init = get_attribute_from_bases(bases, 'model_post_init')
393 if model_post_init is not BaseModel.model_post_init:
394 return model_post_init
395
396
397def inspect_namespace(
398 namespace: dict[str, Any],
399 ignored_types: tuple[type[Any], ...],
400 *,
401 base_class_vars: set[str],
402 base_class_fields: set[str],
403) -> ModelNamespaceInfo:
404 """Inspect the namespace prior to class creation.
405
406 Most of the collected information is then used by `collect_model_fields()` to perform checks.
407
408 Args:
409 namespace: The attribute dictionary of the class to be created.
410 ignored_types: Tuple of ignore types.
411 base_class_vars: The set of base class variables.
412 base_class_fields: The set of base class fields.
413
414 Returns:
415 A `ModelNamespaceInfo` instance.
416
417 Raises:
418 PydanticUserError: If there is a `__root__` field in model, or if a private attribute name is invalid.
419 """
420 from ..fields import ModelPrivateAttr
421
422 FieldInfo = import_cached_field_info()
423
424 all_ignored_types = ignored_types + default_ignored_types()
425
426 private_attributes: dict[str, ModelPrivateAttr] = {}
427 ignored_names: set[str] = set()
428 private_candidates: list[str] = []
429 field_candidates: list[str] = []
430
431 if '__root__' in namespace:
432 # This only checks for assignment, `collect_model_fields()` will raise if only an annotated `__root__` is defined:
433 raise PydanticUserError(
434 "To define root models, use `pydantic.RootModel` rather than a field called '__root__'", code=None
435 )
436
437 for var_name, value in namespace.items():
438 if var_name == 'model_config' or var_name == '__pydantic_extra__':
439 continue
440 elif (
441 isinstance(value, type)
442 and value.__module__ == namespace['__module__']
443 and '__qualname__' in namespace
444 and value.__qualname__.startswith(f'{namespace["__qualname__"]}.')
445 ):
446 # `value` is a nested type defined in this namespace; don't error
447 continue
448 elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools':
449 ignored_names.add(var_name)
450 continue
451 elif isinstance(value, ModelPrivateAttr):
452 if var_name.startswith('__'):
453 raise PydanticUserError(
454 'Private attributes must not use dunder names;'
455 f' use a single underscore prefix instead of {var_name!r}.',
456 code=None,
457 )
458 elif is_valid_field_name(var_name):
459 raise PydanticUserError(
460 'Private attributes must not use valid field names;'
461 f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.',
462 code=None,
463 )
464 private_attributes[var_name] = value
465 elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name):
466 suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name
467 raise PydanticUserError(
468 f'Fields must not use names with leading underscores;'
469 f' e.g., use {suggested_name!r} instead of {var_name!r}.',
470 code=None,
471 )
472 elif var_name.startswith('__'):
473 continue
474 elif is_valid_privateattr_name(var_name):
475 # A private attribute, unless annotated as a class variable (only known
476 # once annotations are evaluated during fields collection):
477 private_candidates.append(var_name)
478 elif var_name in base_class_vars:
479 continue
480 else:
481 # A field default or an invalid non-annotated attribute (only known
482 # once annotations are evaluated during fields collection):
483 field_candidates.append(var_name)
484
485 for var_name in private_attributes:
486 del namespace[var_name]
487
488 return ModelNamespaceInfo(
489 private_attributes=private_attributes,
490 ignored_names=ignored_names,
491 ignored_types=all_ignored_types,
492 private_candidates=private_candidates,
493 field_candidates=field_candidates,
494 base_field_names=base_class_fields,
495 model_config_assigned=namespace.get('model_config') is not None,
496 )
497
498
499def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None:
500 base_hash_func = get_attribute_from_bases(bases, '__hash__')
501 new_hash_func = make_hash_func(cls)
502 if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__:
503 # If `__hash__` is some default, we generate a hash function.
504 # It will be `None` if not overridden from BaseModel.
505 # It may be `object.__hash__` if there is another
506 # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`).
507 # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model.
508 # In the last case we still need a new hash function to account for new `model_fields`.
509 cls.__hash__ = new_hash_func
510
511
512def make_hash_func(cls: type[BaseModel]) -> Any:
513 getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0
514
515 def hash_func(self: Any) -> int:
516 try:
517 return hash(getter(self.__dict__))
518 except KeyError:
519 # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain
520 # all model fields, which is how we can get here.
521 # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys,
522 # and wrapping it in a `try` doesn't slow things down much in the common case.
523 return hash(getter(SafeGetItemProxy(self.__dict__)))
524
525 return hash_func
526
527
528def set_model_fields(
529 cls: type[BaseModel],
530 config_wrapper: ConfigWrapper,
531 ns_resolver: NsResolver,
532 namespace_info: ModelNamespaceInfo | None = None,
533) -> None:
534 """Collect and set `cls.__pydantic_fields__`, `cls.__class_vars__` and `cls.__private_attributes__`.
535
536 Args:
537 cls: BaseModel or dataclass.
538 config_wrapper: The config wrapper instance.
539 ns_resolver: Namespace resolver to use when getting model annotations.
540 namespace_info: The result of inspecting the class namespace before the class was created.
541 """
542 typevars_map = get_model_typevars_map(cls)
543 fields, pydantic_extra_info, class_vars, private_attributes = collect_model_fields(
544 cls, config_wrapper, ns_resolver, typevars_map=typevars_map, namespace_info=namespace_info
545 )
546
547 cls.__pydantic_fields__ = fields
548 cls.__pydantic_extra_info__ = pydantic_extra_info
549 cls.__class_vars__.update(class_vars)
550 cls.__private_attributes__.update(private_attributes)
551
552 for k in class_vars:
553 # Class vars should not be private attributes (this can happen when a class variable
554 # of a base class shadows a private attribute, e.g. when annotated as a `ClassVar`
555 # in a subclass):
556 value = cls.__private_attributes__.pop(k, None)
557 if value is not None and value.default is not PydanticUndefined:
558 setattr(cls, k, value.default)
559
560
561def complete_model_class(
562 cls: type[BaseModel],
563 config_wrapper: ConfigWrapper,
564 ns_resolver: NsResolver,
565 *,
566 raise_errors: bool = True,
567 call_on_complete_hook: bool = True,
568 create_model_module: str | None = None,
569 is_force_rebuild: bool = False,
570) -> bool:
571 """Finish building a model class.
572
573 This logic must be called after class has been created since validation functions must be bound
574 and `get_type_hints` requires a class object.
575
576 Args:
577 cls: BaseModel or dataclass.
578 config_wrapper: The config wrapper instance.
579 ns_resolver: The namespace resolver instance to use during schema building.
580 raise_errors: Whether to raise errors.
581 call_on_complete_hook: Whether to call the `__pydantic_on_complete__` hook.
582 create_model_module: The module of the class to be created, if created by `create_model`.
583 is_force_rebuild: Whether the model is being force-rebuilt (if True, pre-built serializers and
584 validators are not used, to avoid stale references).
585
586 Returns:
587 `True` if the model is successfully completed, else `False`.
588
589 Raises:
590 PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs during core schema generation
591 and `raise_errors` is True.
592 """
593 typevars_map = get_model_typevars_map(cls)
594
595 if not cls.__pydantic_fields_complete__:
596 # Note: when coming from `ModelMetaclass.__new__()`, this results in fields being built twice.
597 # We do so a second time here so that we can get the ``NameError`` for the specific undefined annotation.
598 # Alternatively, we could let `GenerateSchema()` raise the error, but there are cases where incomplete
599 # fields are inherited in `collect_model_fields()` and can actually have their annotation resolved in the
600 # generate schema process. As we want to avoid having `__pydantic_fields_complete__` set to `False`
601 # when `__pydantic_complete__` is `True`, we rebuild here:
602 try:
603 cls.__pydantic_fields__, cls.__pydantic_extra_info__ = rebuild_model_fields(
604 cls,
605 config_wrapper=config_wrapper,
606 ns_resolver=ns_resolver,
607 typevars_map=typevars_map,
608 )
609 except NameError as e:
610 exc = PydanticUndefinedAnnotation.from_name_error(e)
611 set_model_mocks(cls, f'`{exc.name}`' if exc.name is not None else None)
612 if raise_errors:
613 raise exc from e
614
615 if not raise_errors and not cls.__pydantic_fields_complete__:
616 # No need to continue with schema gen, it is guaranteed to fail
617 return False
618
619 assert cls.__pydantic_fields_complete__
620
621 gen_schema = GenerateSchema(
622 config_wrapper,
623 ns_resolver,
624 typevars_map,
625 )
626
627 try:
628 schema = gen_schema.generate_schema(cls)
629 except PydanticUndefinedAnnotation as e:
630 if raise_errors:
631 raise
632 set_model_mocks(cls, f'`{e.name}`' if e.name is not None else None)
633 return False
634
635 core_config = config_wrapper.core_config(title=cls.__name__)
636
637 try:
638 schema = gen_schema.clean_schema(schema)
639 except InvalidSchemaError:
640 set_model_mocks(cls)
641 return False
642
643 # This needs to happen *after* model schema generation, as the return types
644 # of the properties are evaluated and the `ComputedFieldInfo` are recreated:
645 cls.__pydantic_computed_fields__ = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()}
646
647 set_deprecated_descriptors(cls)
648
649 cls.__pydantic_core_schema__ = schema
650
651 cls.__pydantic_validator__ = create_schema_validator(
652 schema,
653 cls,
654 create_model_module or cls.__module__,
655 cls.__qualname__,
656 'create_model' if create_model_module else 'BaseModel',
657 core_config,
658 config_wrapper.plugin_settings,
659 _use_prebuilt=not is_force_rebuild,
660 )
661 cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config, _use_prebuilt=not is_force_rebuild)
662
663 # set __signature__ attr only for model class, but not for its instances
664 # (because instances can define `__call__`, and `inspect.signature` shouldn't
665 # use the `__signature__` attribute and instead generate from `__call__`).
666 cls.__signature__ = LazyClassAttribute(
667 '__signature__',
668 partial(
669 generate_pydantic_signature,
670 init=cls.__init__,
671 fields=cls.__pydantic_fields__,
672 validate_by_alias=config_wrapper.validate_by_alias,
673 extra=config_wrapper.extra,
674 ),
675 )
676
677 cls.__pydantic_complete__ = True
678
679 if call_on_complete_hook:
680 cls.__pydantic_on_complete__()
681
682 return True
683
684
685def set_deprecated_descriptors(cls: type[BaseModel]) -> None:
686 """Set data descriptors on the class for deprecated fields."""
687 for field, field_info in cls.__pydantic_fields__.items():
688 if (msg := field_info.deprecation_message) is not None:
689 desc = _DeprecatedFieldDescriptor(msg)
690 desc.__set_name__(cls, field)
691 setattr(cls, field, desc)
692
693 for field, computed_field_info in cls.__pydantic_computed_fields__.items():
694 if (
695 (msg := computed_field_info.deprecation_message) is not None
696 # If the property was already decorated with `@deprecated`, we avoid emitting the warning message
697 # from `@computed_field` and give priority to the other. Ideally, the deprecation message from
698 # `@computed_field` should take priority, but we can't safely unwrap (using `inspect.unwrap()`)
699 # to get the inner property decorated by `@deprecated`, as we might skip other user-defined decorators
700 # while doing so:
701 and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__')
702 ):
703 desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property)
704 desc.__set_name__(cls, field)
705 setattr(cls, field, desc)
706
707
708class _DeprecatedFieldDescriptor:
709 """Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.
710
711 Attributes:
712 msg: The deprecation message to be emitted.
713 wrapped_property: The property instance if the deprecated field is a computed field, or `None`.
714 field_name: The name of the field being deprecated.
715 """
716
717 field_name: str
718
719 def __init__(self, msg: str, wrapped_property: property | None = None) -> None:
720 self.msg = msg
721 self.wrapped_property = wrapped_property
722
723 def __set_name__(self, cls: type[BaseModel], name: str) -> None:
724 self.field_name = name
725
726 def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
727 if obj is None:
728 if self.wrapped_property is not None:
729 return self.wrapped_property.__get__(None, obj_type)
730 raise AttributeError(self.field_name)
731
732 warnings.warn(self.msg, DeprecationWarning, stacklevel=2)
733
734 if self.wrapped_property is not None:
735 return self.wrapped_property.__get__(obj, obj_type)
736 return obj.__dict__[self.field_name]
737
738 # Defined to make it a data descriptor and take precedence over the instance's dictionary.
739 # Note that it will not be called when setting a value on a model instance
740 # as `BaseModel.__setattr__` is defined and takes priority.
741 def __set__(self, obj: Any, value: Any) -> NoReturn:
742 raise AttributeError(self.field_name)
743
744
745class _PydanticWeakRef:
746 """Wrapper for `weakref.ref` that enables `pickle` serialization.
747
748 Cloudpickle fails to serialize weakref.ref objects due to an arcane error related to
749 to abstract base classes (`abc.ABC`). This class works around the issue by wrapping
750 `weakref.ref` instead of subclassing it.
751
752 See https://github.com/pydantic/pydantic/issues/6763 for context.
753
754 Semantics:
755 - If not pickled, behaves the same as a `weakref.ref`.
756 - If pickled along with the referenced object, the same `weakref.ref` behavior
757 will be maintained between them after unpickling.
758 - If pickled without the referenced object, after unpickling the underlying
759 reference will be cleared (`__call__` will always return `None`).
760 """
761
762 def __init__(self, obj: Any):
763 if obj is None:
764 # The object will be `None` upon deserialization if the serialized weakref
765 # had lost its underlying object.
766 self._wr = None
767 else:
768 self._wr = weakref.ref(obj)
769
770 def __call__(self) -> Any:
771 if self._wr is None:
772 return None
773 else:
774 return self._wr()
775
776 def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]:
777 return _PydanticWeakRef, (self(),)
778
779
780def build_lenient_weakvaluedict(d: MutableMapping[str, Any] | None) -> dict[str, Any] | None:
781 """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs.
782
783 We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values
784 in a WeakValueDictionary.
785
786 The `unpack_lenient_weakvaluedict` function can be used to reverse this operation.
787 """
788 if d is None:
789 return None
790 result = {}
791 for k, v in d.items():
792 try:
793 proxy = _PydanticWeakRef(v)
794 except TypeError:
795 proxy = v
796 result[k] = proxy
797 return result
798
799
800def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
801 """Inverts the transform performed by `build_lenient_weakvaluedict`."""
802 if d is None:
803 return None
804
805 result = {}
806 for k, v in d.items():
807 if isinstance(v, _PydanticWeakRef):
808 v = v()
809 if v is not None:
810 result[k] = v
811 else:
812 result[k] = v
813 return result
814
815
816@cache
817def default_ignored_types() -> tuple[type[Any], ...]:
818 from ..fields import ComputedFieldInfo
819
820 ignored_types = [
821 FunctionType,
822 property,
823 classmethod,
824 staticmethod,
825 PydanticDescriptorProxy,
826 ComputedFieldInfo,
827 TypeAliasType, # from `typing_extensions`
828 ]
829
830 if sys.version_info >= (3, 12):
831 ignored_types.append(typing.TypeAliasType)
832
833 return tuple(ignored_types)