1"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
2
3from __future__ import annotations as _annotations
4
5import dataclasses
6import warnings
7from collections.abc import Callable, Mapping
8from functools import cache
9from inspect import Parameter, ismethoddescriptor
10from re import Pattern
11from typing import TYPE_CHECKING, Any, TypeVar, cast
12
13from pydantic_core import PydanticUndefined
14from typing_extensions import TypeForm, TypeIs, get_args, get_origin # noqa: UP035 (for `get_args` and `get_origin`)
15from typing_inspection import typing_objects
16from typing_inspection.introspection import AnnotationSource
17
18from pydantic import PydanticDeprecatedSince211
19from pydantic.errors import PydanticUserError
20
21from ..aliases import AliasGenerator
22from . import _generics, _typing_extra
23from ._config import ConfigWrapper
24from ._docs_extraction import extract_docstrings_from_cls
25from ._import_utils import import_cached_base_model, import_cached_field_info
26from ._namespace_utils import NsResolver
27from ._repr import Representation
28from ._utils import can_be_positional, get_first_not_none, smart_deepcopy
29
30if TYPE_CHECKING:
31 from annotated_types import BaseMetadata
32
33 from ..fields import FieldInfo, ModelPrivateAttr
34 from ..main import BaseModel
35 from ._dataclasses import PydanticDataclass, StandardDataclass
36 from ._decorators import DecoratorInfos
37
38
39class PydanticMetadata(Representation):
40 """Base class for annotation markers like `Strict`."""
41
42 __slots__ = ()
43
44
45@dataclasses.dataclass(slots=True, kw_only=True)
46class PydanticExtraInfo:
47 annotation: TypeForm[Any]
48 complete: bool
49
50
51@dataclasses.dataclass(slots=True, kw_only=True)
52class ModelNamespaceInfo:
53 """Information gathered by inspecting the namespace of a model class before it is created.
54
55 Decisions requiring the (possibly unevaluatable at class creation time) annotations (such as
56 whether a sunder-named attribute is a private attribute or a class variable) are deferred to
57 `collect_model_fields()`, where evaluated annotations are available.
58 """
59
60 private_attributes: dict[str, ModelPrivateAttr]
61 """Private attributes explicitly assigned a `ModelPrivateAttr` instance (removed from the namespace)."""
62
63 ignored_names: set[str]
64 """Names of the namespace attributes with a value of an ignored type."""
65
66 ignored_types: tuple[type[Any], ...]
67 """The ignored types (the `ignored_types` config value, together with the implicitly ignored types)."""
68
69 private_candidates: list[str]
70 """Sunder-named attributes assigned in the namespace, to be converted to private attributes
71 unless annotated as class variables."""
72
73 field_candidates: list[str]
74 """Namespace attributes with a valid field name, required to have a type annotation."""
75
76 base_field_names: set[str]
77 """The field names of the model's bases."""
78
79 model_config_assigned: bool
80 """Whether `model_config` was assigned a value in the namespace."""
81
82
83def pydantic_general_metadata(**metadata: Any) -> BaseMetadata:
84 """Create a new `_PydanticGeneralMetadata` class with the given metadata.
85
86 Args:
87 **metadata: The metadata to add.
88
89 Returns:
90 The new `_PydanticGeneralMetadata` class.
91 """
92 return _general_metadata_cls()(metadata) # type: ignore
93
94
95@cache
96def _general_metadata_cls() -> type[BaseMetadata]:
97 """Do it this way to avoid importing `annotated_types` at import time."""
98 from annotated_types import BaseMetadata
99
100 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata):
101 """Pydantic general metadata like `max_digits`."""
102
103 def __init__(self, metadata: Any):
104 self.__dict__ = metadata
105
106 return _PydanticGeneralMetadata # type: ignore
107
108
109def _check_protected_namespaces(
110 protected_namespaces: tuple[str | Pattern[str], ...],
111 ann_name: str,
112 bases: tuple[type[Any], ...],
113 cls_name: str,
114) -> None:
115 BaseModel = import_cached_base_model()
116
117 for protected_namespace in protected_namespaces:
118 ns_violation = False
119 if isinstance(protected_namespace, Pattern):
120 ns_violation = protected_namespace.match(ann_name) is not None
121 elif isinstance(protected_namespace, str):
122 ns_violation = ann_name.startswith(protected_namespace)
123
124 if ns_violation:
125 for b in bases:
126 if hasattr(b, ann_name):
127 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})):
128 raise ValueError(
129 f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
130 f' of protected namespace {protected_namespace!r}.'
131 )
132 else:
133 valid_namespaces: list[str] = []
134 for pn in protected_namespaces:
135 if isinstance(pn, Pattern):
136 if not pn.match(ann_name):
137 valid_namespaces.append(f're.compile({pn.pattern!r})')
138 else:
139 if not ann_name.startswith(pn):
140 valid_namespaces.append(f"'{pn}'")
141
142 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}'
143
144 warnings.warn(
145 f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
146 f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
147 UserWarning,
148 stacklevel=5,
149 )
150
151
152def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None:
153 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect)
154 for ann_name, field_info in fields.items():
155 if field_info.description is None and ann_name in fields_docs:
156 field_info.description = fields_docs[ann_name]
157
158
159def _apply_field_title_generator_to_field_info(
160 title_generator: Callable[[str, FieldInfo], str],
161 field_name: str,
162 field_info: FieldInfo,
163):
164 if field_info.title is None:
165 title = title_generator(field_name, field_info)
166 if not isinstance(title, str):
167 raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}')
168
169 field_info.title = title
170
171
172def _apply_alias_generator_to_field_info(
173 alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
174):
175 """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
176
177 Args:
178 alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
179 field_name: The name of the field from which to generate the alias.
180 field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
181 """
182 # Apply an alias_generator if
183 # 1. An alias is not specified
184 # 2. An alias is specified, but the priority is <= 1
185 if (
186 field_info.alias_priority is None
187 or field_info.alias_priority <= 1
188 or field_info.alias is None
189 or field_info.validation_alias is None
190 or field_info.serialization_alias is None
191 ):
192 alias, validation_alias, serialization_alias = None, None, None
193
194 if isinstance(alias_generator, AliasGenerator):
195 alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name)
196 elif callable(alias_generator):
197 alias = alias_generator(field_name)
198 if not isinstance(alias, str):
199 raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}')
200
201 # if priority is not set, we set to 1
202 # which supports the case where the alias_generator from a child class is used
203 # to generate an alias for a field in a parent class
204 if field_info.alias_priority is None or field_info.alias_priority <= 1:
205 field_info.alias_priority = 1
206
207 # if the priority is 1, then we set the aliases to the generated alias
208 if field_info.alias_priority == 1:
209 field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
210 field_info.validation_alias = get_first_not_none(validation_alias, alias)
211 field_info.alias = alias
212
213 # if any of the aliases are not set, then we set them to the corresponding generated alias
214 if field_info.alias is None:
215 field_info.alias = alias
216 if field_info.serialization_alias is None:
217 field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
218 if field_info.validation_alias is None:
219 field_info.validation_alias = get_first_not_none(validation_alias, alias)
220
221
222def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None:
223 """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
224
225 This will apply the title and alias generators from the configuration.
226
227 Args:
228 config_wrapper: The configuration from the model.
229 field_name: The field name the `FieldInfo` instance is attached to.
230 field_info: The `FieldInfo` instance to update.
231 """
232 field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator
233 if field_title_generator is not None:
234 _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info)
235 if config_wrapper.alias_generator is not None:
236 _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info)
237
238
239_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'}
240
241_deprecated_classmethod_names = {
242 'parse_obj',
243 'parse_raw',
244 'parse_file',
245 'from_orm',
246 'construct',
247 'schema',
248 'schema_json',
249 'validate',
250 'update_forward_refs',
251 '_get_value',
252}
253
254
255@cache
256def _deprecated_base_model_method_ids() -> tuple[frozenset[int], frozenset[int]]:
257 """IDs of the deprecated `BaseModel` methods (and the underlying functions of the deprecated classmethods).
258
259 These are used to check if an assigned field value is one of the deprecated methods by identity.
260 The `BaseModel` class (and thus the methods) stays alive for the lifetime of the process, so the
261 IDs are guaranteed to be stable.
262 """
263 BaseModel_ = import_cached_base_model()
264 return (
265 frozenset(
266 id(method) for name in _deprecated_method_names if (method := getattr(BaseModel_, name, None)) is not None
267 ),
268 frozenset(
269 id(func)
270 for name in _deprecated_classmethod_names
271 if (func := getattr(getattr(BaseModel_, name, None), '__func__', None)) is not None
272 ),
273 )
274
275
276def collect_model_fields( # noqa: C901
277 cls: type[BaseModel],
278 config_wrapper: ConfigWrapper,
279 ns_resolver: NsResolver,
280 *,
281 typevars_map: Mapping[TypeVar, Any] | None = None,
282 namespace_info: ModelNamespaceInfo | None = None,
283) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None, set[str], dict[str, ModelPrivateAttr]]:
284 """Collect the fields, class variables names and private attributes of a nascent Pydantic model.
285
286 The fields collection process is *lenient*, meaning it won't error if string annotations
287 fail to evaluate. If this happens, the original annotation (and assigned value, if any)
288 is stored on the created `FieldInfo` instance.
289
290 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
291 and will make use of these stored attributes.
292
293 Args:
294 cls: BaseModel or dataclass.
295 config_wrapper: The config wrapper instance.
296 ns_resolver: Namespace resolver to use when getting model annotations.
297 typevars_map: A dictionary mapping type variables to their concrete types.
298 namespace_info: The result of inspecting the class namespace before the class was created.
299
300 Returns:
301 A four-tuple containing the model fields, the `PydanticExtraInfo` instance if the `__pydantic_extra__` annotation is set,
302 the class variables names, and the private attributes discovered from the namespace and annotations.
303
304 Raises:
305 NameError: If there is a conflict between a field name and protected namespaces.
306 PydanticUserError: If there is a field other than `root` in `RootModel`, or if one of
307 the namespace checks performed with `namespace_info` fails.
308 """
309 FieldInfo_ = import_cached_field_info()
310
311 from ..fields import ModelPrivateAttr, PrivateAttr
312
313 bases = cls.__bases__
314 parent_fields_lookup: dict[str, FieldInfo] = {}
315 for base in reversed(bases):
316 if model_fields := getattr(base, '__pydantic_fields__', None):
317 parent_fields_lookup.update(model_fields)
318
319 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver)
320
321 # `cls_annotations` is only used to determine if an annotation comes from a parent class
322 cls_annotations = _typing_extra.safe_get_annotations(cls)
323
324 private_attributes: dict[str, ModelPrivateAttr] = {}
325
326 if namespace_info is not None:
327 if '__root__' in cls_annotations:
328 # This check also exists in `inspect_namespace()`, but for assignments only:
329 raise PydanticUserError(
330 "To define root models, use `pydantic.RootModel` rather than a field called '__root__'", code=None
331 )
332
333 if 'model_config' in cls_annotations and not namespace_info.model_config_assigned:
334 raise PydanticUserError(
335 '`model_config` cannot be used as a model field name. Use `model_config` for model configuration.',
336 code='model-config-invalid-field-name',
337 )
338
339 for var_name in namespace_info.private_candidates:
340 # A sunder-named attribute with an assigned value is a private attribute,
341 # unless annotated as a class variable:
342 hint = type_hints.get(var_name)
343 if hint is None or not _typing_extra.is_classvar_annotation(hint[0]):
344 # Unlike for the annotation-only private attributes below, `__set_name__()` must not be
345 # called on the wrapping `PrivateAttr()` here: the assigned value stayed in the namespace
346 # during class creation, so `type.__new__()` already invoked the protocol on it natively:
347 private_attributes[var_name] = cast('ModelPrivateAttr', PrivateAttr(default=cls.__dict__[var_name]))
348 delattr(cls, var_name)
349
350 for var_name in namespace_info.field_candidates:
351 if var_name in cls_annotations:
352 continue
353 if var_name in namespace_info.base_field_names:
354 raise PydanticUserError(
355 f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. '
356 f'All field definitions, including overrides, require a type annotation.',
357 code='model-field-overridden',
358 )
359 value = cls.__dict__[var_name]
360 if isinstance(value, FieldInfo_):
361 raise PydanticUserError(
362 f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation'
363 )
364 else:
365 raise PydanticUserError(
366 f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a '
367 f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this '
368 f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.",
369 code='model-field-missing-annotation',
370 )
371
372 fields: dict[str, FieldInfo] = {}
373
374 deprecated_method_ids, deprecated_classmethod_func_ids = _deprecated_base_model_method_ids()
375
376 class_vars: set[str] = set()
377 for ann_name, (ann_type, evaluated) in type_hints.items():
378 if ann_name == 'model_config':
379 # We never want to treat `model_config` as a field
380 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
381 # protected namespaces (where `model_config` might be allowed as a field name)
382 continue
383
384 _check_protected_namespaces(
385 protected_namespaces=config_wrapper.protected_namespaces,
386 ann_name=ann_name,
387 bases=bases,
388 cls_name=cls.__name__,
389 )
390
391 if _typing_extra.is_classvar_annotation(ann_type):
392 class_vars.add(ann_name)
393 continue
394
395 if not is_valid_field_name(ann_name):
396 if (
397 namespace_info is not None
398 and is_valid_privateattr_name(ann_name)
399 and ann_name in cls_annotations
400 and ann_name not in private_attributes
401 and ann_name not in namespace_info.private_attributes
402 and ann_name not in namespace_info.ignored_names
403 and ann_type not in namespace_info.ignored_types
404 and getattr(ann_type, '__module__', None) != 'functools'
405 ):
406 # A sunder-named annotation (with no assigned value, as this case is handled above)
407 # defines a private attribute:
408 private_attr: ModelPrivateAttr | None = None
409 if typing_objects.is_annotated(get_origin(ann_type)):
410 _, *metadata = get_args(ann_type)
411 private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None)
412 if private_attr is None:
413 private_attr = cast('ModelPrivateAttr', PrivateAttr())
414 private_attr.__set_name__(cls, ann_name)
415 private_attributes[ann_name] = private_attr
416 continue
417
418 assigned_value = getattr(cls, ann_name, PydanticUndefined)
419 if assigned_value is not PydanticUndefined and (
420 # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
421 id(assigned_value) in deprecated_method_ids
422 # One of the deprecated class methods was used as a field name (e.g. `schema()`):
423 or (
424 (assigned_func := getattr(assigned_value, '__func__', None)) is not None
425 and id(assigned_func) in deprecated_classmethod_func_ids
426 )
427 ):
428 # Then `assigned_value` would be the method, even though no default was specified:
429 assigned_value = PydanticUndefined
430
431 if cls.__pydantic_root_model__ and ann_name != 'root':
432 raise PydanticUserError(
433 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`",
434 code=None,
435 )
436
437 for base in bases:
438 if hasattr(base, ann_name):
439 if ann_name not in cls_annotations:
440 # Don't warn when a field exists in a parent class but has not been defined in the current class
441 continue
442
443 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
444 # "... shadows an attribute" warnings
445 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
446 if base is generic_origin:
447 # Don't warn when "shadowing" of attributes in parametrized generics
448 continue
449
450 dataclass_fields = {
451 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
452 }
453 if ann_name in dataclass_fields:
454 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
455 # on the class instance.
456 continue
457
458 warnings.warn(
459 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
460 f'"{base.__qualname__}"',
461 UserWarning,
462 stacklevel=4,
463 )
464
465 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation
466 if ann_name in cls_annotations or ann_name not in parent_fields_lookup:
467 # field is either:
468 # - present in the current model's annotations (and *not* from parent classes)
469 # - not found on any base classes; this seems to be caused by fields not getting
470 # generated due to models not being fully defined while initializing recursive models.
471 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
472 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS)
473 field_info._original_annotation = ann_type
474 if not evaluated:
475 field_info._complete = False
476 # Store the original annotation that should be used to rebuild
477 # the field info later:
478 else:
479 # The field was present on one of the (possibly multiple) base classes, we make a copy directly from it.
480 parent_field_info = parent_fields_lookup[ann_name]._copy()
481
482 # The only case where substituting the type variables is relevant (i.e. when `typevars_map` is not empty)
483 # is when a generic class is parameterized (e.g. `MyGenericModel[int, str]`), which creates a new class object
484 # (unlike the stdlib genercis that create a generic alias). In this case, we are guaranteed to only have to copy
485 # from the origin/parent model (e.g. `MyGenericModel`).
486 if typevars_map:
487 field_info = _recreate_field_info(
488 parent_field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=True
489 )
490 else:
491 field_info = parent_field_info
492
493 else: # An assigned value is present (either the default value, or a `Field()` function)
494 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default):
495 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
496 # for descriptors, so we do the same if the `= field(default=...)` form is used.
497 # Note that we only do this for method descriptors for now, we might want to
498 # extend this to any descriptor in the future (by simply checking for
499 # `hasattr(assigned_value.default, '__get__')`).
500 default = assigned_value.default.__get__(None, cls)
501 assigned_value.default = default
502 assigned_value._attributes_set['default'] = default
503
504 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS)
505
506 # Store the original annotation and assignment value that could be used to rebuild the field info later.
507 field_info._original_assignment = assigned_value
508 field_info._original_annotation = ann_type
509 if not evaluated:
510 field_info._complete = False
511 elif 'final' in field_info._qualifiers and not field_info.is_required():
512 warnings.warn(
513 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
514 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
515 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`',
516 category=PydanticDeprecatedSince211,
517 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
518 stacklevel=4,
519 )
520 class_vars.add(ann_name)
521 continue
522
523 # attributes which are fields are removed from the class namespace:
524 # 1. To match the behaviour of annotation-only fields
525 # 2. To avoid false positives in the NameError check above
526 try:
527 delattr(cls, ann_name)
528 except AttributeError:
529 pass # indicates the attribute was on a parent class
530
531 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
532 # to make sure the decorators have already been built for this exact class
533 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__']
534 if ann_name in decorators.computed_fields:
535 raise TypeError(
536 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
537 'This override with a computed_field is incompatible.'
538 )
539 fields[ann_name] = field_info
540
541 if field_info._complete:
542 # If not complete, this will be called in `rebuild_model_fields()`:
543 update_field_from_config(config_wrapper, ann_name, field_info)
544
545 if config_wrapper.use_attribute_docstrings:
546 _update_fields_from_docstrings(cls, fields)
547
548 pydantic_extra_info: PydanticExtraInfo | None = None
549 if '__pydantic_extra__' in type_hints:
550 parent_extra_info: PydanticExtraInfo | None = None
551 if '__pydantic_extra__' not in cls_annotations:
552 # The annotation is only present on parent classes. As with fields, we make use of the parent's
553 # extra info, where type variables are already substituted:
554 for base in bases:
555 parent_extra_info = getattr(base, '__pydantic_extra_info__', None)
556 if parent_extra_info is not None:
557 break
558
559 if parent_extra_info is not None:
560 # As with fields, the only case where substituting the type variables is relevant (i.e. when
561 # `typevars_map` is not empty) is when a generic class is parameterized:
562 if typevars_map and parent_extra_info.complete:
563 # If not complete, `rebuild_model_fields()` takes care of it:
564 pydantic_extra_info = PydanticExtraInfo(
565 annotation=_generics.replace_types(parent_extra_info.annotation, typevars_map),
566 complete=True,
567 )
568 else:
569 pydantic_extra_info = parent_extra_info
570 else:
571 ann, complete = type_hints['__pydantic_extra__']
572 if complete:
573 # If not complete, `rebuild_model_fields()` takes care of it:
574 ann = _generics.replace_types(ann, typevars_map)
575 pydantic_extra_info = PydanticExtraInfo(
576 annotation=ann,
577 complete=complete,
578 )
579
580 return fields, pydantic_extra_info, class_vars, private_attributes
581
582
583def rebuild_model_fields(
584 cls: type[BaseModel],
585 *,
586 config_wrapper: ConfigWrapper,
587 ns_resolver: NsResolver,
588 typevars_map: Mapping[TypeVar, Any],
589) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None]:
590 """Rebuild the (already present) model fields by trying to reevaluate annotations.
591
592 This function should be called whenever a model with incomplete fields is encountered.
593
594 Returns:
595 A two-tuple, the first element being the rebuilt fields, the second element being
596 the rebuild `PydanticExtraInfo` instance, if available.
597
598 Raises:
599 NameError: If one of the annotations failed to evaluate.
600
601 Note:
602 This function *doesn't* mutate the model fields in place, as it can be called during
603 schema generation, where you don't want to mutate other model's fields.
604 """
605 rebuilt_fields: dict[str, FieldInfo] = {}
606 origin = cls.__pydantic_generic_metadata__['origin']
607 with ns_resolver.push(cls):
608 for f_name, field_info in cls.__pydantic_fields__.items():
609 if field_info._complete:
610 rebuilt_fields[f_name] = field_info
611 else:
612 annotation = field_info._original_annotation
613 if origin is not None and (origin_field := origin.__pydantic_fields__.get(f_name)) is not None:
614 # For parametrized generic models, the stored annotation can mix names from the origin class
615 # and (forward) type arguments, which need to be evaluated in different namespaces. For instance:
616 #
617 # class Model[T](BaseModel):
618 # a: dict[T, 'Later']
619 #
620 # M = Model['Item']
621 #
622 # The stored annotation of `M.a` is `dict['Item', 'Later']`, where `'Later'` must be evaluated
623 # in the namespace of `Model` and `'Item'` in the namespace of `M`. Instead,
624 # first evaluate the (unsubstituted) annotation of the origin in its own namespace:
625 with ns_resolver.push(origin):
626 annotation = _typing_extra.eval_type(
627 origin_field._original_annotation,
628 *ns_resolver.types_namespace,
629 )
630 new_field = _recreate_field_info(
631 field_info,
632 ns_resolver=ns_resolver,
633 typevars_map=typevars_map,
634 lenient=False,
635 annotation=annotation,
636 )
637 update_field_from_config(config_wrapper, f_name, new_field)
638 rebuilt_fields[f_name] = new_field
639
640 if cls.__pydantic_extra_info__ is not None and not cls.__pydantic_extra_info__.complete:
641 # Not the best pattern (see comment in `_recreate_field_info()`):
642 ann = _typing_extra.eval_type(
643 cls.__pydantic_extra_info__.annotation,
644 *ns_resolver.types_namespace,
645 )
646 ann = _generics.replace_types(ann, typevars_map)
647 ann = _typing_extra.eval_type(
648 ann,
649 *ns_resolver.types_namespace,
650 )
651 rebuilt_extra_info = PydanticExtraInfo(
652 annotation=ann,
653 complete=True,
654 )
655 else:
656 rebuilt_extra_info = cls.__pydantic_extra_info__
657
658 return rebuilt_fields, rebuilt_extra_info
659
660
661def _recreate_field_info(
662 field_info: FieldInfo,
663 ns_resolver: NsResolver,
664 typevars_map: Mapping[TypeVar, Any],
665 *,
666 lenient: bool,
667 annotation: Any = PydanticUndefined,
668) -> FieldInfo:
669 FieldInfo_ = import_cached_field_info()
670
671 if annotation is PydanticUndefined:
672 annotation = field_info._original_annotation
673
674 existing_desc = field_info.description
675 if lenient:
676 ann = _generics.replace_types(field_info._original_annotation, typevars_map)
677 ann, evaluated = _typing_extra.try_eval_type(
678 ann,
679 *ns_resolver.types_namespace,
680 )
681 else:
682 # Not the best pattern, maybe we could ship our own `eval_type()`,
683 # that would replace the type variables on the fly during evaluation.
684 ann = _typing_extra.eval_type(
685 annotation,
686 *ns_resolver.types_namespace,
687 )
688 ann = _generics.replace_types(ann, typevars_map)
689 ann = _typing_extra.eval_type(
690 ann,
691 *ns_resolver.types_namespace,
692 )
693 evaluated = True
694
695 if (assign := field_info._original_assignment) is PydanticUndefined:
696 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS)
697 else:
698 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS)
699 new_field._original_assignment = assign
700 new_field._original_annotation = ann
701 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
702 new_field.description = new_field.description if new_field.description is not None else existing_desc
703 if not evaluated:
704 new_field._complete = False
705
706 return new_field
707
708
709def collect_dataclass_fields(
710 cls: type[StandardDataclass],
711 *,
712 config_wrapper: ConfigWrapper,
713 ns_resolver: NsResolver | None = None,
714 typevars_map: dict[Any, Any] | None = None,
715) -> dict[str, FieldInfo]:
716 """Collect the fields of a dataclass.
717
718 Args:
719 cls: dataclass.
720 config_wrapper: The config wrapper instance.
721 ns_resolver: Namespace resolver to use when getting dataclass annotations.
722 Defaults to an empty instance.
723 typevars_map: A dictionary mapping type variables to their concrete types.
724
725 Returns:
726 The dataclass fields.
727 """
728 FieldInfo_ = import_cached_field_info()
729
730 fields: dict[str, FieldInfo] = {}
731 ns_resolver = ns_resolver or NsResolver()
732 dataclass_fields = cls.__dataclass_fields__
733
734 # The logic here is similar to `_typing_extra.get_cls_type_hints`,
735 # although we do it manually as stdlib dataclasses already have annotations
736 # collected in each class:
737 for base in reversed(cls.__mro__):
738 if not dataclasses.is_dataclass(base):
739 continue
740
741 with ns_resolver.push(base):
742 for ann_name, dataclass_field in dataclass_fields.items():
743 base_anns = _typing_extra.safe_get_annotations(base)
744
745 if ann_name not in base_anns:
746 # `__dataclass_fields__`contains every field, even the ones from base classes.
747 # Only collect the ones defined on `base`.
748 continue
749
750 globalns, localns = ns_resolver.types_namespace
751 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns)
752
753 if _typing_extra.is_classvar_annotation(ann_type):
754 continue
755
756 if (
757 not dataclass_field.init
758 and dataclass_field.default is dataclasses.MISSING
759 and dataclass_field.default_factory is dataclasses.MISSING
760 ):
761 # TODO: We should probably do something with this so that validate_assignment behaves properly
762 # Issue: https://github.com/pydantic/pydantic/issues/5470
763 continue
764
765 if isinstance(dataclass_field.default, FieldInfo_):
766 if dataclass_field.default.init_var:
767 if dataclass_field.default.init is False:
768 raise PydanticUserError(
769 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
770 code='clashing-init-and-init-var',
771 )
772
773 # TODO: same note as above re validate_assignment
774 continue
775 field_info = FieldInfo_.from_annotated_attribute(
776 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
777 )
778 field_info._original_assignment = dataclass_field.default
779 else:
780 field_info = FieldInfo_.from_annotated_attribute(
781 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
782 )
783 field_info._original_assignment = dataclass_field
784
785 if not evaluated:
786 field_info._complete = False
787 field_info._original_annotation = ann_type
788
789 fields[ann_name] = field_info
790 update_field_from_config(config_wrapper, ann_name, field_info)
791
792 if field_info.default is not PydanticUndefined and isinstance(
793 getattr(cls, ann_name, field_info), FieldInfo_
794 ):
795 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
796 setattr(cls, ann_name, field_info.default)
797
798 if typevars_map:
799 for field in fields.values():
800 # We don't pass any ns, as `field.annotation`
801 # was already evaluated. TODO: is this method relevant?
802 # Can't we juste use `_generics.replace_types`?
803 field.apply_typevars_map(typevars_map)
804
805 if config_wrapper.use_attribute_docstrings:
806 _update_fields_from_docstrings(
807 cls,
808 fields,
809 # We can't rely on the (more reliable) frame inspection method
810 # for stdlib dataclasses:
811 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
812 )
813
814 return fields
815
816
817def rebuild_dataclass_fields(
818 cls: type[PydanticDataclass],
819 *,
820 config_wrapper: ConfigWrapper,
821 ns_resolver: NsResolver,
822 typevars_map: Mapping[TypeVar, Any],
823) -> dict[str, FieldInfo]:
824 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations.
825
826 This function should be called whenever a dataclass with incomplete fields is encountered.
827
828 Raises:
829 NameError: If one of the annotations failed to evaluate.
830
831 Note:
832 This function *doesn't* mutate the dataclass fields in place, as it can be called during
833 schema generation, where you don't want to mutate other dataclass's fields.
834 """
835 FieldInfo_ = import_cached_field_info()
836
837 rebuilt_fields: dict[str, FieldInfo] = {}
838 with ns_resolver.push(cls):
839 for f_name, field_info in cls.__pydantic_fields__.items():
840 if field_info._complete:
841 rebuilt_fields[f_name] = field_info
842 else:
843 existing_desc = field_info.description
844 ann = _typing_extra.eval_type(
845 field_info._original_annotation,
846 *ns_resolver.types_namespace,
847 )
848 ann = _generics.replace_types(ann, typevars_map)
849 new_field = FieldInfo_.from_annotated_attribute(
850 ann,
851 field_info._original_assignment,
852 _source=AnnotationSource.DATACLASS,
853 )
854
855 # The description might come from the docstring if `use_attribute_docstrings` was `True`:
856 new_field.description = new_field.description if new_field.description is not None else existing_desc
857 update_field_from_config(config_wrapper, f_name, new_field)
858 rebuilt_fields[f_name] = new_field
859
860 return rebuilt_fields
861
862
863def is_valid_field_name(name: str) -> bool:
864 return not name.startswith('_')
865
866
867def is_valid_privateattr_name(name: str) -> bool:
868 return name.startswith('_') and not name.startswith('__')
869
870
871def takes_validated_data_argument(
872 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
873) -> TypeIs[Callable[[dict[str, Any]], Any]]:
874 """Whether the provided default factory callable has a validated data parameter."""
875 try:
876 sig = _typing_extra.signature_no_eval(default_factory)
877 except (ValueError, TypeError):
878 # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
879 # In this case, we assume no data argument is present:
880 return False
881
882 parameters = list(sig.parameters.values())
883
884 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty
885
886
887def resolve_default_value(
888 *,
889 default: Any,
890 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None,
891 default_factory_takes_validated_data_argument: bool | None,
892 validated_data: dict[str, Any] | None = None,
893 call_default_factory: bool = False,
894) -> Any:
895 """Resolve the default value using either a static default or a default_factory."""
896 if default_factory is None:
897 return smart_deepcopy(default)
898 if call_default_factory:
899 if default_factory_takes_validated_data_argument:
900 fac = cast('Callable[[dict[str, Any]], Any]', default_factory)
901 if validated_data is None:
902 raise ValueError(
903 "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default()'."
904 )
905 return fac(validated_data)
906 else:
907 fac = cast('Callable[[], Any]', default_factory)
908 return fac()
909
910 return PydanticUndefined