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