Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pydantic/_internal/_fields.py: 53%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

359 statements  

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 with ns_resolver.push(cls): 

607 for f_name, field_info in cls.__pydantic_fields__.items(): 

608 if field_info._complete: 

609 rebuilt_fields[f_name] = field_info 

610 else: 

611 new_field = _recreate_field_info( 

612 field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=False 

613 ) 

614 update_field_from_config(config_wrapper, f_name, new_field) 

615 rebuilt_fields[f_name] = new_field 

616 

617 if cls.__pydantic_extra_info__ is not None and not cls.__pydantic_extra_info__.complete: 

618 # Not the best pattern (see comment in `_recreate_field_info()`): 

619 ann = _typing_extra.eval_type( 

620 cls.__pydantic_extra_info__.annotation, 

621 *ns_resolver.types_namespace, 

622 ) 

623 ann = _generics.replace_types(ann, typevars_map) 

624 ann = _typing_extra.eval_type( 

625 ann, 

626 *ns_resolver.types_namespace, 

627 ) 

628 rebuilt_extra_info = PydanticExtraInfo( 

629 annotation=ann, 

630 complete=True, 

631 ) 

632 else: 

633 rebuilt_extra_info = cls.__pydantic_extra_info__ 

634 

635 return rebuilt_fields, rebuilt_extra_info 

636 

637 

638def _recreate_field_info( 

639 field_info: FieldInfo, 

640 ns_resolver: NsResolver, 

641 typevars_map: Mapping[TypeVar, Any], 

642 *, 

643 lenient: bool, 

644) -> FieldInfo: 

645 FieldInfo_ = import_cached_field_info() 

646 

647 existing_desc = field_info.description 

648 if lenient: 

649 ann = _generics.replace_types(field_info._original_annotation, typevars_map) 

650 ann, evaluated = _typing_extra.try_eval_type( 

651 ann, 

652 *ns_resolver.types_namespace, 

653 ) 

654 else: 

655 # Not the best pattern, maybe we could ship our own `eval_type()`, 

656 # that would replace the type variables on the fly during evaluation. 

657 ann = _typing_extra.eval_type( 

658 field_info._original_annotation, 

659 *ns_resolver.types_namespace, 

660 ) 

661 ann = _generics.replace_types(ann, typevars_map) 

662 ann = _typing_extra.eval_type( 

663 ann, 

664 *ns_resolver.types_namespace, 

665 ) 

666 evaluated = True 

667 

668 if (assign := field_info._original_assignment) is PydanticUndefined: 

669 new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS) 

670 else: 

671 new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS) 

672 new_field._original_assignment = assign 

673 new_field._original_annotation = ann 

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

675 new_field.description = new_field.description if new_field.description is not None else existing_desc 

676 if not evaluated: 

677 new_field._complete = False 

678 

679 return new_field 

680 

681 

682def collect_dataclass_fields( 

683 cls: type[StandardDataclass], 

684 *, 

685 config_wrapper: ConfigWrapper, 

686 ns_resolver: NsResolver | None = None, 

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

688) -> dict[str, FieldInfo]: 

689 """Collect the fields of a dataclass. 

690 

691 Args: 

692 cls: dataclass. 

693 config_wrapper: The config wrapper instance. 

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

695 Defaults to an empty instance. 

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

697 

698 Returns: 

699 The dataclass fields. 

700 """ 

701 FieldInfo_ = import_cached_field_info() 

702 

703 fields: dict[str, FieldInfo] = {} 

704 ns_resolver = ns_resolver or NsResolver() 

705 dataclass_fields = cls.__dataclass_fields__ 

706 

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

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

709 # collected in each class: 

710 for base in reversed(cls.__mro__): 

711 if not dataclasses.is_dataclass(base): 

712 continue 

713 

714 with ns_resolver.push(base): 

715 for ann_name, dataclass_field in dataclass_fields.items(): 

716 base_anns = _typing_extra.safe_get_annotations(base) 

717 

718 if ann_name not in base_anns: 

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

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

721 continue 

722 

723 globalns, localns = ns_resolver.types_namespace 

724 ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns) 

725 

726 if _typing_extra.is_classvar_annotation(ann_type): 

727 continue 

728 

729 if ( 

730 not dataclass_field.init 

731 and dataclass_field.default is dataclasses.MISSING 

732 and dataclass_field.default_factory is dataclasses.MISSING 

733 ): 

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

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

736 continue 

737 

738 if isinstance(dataclass_field.default, FieldInfo_): 

739 if dataclass_field.default.init_var: 

740 if dataclass_field.default.init is False: 

741 raise PydanticUserError( 

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

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

744 ) 

745 

746 # TODO: same note as above re validate_assignment 

747 continue 

748 field_info = FieldInfo_.from_annotated_attribute( 

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

750 ) 

751 field_info._original_assignment = dataclass_field.default 

752 else: 

753 field_info = FieldInfo_.from_annotated_attribute( 

754 ann_type, dataclass_field, _source=AnnotationSource.DATACLASS 

755 ) 

756 field_info._original_assignment = dataclass_field 

757 

758 if not evaluated: 

759 field_info._complete = False 

760 field_info._original_annotation = ann_type 

761 

762 fields[ann_name] = field_info 

763 update_field_from_config(config_wrapper, ann_name, field_info) 

764 

765 if field_info.default is not PydanticUndefined and isinstance( 

766 getattr(cls, ann_name, field_info), FieldInfo_ 

767 ): 

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

769 setattr(cls, ann_name, field_info.default) 

770 

771 if typevars_map: 

772 for field in fields.values(): 

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

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

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

776 field.apply_typevars_map(typevars_map) 

777 

778 if config_wrapper.use_attribute_docstrings: 

779 _update_fields_from_docstrings( 

780 cls, 

781 fields, 

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

783 # for stdlib dataclasses: 

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

785 ) 

786 

787 return fields 

788 

789 

790def rebuild_dataclass_fields( 

791 cls: type[PydanticDataclass], 

792 *, 

793 config_wrapper: ConfigWrapper, 

794 ns_resolver: NsResolver, 

795 typevars_map: Mapping[TypeVar, Any], 

796) -> dict[str, FieldInfo]: 

797 """Rebuild the (already present) dataclass fields by trying to reevaluate annotations. 

798 

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

800 

801 Raises: 

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

803 

804 Note: 

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

806 schema generation, where you don't want to mutate other dataclass's fields. 

807 """ 

808 FieldInfo_ = import_cached_field_info() 

809 

810 rebuilt_fields: dict[str, FieldInfo] = {} 

811 with ns_resolver.push(cls): 

812 for f_name, field_info in cls.__pydantic_fields__.items(): 

813 if field_info._complete: 

814 rebuilt_fields[f_name] = field_info 

815 else: 

816 existing_desc = field_info.description 

817 ann = _typing_extra.eval_type( 

818 field_info._original_annotation, 

819 *ns_resolver.types_namespace, 

820 ) 

821 ann = _generics.replace_types(ann, typevars_map) 

822 new_field = FieldInfo_.from_annotated_attribute( 

823 ann, 

824 field_info._original_assignment, 

825 _source=AnnotationSource.DATACLASS, 

826 ) 

827 

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

829 new_field.description = new_field.description if new_field.description is not None else existing_desc 

830 update_field_from_config(config_wrapper, f_name, new_field) 

831 rebuilt_fields[f_name] = new_field 

832 

833 return rebuilt_fields 

834 

835 

836def is_valid_field_name(name: str) -> bool: 

837 return not name.startswith('_') 

838 

839 

840def is_valid_privateattr_name(name: str) -> bool: 

841 return name.startswith('_') and not name.startswith('__') 

842 

843 

844def takes_validated_data_argument( 

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

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

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

848 try: 

849 sig = _typing_extra.signature_no_eval(default_factory) 

850 except (ValueError, TypeError): 

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

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

853 return False 

854 

855 parameters = list(sig.parameters.values()) 

856 

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

858 

859 

860def resolve_default_value( 

861 *, 

862 default: Any, 

863 default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None, 

864 default_factory_takes_validated_data_argument: bool | None, 

865 validated_data: dict[str, Any] | None = None, 

866 call_default_factory: bool = False, 

867) -> Any: 

868 """Resolve the default value using either a static default or a default_factory.""" 

869 if default_factory is None: 

870 return smart_deepcopy(default) 

871 if call_default_factory: 

872 if default_factory_takes_validated_data_argument: 

873 fac = cast('Callable[[dict[str, Any]], Any]', default_factory) 

874 if validated_data is None: 

875 raise ValueError( 

876 "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default()'." 

877 ) 

878 return fac(validated_data) 

879 else: 

880 fac = cast('Callable[[], Any]', default_factory) 

881 return fac() 

882 

883 return PydanticUndefined