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