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

253 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-22 09:30 +0000

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

2 

3from __future__ import annotations as _annotations 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

4 

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

13 

14from pydantic_core import PydanticUndefined 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

15from typing_extensions import TypeIs 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

16from typing_inspection.introspection import AnnotationSource 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

17 

18from pydantic import PydanticDeprecatedSince211 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

19from pydantic.errors import PydanticUserError 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

20 

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

29 

30if TYPE_CHECKING: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

31 from annotated_types import BaseMetadata 

32 

33 from ..fields import FieldInfo 

34 from ..main import BaseModel 

35 from ._dataclasses import PydanticDataclass, StandardDataclass 

36 from ._decorators import DecoratorInfos 

37 

38 

39class PydanticMetadata(Representation): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

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

41 

42 __slots__ = () 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

43 

44 

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

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 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

55 

56 

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

61 

62 class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata): 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

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

64 

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

66 self.__dict__ = metadata 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

67 

68 return _PydanticGeneralMetadata # type: ignore 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

69 

70 

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

78 

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

85 

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

103 

104 valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}' 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

105 

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 ) 

112 

113 

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

119 

120 

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

130 

131 field_info.title = title 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

132 

133 

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. 

138 

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

155 

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

162 

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

168 

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

174 

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

182 

183 

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. 

186 

187 This will apply the title and alias generators from the configuration. 

188 

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

199 

200 

201_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

202 

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} 

215 

216 

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. 

225 

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. 

229 

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. 

232 

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. 

238 

239 Returns: 

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

241 

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

250 

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

256 

257 type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

258 

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

262 

263 fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

264 

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

272 

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 ) 

279 

280 if _typing_extra.is_classvar_annotation(ann_type): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

281 class_vars.add(ann_name) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

282 continue 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

283 

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

299 

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 ) 

306 

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 

318 

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

323 

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

327 

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 ) 

333 

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

351 

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

362 

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

382 

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

390 

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

400 

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

404 

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

409 

410 if config_wrapper.use_attribute_docstrings: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

411 _update_fields_from_docstrings(cls, fields) 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK

412 return fields, class_vars 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

413 

414 

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. 

423 

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

425 

426 Raises: 

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

428 

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

434 

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

447 

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

456 

457 return rebuilt_fields 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

458 

459 

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. 

468 

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. 

475 

476 Returns: 

477 The dataclass fields. 

478 """ 

479 FieldInfo_ = import_cached_field_info() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

480 

481 fields: dict[str, FieldInfo] = {} 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

482 ns_resolver = ns_resolver or NsResolver() 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

483 dataclass_fields = cls.__dataclass_fields__ 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

484 

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

491 

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

495 

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

500 

501 globalns, localns = ns_resolver.types_namespace 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

502 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

503 

504 if _typing_extra.is_classvar_annotation(ann_type): 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

505 continue 1ambncogyhziALBpqrstuCDEFGHdvewfxjIkJlK

506 

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

515 

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 ) 

523 

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

535 

536 if not evaluated: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

537 field_info._complete = False 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

538 field_info._original_annotation = ann_type 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

539 

540 fields[ann_name] = field_info 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

541 update_field_from_config(config_wrapper, ann_name, field_info) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

542 

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

548 

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

555 

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 ) 

564 

565 return fields 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

566 

567 

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. 

576 

577 This function should be called whenever a dataclass with incomplete fields is encountered. 

578 

579 Raises: 

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

581 

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

587 

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 ) 

605 

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

610 

611 return rebuilt_fields 1ambncogyhziAMLBpqrstuCDEFGHNdvewfxjIkJlKO

612 

613 

614def is_valid_field_name(name: str) -> bool: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

615 return not name.startswith('_') 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

616 

617 

618def is_valid_privateattr_name(name: str) -> bool: 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

619 return name.startswith('_') and not name.startswith('__') 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

620 

621 

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

632 

633 parameters = list(sig.parameters.values()) 1ambncogyhziAMLBpqrstuCDEFGHNPdvewfxjIkJlKO

634 

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