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