Coverage for pydantic/_internal/_fields.py: 98.73%
253 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-20 16:49 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-20 16:49 +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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
16from typing_inspection.introspection import AnnotationSource 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
18from pydantic import PydanticDeprecatedSince211 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
19from pydantic.errors import PydanticUserError 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
21from ..aliases import AliasGenerator 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
22from . import _generics, _typing_extra 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
23from ._config import ConfigWrapper 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
24from ._docs_extraction import extract_docstrings_from_cls 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
25from ._import_utils import import_cached_base_model, import_cached_field_info 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
26from ._namespace_utils import NsResolver 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
27from ._repr import Representation 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
28from ._utils import can_be_positional, get_first_not_none 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
30if TYPE_CHECKING: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
40 """Base class for annotation markers like `Strict`."""
42 __slots__ = () 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
45def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
57@cache 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
58def _general_metadata_cls() -> type[BaseMetadata]: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
59 """Do it this way to avoid importing `annotated_types` at import time."""
60 from annotated_types import BaseMetadata 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
63 """Pydantic general metadata like `max_digits`."""
65 def __init__(self, metadata: Any): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
66 self.__dict__ = metadata 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
68 return _PydanticGeneralMetadata # type: ignore 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
71def _check_protected_namespaces( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
79 for protected_namespace in protected_namespaces: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
80 ns_violation = False 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
81 if isinstance(protected_namespace, Pattern): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
82 ns_violation = protected_namespace.match(ann_name) is not None 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
83 elif isinstance(protected_namespace, str): 83 ↛ 86line 83 didn't jump to line 86 because the condition on line 83 was always true1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
84 ns_violation = ann_name.startswith(protected_namespace) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
86 if ns_violation: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
87 for b in bases: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
88 if hasattr(b, ann_name): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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] = [] 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
96 for pn in protected_namespaces: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
97 if isinstance(pn, Pattern): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
98 if not pn.match(ann_name): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
99 valid_namespaces.append(f're.compile({pn.pattern!r})') 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
100 else:
101 if not ann_name.startswith(pn): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
102 valid_namespaces.append(f"'{pn}'") 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
104 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
106 warnings.warn( 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
115 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
116 for ann_name, field_info in fields.items(): 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
117 if field_info.description is None and ann_name in fields_docs: 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
118 field_info.description = fields_docs[ann_name] 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
121def _apply_field_title_generator_to_field_info( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
122 title_generator: Callable[[str, FieldInfo], str],
123 field_name: str,
124 field_info: FieldInfo,
125):
126 if field_info.title is None: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
127 title = title_generator(field_name, field_info) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
128 if not isinstance(title, str): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
129 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}') 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
131 field_info.title = title 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
134def _apply_alias_generator_to_field_info( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 ( 1amLBpqdv
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 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
156 if isinstance(alias_generator, AliasGenerator): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
157 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
158 elif callable(alias_generator): 158 ↛ 166line 158 didn't jump to line 166 because the condition on line 158 was always true1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
159 alias = alias_generator(field_name) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
160 if not isinstance(alias, str): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
161 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}') 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
167 field_info.alias_priority = 1 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
169 # if the priority is 1, then we set the aliases to the generated alias
170 if field_info.alias_priority == 1: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
171 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
172 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
173 field_info.alias = alias 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
177 field_info.alias = alias 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
178 if field_info.serialization_alias is None: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
179 field_info.serialization_alias = get_first_not_none(serialization_alias, alias) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
180 if field_info.validation_alias is None: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
181 field_info.validation_alias = get_first_not_none(validation_alias, alias) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
184def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
195 if field_title_generator is not None: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
196 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
197 if config_wrapper.alias_generator is not None: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
198 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
201_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
203_deprecated_classmethod_names = { 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
249 BaseModel_ = import_cached_base_model() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
251 bases = cls.__bases__ 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
252 parent_fields_lookup: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
253 for base in reversed(bases): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
254 if model_fields := getattr(base, '__pydantic_fields__', None): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
255 parent_fields_lookup.update(model_fields) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
257 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
263 fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
265 class_vars: set[str] = set() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
266 for ann_name, (ann_type, evaluated) in type_hints.items(): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
267 if ann_name == 'model_config': 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
273 _check_protected_namespaces( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
281 class_vars.add(ann_name) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
282 continue 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
284 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
285 if assigned_value is not PydanticUndefined and ( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
300 if not is_valid_field_name(ann_name): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
301 continue 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
302 if cls.__pydantic_root_model__ and ann_name != 'root': 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
303 raise NameError( 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
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') 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
310 for base in bases: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
311 dataclass_fields = { 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
312 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
313 }
314 if hasattr(base, ann_name): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
315 if base is generic_origin: 315 ↛ 317line 315 didn't jump to line 317 because the condition on line 315 was never true1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
316 # Don't warn when "shadowing" of attributes in parametrized generics
317 continue
319 if ann_name in dataclass_fields: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
320 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
321 # on the class instance.
322 continue 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
324 if ann_name not in annotations: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
325 # Don't warn when a field exists in a parent class but has not been defined in the current class
326 continue 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
328 warnings.warn( 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
335 if ann_name in annotations or ann_name not in parent_fields_lookup: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
342 if not evaluated: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
343 field_info._complete = False 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
344 # Store the original annotation that should be used to rebuild
345 # the field info later:
346 field_info._original_annotation = ann_type 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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]) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
360 assigned_value.default = default 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
361 assigned_value._attributes_set['default'] = default 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
363 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
368 if not evaluated: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
369 field_info._complete = False 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
370 field_info._original_annotation = ann_type 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
371 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
372 warnings.warn( 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
381 continue 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
387 delattr(cls, ann_name) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
388 except AttributeError: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
389 pass # indicates the attribute was on a parent class 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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__'] 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
394 if ann_name in decorators.computed_fields: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
395 raise TypeError( 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
401 if field_info._complete: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
402 # If not complete, this will be called in `rebuild_model_fields()`:
403 update_field_from_config(config_wrapper, ann_name, field_info) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
405 if typevars_map: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
406 for field in fields.values(): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
407 if field._complete: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
408 field.apply_typevars_map(typevars_map) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
410 if config_wrapper.use_attribute_docstrings: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
411 _update_fields_from_docstrings(cls, fields) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
412 return fields, class_vars 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
415def rebuild_model_fields( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
435 rebuilt_fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
436 with ns_resolver.push(cls): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
437 for f_name, field_info in cls.__pydantic_fields__.items(): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
438 if field_info._complete: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
439 rebuilt_fields[f_name] = field_info 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
440 else:
441 existing_desc = field_info.description 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
442 ann = _typing_extra.eval_type( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
443 field_info._original_annotation,
444 *ns_resolver.types_namespace,
445 )
446 ann = _generics.replace_types(ann, typevars_map) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
448 if (assign := field_info._original_assignment) is PydanticUndefined: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
449 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
450 else:
451 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
454 update_field_from_config(config_wrapper, f_name, new_field) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
455 rebuilt_fields[f_name] = new_field 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
457 return rebuilt_fields 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
460def collect_dataclass_fields( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
481 fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
482 ns_resolver = ns_resolver or NsResolver() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
483 dataclass_fields = cls.__dataclass_fields__ 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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__): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
489 if not dataclasses.is_dataclass(base): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
490 continue 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
492 with ns_resolver.push(base): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
493 for ann_name, dataclass_field in dataclass_fields.items(): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
494 base_anns = _typing_extra.safe_get_annotations(base) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
496 if ann_name not in base_anns: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
497 # `__dataclass_fields__`contains every field, even the ones from base classes.
498 # Only collect the ones defined on `base`.
499 continue 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
501 globalns, localns = ns_resolver.types_namespace 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
502 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
504 if _typing_extra.is_classvar_annotation(ann_type): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
505 continue 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
507 if ( 1ambncoLBpqrstudvewfx
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 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
516 if isinstance(dataclass_field.default, FieldInfo_): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
517 if dataclass_field.default.init_var: 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
518 if dataclass_field.default.init is False: 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
519 raise PydanticUserError( 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
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 1bncogyhziABrstuCDEFGHewfxjIkJlK
526 field_info = FieldInfo_.from_annotated_attribute( 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
527 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
528 )
529 field_info._original_assignment = dataclass_field.default 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
530 else:
531 field_info = FieldInfo_.from_annotated_attribute( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
532 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
533 )
534 field_info._original_assignment = dataclass_field 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
536 if not evaluated: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
537 field_info._complete = False 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
538 field_info._original_annotation = ann_type 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
540 fields[ann_name] = field_info 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
541 update_field_from_config(config_wrapper, ann_name, field_info) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
543 if field_info.default is not PydanticUndefined and isinstance( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
549 if typevars_map: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
550 for field in fields.values(): 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
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) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
556 if config_wrapper.use_attribute_docstrings: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
557 _update_fields_from_docstrings( 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
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 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
568def rebuild_dataclass_fields( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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() 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
588 rebuilt_fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
589 with ns_resolver.push(cls): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
590 for f_name, field_info in cls.__pydantic_fields__.items(): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
591 if field_info._complete: 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
592 rebuilt_fields[f_name] = field_info 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK
593 else:
594 existing_desc = field_info.description 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
595 ann = _typing_extra.eval_type( 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
596 field_info._original_annotation,
597 *ns_resolver.types_namespace,
598 )
599 ann = _generics.replace_types(ann, typevars_map) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
600 new_field = FieldInfo_.from_annotated_attribute( 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
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 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
608 update_field_from_config(config_wrapper, f_name, new_field) 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
609 rebuilt_fields[f_name] = new_field 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
611 return rebuilt_fields 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO
614def is_valid_field_name(name: str) -> bool: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
615 return not name.startswith('_') 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
618def is_valid_privateattr_name(name: str) -> bool: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
619 return name.startswith('_') and not name.startswith('__') 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
622def takes_validated_data_argument( 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
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: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
627 sig = signature(default_factory) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
628 except (ValueError, TypeError): 1ambncogyhziAMpqrstuCDEFGHNdvewfxjIkJlKO
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 1ambncogyhziAMpqrstuCDEFGHNdvewfxjIkJlKO
633 parameters = list(sig.parameters.values()) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO
635 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO