Coverage for pydantic/_internal/_fields.py: 98.73%
253 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 related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
3from __future__ import annotations as _annotations 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
5import dataclasses 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
6import warnings 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
7from collections.abc import Mapping 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
8from copy import copy 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
9from functools import cache 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
10from inspect import Parameter, ismethoddescriptor, signature 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
11from re import Pattern 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
12from typing import TYPE_CHECKING, Any, Callable, TypeVar 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
14from pydantic_core import PydanticUndefined 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
15from typing_extensions import TypeIs 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
16from typing_inspection.introspection import AnnotationSource 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
18from pydantic import PydanticDeprecatedSince211 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
19from pydantic.errors import PydanticUserError 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
21from ..aliases import AliasGenerator 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
22from . import _generics, _typing_extra 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
23from ._config import ConfigWrapper 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
24from ._docs_extraction import extract_docstrings_from_cls 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
25from ._import_utils import import_cached_base_model, import_cached_field_info 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
26from ._namespace_utils import NsResolver 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
27from ._repr import Representation 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
28from ._utils import can_be_positional, get_first_not_none 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
30if TYPE_CHECKING: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
31 from annotated_types import BaseMetadata
33 from ..fields import FieldInfo
34 from ..main import BaseModel
35 from ._dataclasses import PydanticDataclass, StandardDataclass
36 from ._decorators import DecoratorInfos
39class PydanticMetadata(Representation): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
40 """Base class for annotation markers like `Strict`."""
42 __slots__ = () 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
45def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
46 """Create a new `_PydanticGeneralMetadata` class with the given metadata.
48 Args:
49 **metadata: The metadata to add.
51 Returns:
52 The new `_PydanticGeneralMetadata` class.
53 """
54 return _general_metadata_cls()(metadata) # type: ignore 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
57@cache 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
58def _general_metadata_cls() -> type[BaseMetadata]: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
59 """Do it this way to avoid importing `annotated_types` at import time."""
60 from annotated_types import BaseMetadata 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
63 """Pydantic general metadata like `max_digits`."""
65 def __init__(self, metadata: Any): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
66 self.__dict__ = metadata 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
68 return _PydanticGeneralMetadata # type: ignore 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
71def _check_protected_namespaces( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
72 protected_namespaces: tuple[str | Pattern[str], ...],
73 ann_name: str,
74 bases: tuple[type[Any], ...],
75 cls_name: str,
76) -> None:
77 BaseModel = import_cached_base_model() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
79 for protected_namespace in protected_namespaces: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
80 ns_violation = False 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
81 if isinstance(protected_namespace, Pattern): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
82 ns_violation = protected_namespace.match(ann_name) is not None 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
83 elif isinstance(protected_namespace, str): 83 ↛ 86line 83 didn't jump to line 86 because the condition on line 83 was always true1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
84 ns_violation = ann_name.startswith(protected_namespace) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
86 if ns_violation: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
87 for b in bases: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
88 if hasattr(b, ann_name): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
89 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 89 ↛ 87line 89 didn't jump to line 87 because the condition on line 89 was always true1abcghidefjkl
90 raise ValueError( 1abcghidefjkl
91 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
92 f' of protected namespace {protected_namespace!r}.'
93 )
94 else:
95 valid_namespaces: list[str] = [] 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
96 for pn in protected_namespaces: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
97 if isinstance(pn, Pattern): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
98 if not pn.match(ann_name): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
99 valid_namespaces.append(f're.compile({pn.pattern!r})') 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
100 else:
101 if not ann_name.startswith(pn): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
102 valid_namespaces.append(f"'{pn}'") 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
104 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
106 warnings.warn( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
107 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
108 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
109 UserWarning,
110 stacklevel=5,
111 )
114def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
115 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
116 for ann_name, field_info in fields.items(): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
117 if field_info.description is None and ann_name in fields_docs: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
118 field_info.description = fields_docs[ann_name] 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
121def _apply_field_title_generator_to_field_info( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
122 title_generator: Callable[[str, FieldInfo], str],
123 field_name: str,
124 field_info: FieldInfo,
125):
126 if field_info.title is None: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
127 title = title_generator(field_name, field_info) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
128 if not isinstance(title, str): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
129 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}') 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
131 field_info.title = title 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
134def _apply_alias_generator_to_field_info( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
135 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
136):
137 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
139 Args:
140 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
141 field_name: The name of the field from which to generate the alias.
142 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
143 """
144 # Apply an alias_generator if
145 # 1. An alias is not specified
146 # 2. An alias is specified, but the priority is <= 1
147 if ( 1amOCpqdv
148 field_info.alias_priority is None
149 or field_info.alias_priority <= 1
150 or field_info.alias is None
151 or field_info.validation_alias is None
152 or field_info.serialization_alias is None
153 ):
154 alias, validation_alias, serialization_alias = None, None, None 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
156 if isinstance(alias_generator, AliasGenerator): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
157 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
158 elif callable(alias_generator): 158 ↛ 166line 158 didn't jump to line 166 because the condition on line 158 was always true1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
159 alias = alias_generator(field_name) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
160 if not isinstance(alias, str): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
161 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
163 # if priority is not set, we set to 1
164 # which supports the case where the alias_generator from a child class is used
165 # to generate an alias for a field in a parent class
166 if field_info.alias_priority is None or field_info.alias_priority <= 1: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
167 field_info.alias_priority = 1 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
169 # if the priority is 1, then we set the aliases to the generated alias
170 if field_info.alias_priority == 1: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
171 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
172 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
173 field_info.alias = alias 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
175 # if any of the aliases are not set, then we set them to the corresponding generated alias
176 if field_info.alias is None: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
177 field_info.alias = alias 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
178 if field_info.serialization_alias is None: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
179 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
180 if field_info.validation_alias is None: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
181 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
184def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
185 """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
187 This will apply the title and alias generators from the configuration.
189 Args:
190 config_wrapper: The configuration from the model.
191 field_name: The field name the `FieldInfo` instance is attached to.
192 field_info: The `FieldInfo` instance to update.
193 """
194 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
195 if field_title_generator is not None: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
196 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
197 if config_wrapper.alias_generator is not None: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
198 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
201_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'} 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
203_deprecated_classmethod_names = { 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
204 'parse_obj',
205 'parse_raw',
206 'parse_file',
207 'from_orm',
208 'construct',
209 'schema',
210 'schema_json',
211 'validate',
212 'update_forward_refs',
213 '_get_value',
214}
217def collect_model_fields( # noqa: C901 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
218 cls: type[BaseModel],
219 config_wrapper: ConfigWrapper,
220 ns_resolver: NsResolver | None,
221 *,
222 typevars_map: Mapping[TypeVar, Any] | None = None,
223) -> tuple[dict[str, FieldInfo], set[str]]:
224 """Collect the fields and class variables names of a nascent Pydantic model.
226 The fields collection process is *lenient*, meaning it won't error if string annotations
227 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
228 is stored on the created `FieldInfo` instance.
230 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
231 and will make use of these stored attributes.
233 Args:
234 cls: BaseModel or dataclass.
235 config_wrapper: The config wrapper instance.
236 ns_resolver: Namespace resolver to use when getting model annotations.
237 typevars_map: A dictionary mapping type variables to their concrete types.
239 Returns:
240 A two-tuple containing model fields and class variables names.
242 Raises:
243 NameError:
244 - If there is a conflict between a field name and protected namespaces.
245 - If there is a field other than `root` in `RootModel`.
246 - If a field shadows an attribute in the parent model.
247 """
248 FieldInfo_ = import_cached_field_info() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
249 BaseModel_ = import_cached_base_model() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
251 bases = cls.__bases__ 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
252 parent_fields_lookup: dict[str, FieldInfo] = {} 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
253 for base in reversed(bases): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
254 if model_fields := getattr(base, '__pydantic_fields__', None): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
255 parent_fields_lookup.update(model_fields) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
257 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
259 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
260 # annotations is only used for finding fields in parent classes
261 annotations = _typing_extra.safe_get_annotations(cls) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
263 fields: dict[str, FieldInfo] = {} 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
265 class_vars: set[str] = set() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
266 for ann_name, (ann_type, evaluated) in type_hints.items(): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
267 if ann_name == 'model_config': 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
268 # We never want to treat `model_config` as a field
269 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
270 # protected namespaces (where `model_config` might be allowed as a field name)
271 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
273 _check_protected_namespaces( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
274 protected_namespaces=config_wrapper.protected_namespaces,
275 ann_name=ann_name,
276 bases=bases,
277 cls_name=cls.__name__,
278 )
280 if _typing_extra.is_classvar_annotation(ann_type): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
281 class_vars.add(ann_name) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
282 continue 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
284 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
285 if assigned_value is not PydanticUndefined and ( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
286 # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
287 any(getattr(BaseModel_, depr_name, None) is assigned_value for depr_name in _deprecated_method_names)
288 # One of the deprecated class methods was used as a field name (e.g. `schema()`):
289 or (
290 hasattr(assigned_value, '__func__')
291 and any(
292 getattr(getattr(BaseModel_, depr_name, None), '__func__', None) is assigned_value.__func__ # pyright: ignore[reportAttributeAccessIssue]
293 for depr_name in _deprecated_classmethod_names
294 )
295 )
296 ):
297 # Then `assigned_value` would be the method, even though no default was specified:
298 assigned_value = PydanticUndefined 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
300 if not is_valid_field_name(ann_name): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
301 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
302 if cls.__pydantic_root_model__ and ann_name != 'root': 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
303 raise NameError( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
304 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
305 )
307 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
308 # "... shadows an attribute" warnings
309 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
310 for base in bases: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
311 dataclass_fields = { 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
312 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
313 }
314 if hasattr(base, ann_name): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
315 if base is generic_origin: 315 ↛ 317line 315 didn't jump to line 317 because the condition on line 315 was never true1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
316 # Don't warn when "shadowing" of attributes in parametrized generics
317 continue
319 if ann_name in dataclass_fields: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
320 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
321 # on the class instance.
322 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
324 if ann_name not in annotations: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
325 # Don't warn when a field exists in a parent class but has not been defined in the current class
326 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
328 warnings.warn( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
329 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
330 f'"{base.__qualname__}"',
331 UserWarning,
332 )
334 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
335 if ann_name in annotations or ann_name not in parent_fields_lookup: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
336 # field is either:
337 # - present in the current model's annotations (and *not* from parent classes)
338 # - not found on any base classes; this seems to be caused by fields bot getting
339 # generated due to models not being fully defined while initializing recursive models.
340 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
341 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
342 if not evaluated: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
343 field_info._complete = False 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
344 # Store the original annotation that should be used to rebuild
345 # the field info later:
346 field_info._original_annotation = ann_type 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
347 else:
348 # The field was present on one of the (possibly multiple) base classes
349 # copy the field to make sure typevar substitutions don't cause issues with the base classes
350 field_info = copy(parent_fields_lookup[ann_name]) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
352 else: # An assigned value is present (either the default value, or a `Field()` function)
353 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
354 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
355 # for descriptors, so we do the same if the `= field(default=...)` form is used.
356 # Note that we only do this for method descriptors for now, we might want to
357 # extend this to any descriptor in the future (by simply checking for
358 # `hasattr(assigned_value.default, '__get__')`).
359 default = assigned_value.default.__get__(None, cls) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
360 assigned_value.default = default 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
361 assigned_value._attributes_set['default'] = default 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
363 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
364 # Store the original annotation and assignment value that should be used to rebuild the field info later.
365 # Note that the assignment is always stored as the annotation might contain a type var that is later
366 # parameterized with an unknown forward reference (and we'll need it to rebuild the field info):
367 field_info._original_assignment = assigned_value 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
368 if not evaluated: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
369 field_info._complete = False 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
370 field_info._original_annotation = ann_type 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
371 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
372 warnings.warn( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
373 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
374 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
375 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
376 category=PydanticDeprecatedSince211,
377 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
378 stacklevel=4,
379 )
380 class_vars.add(ann_name) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
381 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
383 # attributes which are fields are removed from the class namespace:
384 # 1. To match the behaviour of annotation-only fields
385 # 2. To avoid false positives in the NameError check above
386 try: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
387 delattr(cls, ann_name) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
388 except AttributeError: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
389 pass # indicates the attribute was on a parent class 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
391 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
392 # to make sure the decorators have already been built for this exact class
393 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
394 if ann_name in decorators.computed_fields: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
395 raise TypeError( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
396 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
397 'This override with a computed_field is incompatible.'
398 )
399 fields[ann_name] = field_info 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
401 if field_info._complete: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
402 # If not complete, this will be called in `rebuild_model_fields()`:
403 update_field_from_config(config_wrapper, ann_name, field_info) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
405 if typevars_map: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
406 for field in fields.values(): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
407 if field._complete: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
408 field.apply_typevars_map(typevars_map) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
410 if config_wrapper.use_attribute_docstrings: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
411 _update_fields_from_docstrings(cls, fields) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
412 return fields, class_vars 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
415def rebuild_model_fields( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
416 cls: type[BaseModel],
417 *,
418 config_wrapper: ConfigWrapper,
419 ns_resolver: NsResolver,
420 typevars_map: Mapping[TypeVar, Any],
421) -> dict[str, FieldInfo]:
422 """Rebuild the (already present) model fields by trying to reevaluate annotations.
424 This function should be called whenever a model with incomplete fields is encountered.
426 Raises:
427 NameError: If one of the annotations failed to evaluate.
429 Note:
430 This function *doesn't* mutate the model fields in place, as it can be called during
431 schema generation, where you don't want to mutate other model's fields.
432 """
433 FieldInfo_ = import_cached_field_info() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
435 rebuilt_fields: dict[str, FieldInfo] = {} 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
436 with ns_resolver.push(cls): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
437 for f_name, field_info in cls.__pydantic_fields__.items(): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
438 if field_info._complete: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
439 rebuilt_fields[f_name] = field_info 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
440 else:
441 existing_desc = field_info.description 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
442 ann = _typing_extra.eval_type( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
443 field_info._original_annotation,
444 *ns_resolver.types_namespace,
445 )
446 ann = _generics.replace_types(ann, typevars_map) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
448 if (assign := field_info._original_assignment) is PydanticUndefined: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
449 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
450 else:
451 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
452 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
453 new_field.description = new_field.description if new_field.description is not None else existing_desc 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
454 update_field_from_config(config_wrapper, f_name, new_field) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
455 rebuilt_fields[f_name] = new_field 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
457 return rebuilt_fields 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
460def collect_dataclass_fields( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
461 cls: type[StandardDataclass],
462 *,
463 config_wrapper: ConfigWrapper,
464 ns_resolver: NsResolver | None = None,
465 typevars_map: dict[Any, Any] | None = None,
466) -> dict[str, FieldInfo]:
467 """Collect the fields of a dataclass.
469 Args:
470 cls: dataclass.
471 config_wrapper: The config wrapper instance.
472 ns_resolver: Namespace resolver to use when getting dataclass annotations.
473 Defaults to an empty instance.
474 typevars_map: A dictionary mapping type variables to their concrete types.
476 Returns:
477 The dataclass fields.
478 """
479 FieldInfo_ = import_cached_field_info() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
481 fields: dict[str, FieldInfo] = {} 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
482 ns_resolver = ns_resolver or NsResolver() 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
483 dataclass_fields = cls.__dataclass_fields__ 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
485 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
486 # although we do it manually as stdlib dataclasses already have annotations
487 # collected in each class:
488 for base in reversed(cls.__mro__): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
489 if not dataclasses.is_dataclass(base): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
490 continue 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
492 with ns_resolver.push(base): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
493 for ann_name, dataclass_field in dataclass_fields.items(): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
494 base_anns = _typing_extra.safe_get_annotations(base) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
496 if ann_name not in base_anns: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
497 # `__dataclass_fields__`contains every field, even the ones from base classes.
498 # Only collect the ones defined on `base`.
499 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
501 globalns, localns = ns_resolver.types_namespace 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
502 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
504 if _typing_extra.is_classvar_annotation(ann_type): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
505 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
507 if ( 1ambncoOCpqrstudvewfx
508 not dataclass_field.init
509 and dataclass_field.default is dataclasses.MISSING
510 and dataclass_field.default_factory is dataclasses.MISSING
511 ):
512 # TODO: We should probably do something with this so that validate_assignment behaves properly
513 # Issue: https://github.com/pydantic/pydantic/issues/5470
514 continue 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
516 if isinstance(dataclass_field.default, FieldInfo_): 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
517 if dataclass_field.default.init_var: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
518 if dataclass_field.default.init is False: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
519 raise PydanticUserError( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
520 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
521 code='clashing-init-and-init-var',
522 )
524 # TODO: same note as above re validate_assignment
525 continue 1bncogyhziABCrstuDEFGHIJewfxjKkLlMN
526 field_info = FieldInfo_.from_annotated_attribute( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
527 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
528 )
529 field_info._original_assignment = dataclass_field.default 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
530 else:
531 field_info = FieldInfo_.from_annotated_attribute( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
532 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
533 )
534 field_info._original_assignment = dataclass_field 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
536 if not evaluated: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
537 field_info._complete = False 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
538 field_info._original_annotation = ann_type 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
540 fields[ann_name] = field_info 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
541 update_field_from_config(config_wrapper, ann_name, field_info) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
543 if field_info.default is not PydanticUndefined and isinstance( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
544 getattr(cls, ann_name, field_info), FieldInfo_
545 ):
546 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
547 setattr(cls, ann_name, field_info.default) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
549 if typevars_map: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
550 for field in fields.values(): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
551 # We don't pass any ns, as `field.annotation`
552 # was already evaluated. TODO: is this method relevant?
553 # Can't we juste use `_generics.replace_types`?
554 field.apply_typevars_map(typevars_map) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
556 if config_wrapper.use_attribute_docstrings: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
557 _update_fields_from_docstrings( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
558 cls,
559 fields,
560 # We can't rely on the (more reliable) frame inspection method
561 # for stdlib dataclasses:
562 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
563 )
565 return fields 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
568def rebuild_dataclass_fields( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
569 cls: type[PydanticDataclass],
570 *,
571 config_wrapper: ConfigWrapper,
572 ns_resolver: NsResolver,
573 typevars_map: Mapping[TypeVar, Any],
574) -> dict[str, FieldInfo]:
575 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations.
577 This function should be called whenever a dataclass with incomplete fields is encountered.
579 Raises:
580 NameError: If one of the annotations failed to evaluate.
582 Note:
583 This function *doesn't* mutate the dataclass fields in place, as it can be called during
584 schema generation, where you don't want to mutate other dataclass's fields.
585 """
586 FieldInfo_ = import_cached_field_info() 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
588 rebuilt_fields: dict[str, FieldInfo] = {} 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
589 with ns_resolver.push(cls): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
590 for f_name, field_info in cls.__pydantic_fields__.items(): 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
591 if field_info._complete: 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
592 rebuilt_fields[f_name] = field_info 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
593 else:
594 existing_desc = field_info.description 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
595 ann = _typing_extra.eval_type( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
596 field_info._original_annotation,
597 *ns_resolver.types_namespace,
598 )
599 ann = _generics.replace_types(ann, typevars_map) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
600 new_field = FieldInfo_.from_annotated_attribute( 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
601 ann,
602 field_info._original_assignment,
603 _source=AnnotationSource.DATACLASS,
604 )
606 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
607 new_field.description = new_field.description if new_field.description is not None else existing_desc 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
608 update_field_from_config(config_wrapper, f_name, new_field) 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
609 rebuilt_fields[f_name] = new_field 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
611 return rebuilt_fields 1ambncogyhziABOCpqrstuDEFGHIJdvewfxjKkLlMN
614def is_valid_field_name(name: str) -> bool: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
615 return not name.startswith('_') 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
618def is_valid_privateattr_name(name: str) -> bool: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
619 return name.startswith('_') and not name.startswith('__') 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
622def takes_validated_data_argument( 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
623 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
624) -> TypeIs[Callable[[dict[str, Any]], Any]]:
625 """Whether the provided default factory callable has a validated data parameter."""
626 try: 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
627 sig = signature(default_factory) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
628 except (ValueError, TypeError): 1ambncogyhziABpqrstuDEFGHIJdvewfxjKkLlMN
629 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
630 # In this case, we assume no data argument is present:
631 return False 1ambncogyhziABpqrstuDEFGHIJdvewfxjKkLlMN
633 parameters = list(sig.parameters.values()) 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN
635 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1ambncogyhziABOCpqrstuDEFGHIJPdvewfxjKkLlMN