Coverage for pydantic/_internal/_fields.py: 98.07%

197 statements  

« prev     ^ index     » next       coverage.py v7.8.1, created at 2025-05-22 20:36 +0000

1"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`.""" 

2 

3from __future__ import annotations as _annotations 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

4 

5import dataclasses 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

6import warnings 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

7from collections.abc import Mapping 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

8from copy import copy 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

9from functools import cache 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

10from inspect import Parameter, ismethoddescriptor, signature 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

11from re import Pattern 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

12from typing import TYPE_CHECKING, Any, Callable, TypeVar 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

13 

14from pydantic_core import PydanticUndefined 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

15from typing_extensions import TypeIs, get_origin 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

16from typing_inspection import typing_objects 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

17from typing_inspection.introspection import AnnotationSource 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

18 

19from pydantic import PydanticDeprecatedSince211 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

20from pydantic.errors import PydanticUserError 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

21 

22from . import _generics, _typing_extra 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

23from ._config import ConfigWrapper 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

24from ._docs_extraction import extract_docstrings_from_cls 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

25from ._import_utils import import_cached_base_model, import_cached_field_info 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

26from ._namespace_utils import NsResolver 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

27from ._repr import Representation 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

28from ._utils import can_be_positional 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

29 

30if TYPE_CHECKING: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

31 from annotated_types import BaseMetadata 

32 

33 from ..fields import FieldInfo 

34 from ..main import BaseModel 

35 from ._dataclasses import StandardDataclass 

36 from ._decorators import DecoratorInfos 

37 

38 

39class PydanticMetadata(Representation): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

40 """Base class for annotation markers like `Strict`.""" 

41 

42 __slots__ = () 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

43 

44 

45def pydantic_general_metadata(**metadata: Any) -> BaseMetadata: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

46 """Create a new `_PydanticGeneralMetadata` class with the given metadata. 

47 

48 Args: 

49 **metadata: The metadata to add. 

50 

51 Returns: 

52 The new `_PydanticGeneralMetadata` class. 

53 """ 

54 return _general_metadata_cls()(metadata) # type: ignore 1esakblftguvIHwxmnopyzABChDcqdriEjFG

55 

56 

57@cache 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

58def _general_metadata_cls() -> type[BaseMetadata]: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

59 """Do it this way to avoid importing `annotated_types` at import time.""" 

60 from annotated_types import BaseMetadata 1esakblftguvIHwxmnopyzABChDcqdriEjFG

61 

62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

63 """Pydantic general metadata like `max_digits`.""" 

64 

65 def __init__(self, metadata: Any): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

66 self.__dict__ = metadata 1esakblftguvIHwxmnopyzABChDcqdriEjFG

67 

68 return _PydanticGeneralMetadata # type: ignore 1esakblftguvIHwxmnopyzABChDcqdriEjFG

69 

70 

71def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

72 fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

73 for ann_name, field_info in fields.items(): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

74 if field_info.description is None and ann_name in fields_docs: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

75 field_info.description = fields_docs[ann_name] 1esakblftguvIHwxmnopyzABChDcqdriEjFG

76 

77 

78def collect_model_fields( # noqa: C901 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

79 cls: type[BaseModel], 

80 config_wrapper: ConfigWrapper, 

81 ns_resolver: NsResolver | None, 

82 *, 

83 typevars_map: Mapping[TypeVar, Any] | None = None, 

84) -> tuple[dict[str, FieldInfo], set[str]]: 

85 """Collect the fields and class variables names of a nascent Pydantic model. 

86 

87 The fields collection process is *lenient*, meaning it won't error if string annotations 

88 fail to evaluate. If this happens, the original annotation (and assigned value, if any) 

89 is stored on the created `FieldInfo` instance. 

90 

91 The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model), 

92 and will make use of these stored attributes. 

93 

94 Args: 

95 cls: BaseModel or dataclass. 

96 config_wrapper: The config wrapper instance. 

97 ns_resolver: Namespace resolver to use when getting model annotations. 

98 typevars_map: A dictionary mapping type variables to their concrete types. 

99 

100 Returns: 

101 A two-tuple containing model fields and class variables names. 

102 

103 Raises: 

104 NameError: 

105 - If there is a conflict between a field name and protected namespaces. 

106 - If there is a field other than `root` in `RootModel`. 

107 - If a field shadows an attribute in the parent model. 

108 """ 

109 BaseModel = import_cached_base_model() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

110 FieldInfo_ = import_cached_field_info() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

111 

112 bases = cls.__bases__ 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

113 parent_fields_lookup: dict[str, FieldInfo] = {} 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

114 for base in reversed(bases): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

115 if model_fields := getattr(base, '__pydantic_fields__', None): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

116 parent_fields_lookup.update(model_fields) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

117 

118 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

119 

120 # https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older 

121 # annotations is only used for finding fields in parent classes 

122 annotations = cls.__dict__.get('__annotations__', {}) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

123 fields: dict[str, FieldInfo] = {} 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

124 

125 class_vars: set[str] = set() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

126 for ann_name, (ann_type, evaluated) in type_hints.items(): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

127 if ann_name == 'model_config': 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

128 # We never want to treat `model_config` as a field 

129 # Note: we may need to change this logic if/when we introduce a `BareModel` class with no 

130 # protected namespaces (where `model_config` might be allowed as a field name) 

131 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

132 

133 for protected_namespace in config_wrapper.protected_namespaces: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

134 ns_violation: bool = False 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

135 if isinstance(protected_namespace, Pattern): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

136 ns_violation = protected_namespace.match(ann_name) is not None 1esakblftguvIHwxmnopyzABChDcqdriEjFG

137 elif isinstance(protected_namespace, str): 137 ↛ 140line 137 didn't jump to line 140 because the condition on line 137 was always true1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

138 ns_violation = ann_name.startswith(protected_namespace) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

139 

140 if ns_violation: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

141 for b in bases: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

142 if hasattr(b, ann_name): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

143 if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})): 143 ↛ 141line 143 didn't jump to line 141 because the condition on line 143 was always true1eabfghcdij

144 raise NameError( 1eabfghcdij

145 f'Field "{ann_name}" conflicts with member {getattr(b, ann_name)}' 

146 f' of protected namespace "{protected_namespace}".' 

147 ) 

148 else: 

149 valid_namespaces = () 1esakblftguvIHwxmnopyzABChDcqdriEjFG

150 for pn in config_wrapper.protected_namespaces: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

151 if isinstance(pn, Pattern): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

152 if not pn.match(ann_name): 152 ↛ 153line 152 didn't jump to line 153 because the condition on line 152 was never true1esakblftguvIHwxmnopyzABChDcqdriEjFG

153 valid_namespaces += (f're.compile({pn.pattern})',) 

154 else: 

155 if not ann_name.startswith(pn): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

156 valid_namespaces += (pn,) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

157 

158 warnings.warn( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

159 f'Field "{ann_name}" in {cls.__name__} has conflict with protected namespace "{protected_namespace}".' 

160 '\n\nYou may be able to resolve this warning by setting' 

161 f" `model_config['protected_namespaces'] = {valid_namespaces}`.", 

162 UserWarning, 

163 ) 

164 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

165 class_vars.add(ann_name) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

166 continue 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

167 

168 assigned_value = getattr(cls, ann_name, PydanticUndefined) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

169 

170 if not is_valid_field_name(ann_name): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

171 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

172 if cls.__pydantic_root_model__ and ann_name != 'root': 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

173 raise NameError( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

174 f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`" 

175 ) 

176 

177 # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get 

178 # "... shadows an attribute" warnings 

179 generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin') 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

180 for base in bases: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

181 dataclass_fields = { 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

182 field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ()) 

183 } 

184 if hasattr(base, ann_name): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

185 if base is generic_origin: 185 ↛ 187line 185 didn't jump to line 187 because the condition on line 185 was never true1esakblftguvIHwxmnopyzABChDcqdriEjFG

186 # Don't warn when "shadowing" of attributes in parametrized generics 

187 continue 

188 

189 if ann_name in dataclass_fields: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

190 # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set 

191 # on the class instance. 

192 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

193 

194 if ann_name not in annotations: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

195 # Don't warn when a field exists in a parent class but has not been defined in the current class 

196 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

197 

198 warnings.warn( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

199 f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent ' 

200 f'"{base.__qualname__}"', 

201 UserWarning, 

202 ) 

203 

204 if assigned_value is PydanticUndefined: # no assignment, just a plain annotation 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

205 if ann_name in annotations or ann_name not in parent_fields_lookup: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

206 # field is either: 

207 # - present in the current model's annotations (and *not* from parent classes) 

208 # - not found on any base classes; this seems to be caused by fields bot getting 

209 # generated due to models not being fully defined while initializing recursive models. 

210 # Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this. 

211 field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

212 if not evaluated: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

213 field_info._complete = False 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

214 # Store the original annotation that should be used to rebuild 

215 # the field info later: 

216 field_info._original_annotation = ann_type 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

217 else: 

218 # The field was present on one of the (possibly multiple) base classes 

219 # copy the field to make sure typevar substitutions don't cause issues with the base classes 

220 field_info = copy(parent_fields_lookup[ann_name]) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

221 

222 else: # An assigned value is present (either the default value, or a `Field()` function) 

223 _warn_on_nested_alias_in_annotation(ann_type, ann_name) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

224 if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

225 # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__` 

226 # for descriptors, so we do the same if the `= field(default=...)` form is used. 

227 # Note that we only do this for method descriptors for now, we might want to 

228 # extend this to any descriptor in the future (by simply checking for 

229 # `hasattr(assigned_value.default, '__get__')`). 

230 assigned_value.default = assigned_value.default.__get__(None, cls) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

231 

232 # The `from_annotated_attribute()` call below mutates the assigned `Field()`, so make a copy: 

233 original_assignment = ( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

234 assigned_value._copy() if not evaluated and isinstance(assigned_value, FieldInfo_) else assigned_value 

235 ) 

236 

237 field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

238 if not evaluated: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

239 field_info._complete = False 1esakblftguvIHwxmnopyzABChDcqdriEjFG

240 # Store the original annotation and assignment value that should be used to rebuild 

241 # the field info later: 

242 field_info._original_annotation = ann_type 1esakblftguvIHwxmnopyzABChDcqdriEjFG

243 field_info._original_assignment = original_assignment 1esakblftguvIHwxmnopyzABChDcqdriEjFG

244 elif 'final' in field_info._qualifiers and not field_info.is_required(): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

245 warnings.warn( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

246 f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a ' 

247 'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you ' 

248 f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[<type>] = <default>.`', 

249 category=PydanticDeprecatedSince211, 

250 # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case: 

251 stacklevel=4, 

252 ) 

253 class_vars.add(ann_name) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

254 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

255 

256 # attributes which are fields are removed from the class namespace: 

257 # 1. To match the behaviour of annotation-only fields 

258 # 2. To avoid false positives in the NameError check above 

259 try: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

260 delattr(cls, ann_name) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

261 except AttributeError: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

262 pass # indicates the attribute was on a parent class 1esakblftguvIHwxmnopyzABChDcqdriEjFG

263 

264 # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__ 

265 # to make sure the decorators have already been built for this exact class 

266 decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__'] 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

267 if ann_name in decorators.computed_fields: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

268 raise TypeError( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

269 f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. ' 

270 'This override with a computed_field is incompatible.' 

271 ) 

272 fields[ann_name] = field_info 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

273 

274 if typevars_map: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

275 for field in fields.values(): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

276 if field._complete: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

277 field.apply_typevars_map(typevars_map) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

278 

279 if config_wrapper.use_attribute_docstrings: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

280 _update_fields_from_docstrings(cls, fields) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

281 return fields, class_vars 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

282 

283 

284def _warn_on_nested_alias_in_annotation(ann_type: type[Any], ann_name: str) -> None: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

285 FieldInfo = import_cached_field_info() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

286 

287 args = getattr(ann_type, '__args__', None) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

288 if args: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

289 for anno_arg in args: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

290 if typing_objects.is_annotated(get_origin(anno_arg)): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

291 for anno_type_arg in _typing_extra.get_args(anno_arg): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

292 if isinstance(anno_type_arg, FieldInfo) and anno_type_arg.alias is not None: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

293 warnings.warn( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

294 f'`alias` specification on field "{ann_name}" must be set on outermost annotation to take effect.', 

295 UserWarning, 

296 ) 

297 return 1esakblftguvIHwxmnopyzABChDcqdriEjFG

298 

299 

300def rebuild_model_fields( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

301 cls: type[BaseModel], 

302 *, 

303 ns_resolver: NsResolver, 

304 typevars_map: Mapping[TypeVar, Any], 

305) -> dict[str, FieldInfo]: 

306 """Rebuild the (already present) model fields by trying to reevaluate annotations. 

307 

308 This function should be called whenever a model with incomplete fields is encountered. 

309 

310 Raises: 

311 NameError: If one of the annotations failed to evaluate. 

312 

313 Note: 

314 This function *doesn't* mutate the model fields in place, as it can be called during 

315 schema generation, where you don't want to mutate other model's fields. 

316 """ 

317 FieldInfo_ = import_cached_field_info() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

318 

319 rebuilt_fields: dict[str, FieldInfo] = {} 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

320 with ns_resolver.push(cls): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

321 for f_name, field_info in cls.__pydantic_fields__.items(): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

322 if field_info._complete: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

323 rebuilt_fields[f_name] = field_info 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

324 else: 

325 existing_desc = field_info.description 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

326 ann = _typing_extra.eval_type( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

327 field_info._original_annotation, 

328 *ns_resolver.types_namespace, 

329 ) 

330 ann = _generics.replace_types(ann, typevars_map) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

331 

332 if (assign := field_info._original_assignment) is PydanticUndefined: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

333 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

334 else: 

335 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

336 # The description might come from the docstring if `use_attribute_docstrings` was `True`: 

337 new_field.description = new_field.description if new_field.description is not None else existing_desc 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

338 rebuilt_fields[f_name] = new_field 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

339 

340 return rebuilt_fields 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

341 

342 

343def collect_dataclass_fields( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

344 cls: type[StandardDataclass], 

345 *, 

346 ns_resolver: NsResolver | None = None, 

347 typevars_map: dict[Any, Any] | None = None, 

348 config_wrapper: ConfigWrapper | None = None, 

349) -> dict[str, FieldInfo]: 

350 """Collect the fields of a dataclass. 

351 

352 Args: 

353 cls: dataclass. 

354 ns_resolver: Namespace resolver to use when getting dataclass annotations. 

355 Defaults to an empty instance. 

356 typevars_map: A dictionary mapping type variables to their concrete types. 

357 config_wrapper: The config wrapper instance. 

358 

359 Returns: 

360 The dataclass fields. 

361 """ 

362 FieldInfo_ = import_cached_field_info() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

363 

364 fields: dict[str, FieldInfo] = {} 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

365 ns_resolver = ns_resolver or NsResolver() 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

366 dataclass_fields = cls.__dataclass_fields__ 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

367 

368 # The logic here is similar to `_typing_extra.get_cls_type_hints`, 

369 # although we do it manually as stdlib dataclasses already have annotations 

370 # collected in each class: 

371 for base in reversed(cls.__mro__): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

372 if not dataclasses.is_dataclass(base): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

373 continue 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

374 

375 with ns_resolver.push(base): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

376 for ann_name, dataclass_field in dataclass_fields.items(): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

377 if ann_name not in base.__dict__.get('__annotations__', {}): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

378 # `__dataclass_fields__`contains every field, even the ones from base classes. 

379 # Only collect the ones defined on `base`. 

380 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

381 

382 globalns, localns = ns_resolver.types_namespace 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

383 ann_type, _ = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

384 

385 if _typing_extra.is_classvar_annotation(ann_type): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

386 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

387 

388 if ( 1esakblIHwxmnopJKLhDcqdr

389 not dataclass_field.init 

390 and dataclass_field.default is dataclasses.MISSING 

391 and dataclass_field.default_factory is dataclasses.MISSING 

392 ): 

393 # TODO: We should probably do something with this so that validate_assignment behaves properly 

394 # Issue: https://github.com/pydantic/pydantic/issues/5470 

395 continue 1esakblftguvIHwxmnopyzABChDcqdriEjFG

396 

397 if isinstance(dataclass_field.default, FieldInfo_): 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

398 if dataclass_field.default.init_var: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

399 if dataclass_field.default.init is False: 1esakblftguvIHwxmnopyzABChDcqdriEjFG

400 raise PydanticUserError( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

401 f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.', 

402 code='clashing-init-and-init-var', 

403 ) 

404 

405 # TODO: same note as above re validate_assignment 

406 continue 1akblftguvHmnopyzABCcqdriEjFG

407 field_info = FieldInfo_.from_annotated_attribute( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

408 ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS 

409 ) 

410 else: 

411 field_info = FieldInfo_.from_annotated_attribute( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

412 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS 

413 ) 

414 

415 fields[ann_name] = field_info 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

416 

417 if field_info.default is not PydanticUndefined and isinstance( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

418 getattr(cls, ann_name, field_info), FieldInfo_ 

419 ): 

420 # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo 

421 setattr(cls, ann_name, field_info.default) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

422 

423 if typevars_map: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

424 for field in fields.values(): 1esakblftguvIHwxmnopyzABChDcqdriEjFG

425 # We don't pass any ns, as `field.annotation` 

426 # was already evaluated. TODO: is this method relevant? 

427 # Can't we juste use `_generics.replace_types`? 

428 field.apply_typevars_map(typevars_map) 1esakblftguvIHwxmnopyzABChDcqdriEjFG

429 

430 if config_wrapper is not None and config_wrapper.use_attribute_docstrings: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

431 _update_fields_from_docstrings( 1esakblftguvIHwxmnopyzABChDcqdriEjFG

432 cls, 

433 fields, 

434 # We can't rely on the (more reliable) frame inspection method 

435 # for stdlib dataclasses: 

436 use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'), 

437 ) 

438 

439 return fields 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

440 

441 

442def is_valid_field_name(name: str) -> bool: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

443 return not name.startswith('_') 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

444 

445 

446def is_valid_privateattr_name(name: str) -> bool: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

447 return name.startswith('_') and not name.startswith('__') 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

448 

449 

450def takes_validated_data_argument( 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

451 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any], 

452) -> TypeIs[Callable[[dict[str, Any]], Any]]: 

453 """Whether the provided default factory callable has a validated data parameter.""" 

454 try: 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

455 sig = signature(default_factory) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

456 except (ValueError, TypeError): 1esakblftguvwxmnopyzABChDcqdriEjFG

457 # `inspect.signature` might not be able to infer a signature, e.g. with C objects. 

458 # In this case, we assume no data argument is present: 

459 return False 1esakblftguvwxmnopyzABChDcqdriEjFG

460 

461 parameters = list(sig.parameters.values()) 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG

462 

463 return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty 1esakblftguvIHwxmnopyzABCJKLMNOPhDcqdriEjFG