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

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

369 statements  

1"""Private logic for creating models.""" 

2 

3from __future__ import annotations as _annotations 

4 

5import operator 

6import sys 

7import typing 

8import warnings 

9import weakref 

10from abc import ABCMeta 

11from functools import cache, partial, wraps 

12from types import FunctionType 

13from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, cast 

14 

15from pydantic_core import PydanticUndefined, SchemaSerializer 

16from typing_extensions import TypeAliasType, dataclass_transform, deprecated, get_args, get_origin 

17from typing_inspection import typing_objects 

18 

19from ..errors import PydanticUndefinedAnnotation, PydanticUserError 

20from ..plugin._schema_validator import create_schema_validator 

21from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20 

22from ._config import ConfigWrapper 

23from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function 

24from ._fields import collect_model_fields, is_valid_field_name, is_valid_privateattr_name, rebuild_model_fields 

25from ._generate_schema import GenerateSchema, InvalidSchemaError 

26from ._generics import PydanticGenericMetadata, get_model_typevars_map 

27from ._import_utils import import_cached_base_model, import_cached_field_info 

28from ._mock_val_ser import set_model_mocks 

29from ._namespace_utils import NsResolver 

30from ._signature import generate_pydantic_signature 

31from ._typing_extra import ( 

32 _make_forward_ref, 

33 eval_type_backport, 

34 is_classvar_annotation, 

35 parent_frame_namespace, 

36) 

37from ._utils import LazyClassAttribute, SafeGetItemProxy 

38 

39if TYPE_CHECKING: 

40 from ..fields import Field as PydanticModelField 

41 from ..fields import FieldInfo, ModelPrivateAttr 

42 from ..fields import PrivateAttr as PydanticModelPrivateAttr 

43 from ..main import BaseModel 

44 from ._fields import PydanticExtraInfo 

45else: 

46 PydanticModelField = object() 

47 PydanticModelPrivateAttr = object() 

48 

49object_setattr = object.__setattr__ 

50 

51 

52class _ModelNamespaceDict(dict): 

53 """A dictionary subclass that intercepts attribute setting on model classes and 

54 warns about overriding of decorators. 

55 """ 

56 

57 def __setitem__(self, k: str, v: object) -> None: 

58 existing: Any = self.get(k, None) 

59 if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy): 

60 warnings.warn( 

61 f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator', 

62 stacklevel=2, 

63 ) 

64 

65 return super().__setitem__(k, v) 

66 

67 

68def NoInitField( 

69 *, 

70 init: Literal[False] = False, 

71) -> Any: 

72 """Only for typing purposes. Used as default value of `__pydantic_fields_set__`, 

73 `__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when 

74 synthesizing the `__init__` signature. 

75 """ 

76 

77 

78# For ModelMetaclass.register(): 

79_T = TypeVar('_T') 

80 

81 

82@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField)) 

83class ModelMetaclass(ABCMeta): 

84 def __new__( 

85 mcs, 

86 cls_name: str, 

87 bases: tuple[type[Any], ...], 

88 namespace: dict[str, Any], 

89 __pydantic_generic_metadata__: PydanticGenericMetadata | None = None, 

90 __pydantic_reset_parent_namespace__: bool = True, 

91 _create_model_module: str | None = None, 

92 **kwargs: Any, 

93 ) -> type: 

94 """Metaclass for creating Pydantic models. 

95 

96 Args: 

97 cls_name: The name of the class to be created. 

98 bases: The base classes of the class to be created. 

99 namespace: The attribute dictionary of the class to be created. 

100 __pydantic_generic_metadata__: Metadata for generic models. 

101 __pydantic_reset_parent_namespace__: Reset parent namespace. 

102 _create_model_module: The module of the class to be created, if created by `create_model`. 

103 **kwargs: Catch-all for any other keyword arguments. 

104 

105 Returns: 

106 The new class created by the metaclass. 

107 """ 

108 # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact 

109 # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__` 

110 # call we're in the middle of is for the `BaseModel` class. 

111 if bases: 

112 raw_annotations: dict[str, Any] 

113 if sys.version_info >= (3, 14): 

114 if ( 

115 '__annotations__' in namespace 

116 ): # `from __future__ import annotations` was used in the model's module 

117 raw_annotations = namespace['__annotations__'] 

118 else: 

119 # See https://docs.python.org/3.14/library/annotationlib.html#using-annotations-in-a-metaclass: 

120 from annotationlib import Format, call_annotate_function, get_annotate_from_class_namespace 

121 

122 if annotate := get_annotate_from_class_namespace(namespace): 

123 raw_annotations = call_annotate_function(annotate, format=Format.FORWARDREF) 

124 else: 

125 raw_annotations = {} 

126 else: 

127 raw_annotations = namespace.get('__annotations__', {}) 

128 

129 base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases) 

130 

131 config_wrapper = ConfigWrapper.for_model(bases, namespace, raw_annotations, kwargs) 

132 namespace['model_config'] = config_wrapper.config_dict 

133 private_attributes = inspect_namespace( 

134 namespace, raw_annotations, config_wrapper.ignored_types, class_vars, base_field_names 

135 ) 

136 if private_attributes or base_private_attributes: 

137 original_model_post_init = get_model_post_init(namespace, bases) 

138 if original_model_post_init is not None: 

139 # if there are private attributes and a model_post_init function, we handle both 

140 

141 @wraps(original_model_post_init) 

142 def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: 

143 """We need to both initialize private attributes and call the user-defined model_post_init 

144 method. 

145 """ 

146 init_private_attributes(self, context) 

147 original_model_post_init(self, context) 

148 

149 namespace['model_post_init'] = wrapped_model_post_init 

150 else: 

151 namespace['model_post_init'] = init_private_attributes 

152 

153 namespace['__class_vars__'] = class_vars 

154 namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes} 

155 

156 cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs)) 

157 BaseModel_ = import_cached_base_model() 

158 

159 mro = cls.__mro__ 

160 if Generic in mro and mro.index(Generic) < mro.index(BaseModel_): 

161 warnings.warn( 

162 GenericBeforeBaseModelWarning( 

163 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) ' 

164 'for pydantic generics to work properly.' 

165 ), 

166 stacklevel=2, 

167 ) 

168 

169 cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False) 

170 cls.__pydantic_post_init__ = ( 

171 None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init' 

172 ) 

173 

174 cls.__pydantic_setattr_handlers__ = {} 

175 

176 cls.__pydantic_decorators__ = DecoratorInfos.build(cls, replace_wrapped_methods=True) 

177 cls.__pydantic_decorators__.update_from_config(config_wrapper) 

178 

179 # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class 

180 if __pydantic_generic_metadata__: 

181 cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__ 

182 else: 

183 parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ()) 

184 parameters = getattr(cls, '__parameters__', None) or parent_parameters 

185 if parameters and parent_parameters and not all(x in parameters for x in parent_parameters): 

186 from ..root_model import RootModelRootType 

187 

188 missing_parameters = tuple(x for x in parameters if x not in parent_parameters) 

189 if RootModelRootType in parent_parameters and RootModelRootType not in parameters: 

190 # This is a special case where the user has subclassed RootModel, but has not parameterized 

191 # RootModel with the generic type identifiers being used. Ex: 

192 # class MyModel(RootModel, Generic[T]): 

193 # root: T 

194 # Should instead just be: 

195 # class MyModel(RootModel[T]): 

196 # root: T 

197 parameters_str = ', '.join([x.__name__ for x in missing_parameters]) 

198 error_message = ( 

199 f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) ' 

200 f'{parameters_str} in its parameters. ' 

201 f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.' 

202 ) 

203 else: 

204 combined_parameters = parent_parameters + missing_parameters 

205 parameters_str = ', '.join([str(x) for x in combined_parameters]) 

206 generic_type_label = f'typing.Generic[{parameters_str}]' 

207 error_message = ( 

208 f'All parameters must be present on typing.Generic;' 

209 f' you should inherit from {generic_type_label}.' 

210 ) 

211 if Generic not in bases: # pragma: no cover 

212 # We raise an error here not because it is desirable, but because some cases are mishandled. 

213 # It would be nice to remove this error and still have things behave as expected, it's just 

214 # challenging because we are using a custom `__class_getitem__` to parametrize generic models, 

215 # and not returning a typing._GenericAlias from it. 

216 bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label]) 

217 error_message += ( 

218 f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)' 

219 ) 

220 raise TypeError(error_message) 

221 

222 cls.__pydantic_generic_metadata__ = { 

223 'origin': None, 

224 'args': (), 

225 'parameters': parameters, 

226 } 

227 

228 cls.__pydantic_complete__ = False # Ensure this specific class gets completed 

229 

230 # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487 

231 # for attributes not in `new_namespace` (e.g. private attributes) 

232 for name, obj in private_attributes.items(): 

233 obj.__set_name__(cls, name) 

234 

235 if __pydantic_reset_parent_namespace__: 

236 cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace()) 

237 parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None) 

238 if isinstance(parent_namespace, dict): 

239 parent_namespace = unpack_lenient_weakvaluedict(parent_namespace) 

240 

241 ns_resolver = NsResolver(parent_namespace=parent_namespace) 

242 

243 set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver) 

244 

245 # This is also set in `complete_model_class()`, after schema gen because they are recreated. 

246 # We set them here as well for backwards compatibility: 

247 cls.__pydantic_computed_fields__ = { 

248 k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items() 

249 } 

250 

251 if config_wrapper.defer_build: 

252 set_model_mocks(cls) 

253 else: 

254 # Any operation that requires accessing the field infos instances should be put inside 

255 # `complete_model_class()`: 

256 complete_model_class( 

257 cls, 

258 config_wrapper, 

259 ns_resolver, 

260 raise_errors=False, 

261 create_model_module=_create_model_module, 

262 ) 

263 

264 if config_wrapper.frozen and '__hash__' not in namespace: 

265 set_default_hash_func(cls, bases) 

266 

267 # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__ 

268 # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is 

269 # only hit for _proper_ subclasses of BaseModel 

270 super(cls, cls).__pydantic_init_subclass__(**kwargs) # type: ignore[misc] 

271 return cls 

272 else: 

273 # These are instance variables, but have been assigned to `NoInitField` to trick the type checker. 

274 for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__': 

275 namespace.pop( 

276 instance_slot, 

277 None, # In case the metaclass is used with a class other than `BaseModel`. 

278 ) 

279 namespace.get('__annotations__', {}).clear() 

280 return super().__new__(mcs, cls_name, bases, namespace, **kwargs) 

281 

282 if not TYPE_CHECKING: # pragma: no branch 

283 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access 

284 

285 def __getattr__(self, item: str) -> Any: 

286 """This is necessary to keep attribute access working for class attribute access.""" 

287 private_attributes = self.__dict__.get('__private_attributes__') 

288 if private_attributes and item in private_attributes: 

289 return private_attributes[item] 

290 raise AttributeError(item) 

291 

292 @classmethod 

293 def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]: 

294 return _ModelNamespaceDict() 

295 

296 # Due to performance and memory issues, in the ABCMeta.__subclasscheck__ implementation, we don't support 

297 # registered virtual subclasses. See https://github.com/python/cpython/issues/92810#issuecomment-2762454345. 

298 # This may change once CPython is fixed (possibly in 3.15), in which case we should conditionally 

299 # define `register()`. 

300 def register(self, subclass: type[_T]) -> type[_T]: 

301 warnings.warn( 

302 f"For performance reasons, virtual subclasses registered using '{self.__qualname__}.register()' " 

303 "are not supported in 'isinstance()' and 'issubclass()' checks.", 

304 stacklevel=2, 

305 ) 

306 return super().register(subclass) 

307 

308 __instancecheck__ = type.__instancecheck__ # pyright: ignore[reportAssignmentType] 

309 __subclasscheck__ = type.__subclasscheck__ # pyright: ignore[reportAssignmentType] 

310 

311 @staticmethod 

312 def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]: 

313 BaseModel = import_cached_base_model() 

314 

315 field_names: set[str] = set() 

316 class_vars: set[str] = set() 

317 private_attributes: dict[str, ModelPrivateAttr] = {} 

318 for base in bases: 

319 if issubclass(base, BaseModel) and base is not BaseModel: 

320 # model_fields might not be defined yet in the case of generics, so we use getattr here: 

321 field_names.update(getattr(base, '__pydantic_fields__', {}).keys()) 

322 class_vars.update(base.__class_vars__) 

323 private_attributes.update(base.__private_attributes__) 

324 return field_names, class_vars, private_attributes 

325 

326 @property 

327 @deprecated( 

328 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None 

329 ) 

330 def __fields__(self) -> dict[str, FieldInfo]: 

331 warnings.warn( 

332 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', 

333 PydanticDeprecatedSince20, 

334 stacklevel=2, 

335 ) 

336 return getattr(self, '__pydantic_fields__', {}) 

337 

338 @property 

339 def __pydantic_fields_complete__(self) -> bool: 

340 """Whether the fields were successfully collected (i.e. type hints were successfully resolved). 

341 

342 This is a private attribute, not meant to be used outside Pydantic. 

343 """ 

344 if '__pydantic_fields__' not in self.__dict__: 

345 return False 

346 

347 field_infos = cast('dict[str, FieldInfo]', self.__pydantic_fields__) # pyright: ignore[reportAttributeAccessIssue] 

348 

349 pydantic_extra_info = cast('PydanticExtraInfo | None', self.__pydantic_extra_info__) # pyright: ignore[reportAttributeAccessIssue] 

350 if pydantic_extra_info is not None: 

351 extra_complete = pydantic_extra_info.complete 

352 else: 

353 extra_complete = True 

354 

355 return all(field_info._complete for field_info in field_infos.values()) and extra_complete 

356 

357 def __dir__(self) -> list[str]: 

358 attributes = list(super().__dir__()) 

359 if '__fields__' in attributes: 

360 attributes.remove('__fields__') 

361 return attributes 

362 

363 

364def init_private_attributes(self: BaseModel, context: Any, /) -> None: 

365 """This function is meant to behave like a BaseModel method to initialize private attributes. 

366 

367 It takes context as an argument since that's what pydantic-core passes when calling it. 

368 

369 Args: 

370 self: The BaseModel instance. 

371 context: The context. 

372 """ 

373 if getattr(self, '__pydantic_private__', None) is None: 

374 pydantic_private = {} 

375 for name, private_attr in self.__private_attributes__.items(): 

376 # Avoid needlessly creating a new dict for the validated data: 

377 if private_attr.default_factory_takes_validated_data: 

378 default = private_attr.get_default( 

379 call_default_factory=True, validated_data={**self.__dict__, **pydantic_private} 

380 ) 

381 else: 

382 default = private_attr.get_default(call_default_factory=True) 

383 if default is not PydanticUndefined: 

384 pydantic_private[name] = default 

385 object_setattr(self, '__pydantic_private__', pydantic_private) 

386 

387 

388def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None: 

389 """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined.""" 

390 if 'model_post_init' in namespace: 

391 return namespace['model_post_init'] 

392 

393 BaseModel = import_cached_base_model() 

394 

395 model_post_init = get_attribute_from_bases(bases, 'model_post_init') 

396 if model_post_init is not BaseModel.model_post_init: 

397 return model_post_init 

398 

399 

400def inspect_namespace( # noqa C901 

401 namespace: dict[str, Any], 

402 raw_annotations: dict[str, Any], 

403 ignored_types: tuple[type[Any], ...], 

404 base_class_vars: set[str], 

405 base_class_fields: set[str], 

406) -> dict[str, ModelPrivateAttr]: 

407 """Iterate over the namespace and: 

408 * gather private attributes 

409 * check for items which look like fields but are not (e.g. have no annotation) and warn. 

410 

411 Args: 

412 namespace: The attribute dictionary of the class to be created. 

413 raw_annotations: The (non-evaluated) annotations of the model. 

414 ignored_types: A tuple of ignore types. 

415 base_class_vars: A set of base class class variables. 

416 base_class_fields: A set of base class fields. 

417 

418 Returns: 

419 A dict containing private attributes info. 

420 

421 Raises: 

422 TypeError: If there is a `__root__` field in model. 

423 NameError: If private attribute name is invalid. 

424 PydanticUserError: 

425 - If a field does not have a type annotation. 

426 - If a field on base class was overridden by a non-annotated attribute. 

427 """ 

428 from ..fields import ModelPrivateAttr, PrivateAttr 

429 

430 FieldInfo = import_cached_field_info() 

431 

432 all_ignored_types = ignored_types + default_ignored_types() 

433 

434 private_attributes: dict[str, ModelPrivateAttr] = {} 

435 

436 if '__root__' in raw_annotations or '__root__' in namespace: 

437 raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'") 

438 

439 ignored_names: set[str] = set() 

440 for var_name, value in list(namespace.items()): 

441 if var_name == 'model_config' or var_name == '__pydantic_extra__': 

442 continue 

443 elif ( 

444 isinstance(value, type) 

445 and value.__module__ == namespace['__module__'] 

446 and '__qualname__' in namespace 

447 and value.__qualname__.startswith(f'{namespace["__qualname__"]}.') 

448 ): 

449 # `value` is a nested type defined in this namespace; don't error 

450 continue 

451 elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools': 

452 ignored_names.add(var_name) 

453 continue 

454 elif isinstance(value, ModelPrivateAttr): 

455 if var_name.startswith('__'): 

456 raise NameError( 

457 'Private attributes must not use dunder names;' 

458 f' use a single underscore prefix instead of {var_name!r}.' 

459 ) 

460 elif is_valid_field_name(var_name): 

461 raise NameError( 

462 'Private attributes must not use valid field names;' 

463 f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.' 

464 ) 

465 private_attributes[var_name] = value 

466 del namespace[var_name] 

467 elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name): 

468 suggested_name = var_name.lstrip('_') or 'my_field' # don't suggest '' for all-underscore name 

469 raise NameError( 

470 f'Fields must not use names with leading underscores;' 

471 f' e.g., use {suggested_name!r} instead of {var_name!r}.' 

472 ) 

473 

474 elif var_name.startswith('__'): 

475 continue 

476 elif is_valid_privateattr_name(var_name): 

477 if var_name not in raw_annotations or not is_classvar_annotation(raw_annotations[var_name]): 

478 private_attributes[var_name] = cast(ModelPrivateAttr, PrivateAttr(default=value)) 

479 del namespace[var_name] 

480 elif var_name in base_class_vars: 

481 continue 

482 elif var_name not in raw_annotations: 

483 if var_name in base_class_fields: 

484 raise PydanticUserError( 

485 f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. ' 

486 f'All field definitions, including overrides, require a type annotation.', 

487 code='model-field-overridden', 

488 ) 

489 elif isinstance(value, FieldInfo): 

490 raise PydanticUserError( 

491 f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation' 

492 ) 

493 else: 

494 raise PydanticUserError( 

495 f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a ' 

496 f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this ' 

497 f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.", 

498 code='model-field-missing-annotation', 

499 ) 

500 

501 for ann_name, ann_type in raw_annotations.items(): 

502 if ( 

503 is_valid_privateattr_name(ann_name) 

504 and ann_name not in private_attributes 

505 and ann_name not in ignored_names 

506 # This condition can be a false negative when `ann_type` is stringified, 

507 # but it is handled in most cases in `set_model_fields`: 

508 and not is_classvar_annotation(ann_type) 

509 and ann_type not in all_ignored_types 

510 and getattr(ann_type, '__module__', None) != 'functools' 

511 ): 

512 if isinstance(ann_type, str): 

513 # Walking up the frames to get the module namespace where the model is defined 

514 # (as the model class wasn't created yet, we unfortunately can't use `cls.__module__`): 

515 frame = sys._getframe(2) 

516 if frame is not None: 

517 try: 

518 ann_type = eval_type_backport( 

519 _make_forward_ref(ann_type, is_argument=False, is_class=True), 

520 globalns=frame.f_globals, 

521 localns=frame.f_locals, 

522 ) 

523 except (NameError, TypeError): 

524 pass 

525 

526 if typing_objects.is_annotated(get_origin(ann_type)): 

527 _, *metadata = get_args(ann_type) 

528 private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None) 

529 if private_attr is not None: 

530 private_attributes[ann_name] = private_attr 

531 continue 

532 private_attributes[ann_name] = PrivateAttr() 

533 

534 return private_attributes 

535 

536 

537def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None: 

538 base_hash_func = get_attribute_from_bases(bases, '__hash__') 

539 new_hash_func = make_hash_func(cls) 

540 if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__: 

541 # If `__hash__` is some default, we generate a hash function. 

542 # It will be `None` if not overridden from BaseModel. 

543 # It may be `object.__hash__` if there is another 

544 # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`). 

545 # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model. 

546 # In the last case we still need a new hash function to account for new `model_fields`. 

547 cls.__hash__ = new_hash_func 

548 

549 

550def make_hash_func(cls: type[BaseModel]) -> Any: 

551 getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0 

552 

553 def hash_func(self: Any) -> int: 

554 try: 

555 return hash(getter(self.__dict__)) 

556 except KeyError: 

557 # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain 

558 # all model fields, which is how we can get here. 

559 # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys, 

560 # and wrapping it in a `try` doesn't slow things down much in the common case. 

561 return hash(getter(SafeGetItemProxy(self.__dict__))) 

562 

563 return hash_func 

564 

565 

566def set_model_fields( 

567 cls: type[BaseModel], 

568 config_wrapper: ConfigWrapper, 

569 ns_resolver: NsResolver, 

570) -> None: 

571 """Collect and set `cls.__pydantic_fields__` and `cls.__class_vars__`. 

572 

573 Args: 

574 cls: BaseModel or dataclass. 

575 config_wrapper: The config wrapper instance. 

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

577 """ 

578 typevars_map = get_model_typevars_map(cls) 

579 fields, pydantic_extra_info, class_vars = collect_model_fields( 

580 cls, config_wrapper, ns_resolver, typevars_map=typevars_map 

581 ) 

582 

583 cls.__pydantic_fields__ = fields 

584 cls.__pydantic_extra_info__ = pydantic_extra_info 

585 cls.__class_vars__.update(class_vars) 

586 

587 for k in class_vars: 

588 # Class vars should not be private attributes 

589 # We remove them _here_ and not earlier because we rely on inspecting the class to determine its classvars, 

590 # but private attributes are determined by inspecting the namespace _prior_ to class creation. 

591 # In the case that a classvar with a leading-'_' is defined via a ForwardRef (e.g., when using 

592 # `__future__.annotations`), we want to remove the private attribute which was detected _before_ we knew it 

593 # evaluated to a classvar 

594 

595 value = cls.__private_attributes__.pop(k, None) 

596 if value is not None and value.default is not PydanticUndefined: 

597 setattr(cls, k, value.default) 

598 

599 

600def complete_model_class( 

601 cls: type[BaseModel], 

602 config_wrapper: ConfigWrapper, 

603 ns_resolver: NsResolver, 

604 *, 

605 raise_errors: bool = True, 

606 call_on_complete_hook: bool = True, 

607 create_model_module: str | None = None, 

608 is_force_rebuild: bool = False, 

609) -> bool: 

610 """Finish building a model class. 

611 

612 This logic must be called after class has been created since validation functions must be bound 

613 and `get_type_hints` requires a class object. 

614 

615 Args: 

616 cls: BaseModel or dataclass. 

617 config_wrapper: The config wrapper instance. 

618 ns_resolver: The namespace resolver instance to use during schema building. 

619 raise_errors: Whether to raise errors. 

620 call_on_complete_hook: Whether to call the `__pydantic_on_complete__` hook. 

621 create_model_module: The module of the class to be created, if created by `create_model`. 

622 is_force_rebuild: Whether the model is being force-rebuilt (if True, pre-built serializers and 

623 validators are not used, to avoid stale references). 

624 

625 Returns: 

626 `True` if the model is successfully completed, else `False`. 

627 

628 Raises: 

629 PydanticUndefinedAnnotation: If PydanticUndefinedAnnotation occurs in __get_pydantic_core_schema__ 

630 and `raise_errors=True`. 

631 """ 

632 typevars_map = get_model_typevars_map(cls) 

633 

634 if not cls.__pydantic_fields_complete__: 

635 # Note: when coming from `ModelMetaclass.__new__()`, this results in fields being built twice. 

636 # We do so a second time here so that we can get the ``NameError`` for the specific undefined annotation. 

637 # Alternatively, we could let `GenerateSchema()` raise the error, but there are cases where incomplete 

638 # fields are inherited in `collect_model_fields()` and can actually have their annotation resolved in the 

639 # generate schema process. As we want to avoid having `__pydantic_fields_complete__` set to `False` 

640 # when `__pydantic_complete__` is `True`, we rebuild here: 

641 try: 

642 cls.__pydantic_fields__, cls.__pydantic_extra_info__ = rebuild_model_fields( 

643 cls, 

644 config_wrapper=config_wrapper, 

645 ns_resolver=ns_resolver, 

646 typevars_map=typevars_map, 

647 ) 

648 except NameError as e: 

649 exc = PydanticUndefinedAnnotation.from_name_error(e) 

650 set_model_mocks(cls, f'`{exc.name}`') 

651 if raise_errors: 

652 raise exc from e 

653 

654 if not raise_errors and not cls.__pydantic_fields_complete__: 

655 # No need to continue with schema gen, it is guaranteed to fail 

656 return False 

657 

658 assert cls.__pydantic_fields_complete__ 

659 

660 gen_schema = GenerateSchema( 

661 config_wrapper, 

662 ns_resolver, 

663 typevars_map, 

664 ) 

665 

666 try: 

667 schema = gen_schema.generate_schema(cls) 

668 except PydanticUndefinedAnnotation as e: 

669 if raise_errors: 

670 raise 

671 set_model_mocks(cls, f'`{e.name}`') 

672 return False 

673 

674 core_config = config_wrapper.core_config(title=cls.__name__) 

675 

676 try: 

677 schema = gen_schema.clean_schema(schema) 

678 except InvalidSchemaError: 

679 set_model_mocks(cls) 

680 return False 

681 

682 # This needs to happen *after* model schema generation, as the return types 

683 # of the properties are evaluated and the `ComputedFieldInfo` are recreated: 

684 cls.__pydantic_computed_fields__ = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()} 

685 

686 set_deprecated_descriptors(cls) 

687 

688 cls.__pydantic_core_schema__ = schema 

689 

690 cls.__pydantic_validator__ = create_schema_validator( 

691 schema, 

692 cls, 

693 create_model_module or cls.__module__, 

694 cls.__qualname__, 

695 'create_model' if create_model_module else 'BaseModel', 

696 core_config, 

697 config_wrapper.plugin_settings, 

698 _use_prebuilt=not is_force_rebuild, 

699 ) 

700 cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config, _use_prebuilt=not is_force_rebuild) 

701 

702 # set __signature__ attr only for model class, but not for its instances 

703 # (because instances can define `__call__`, and `inspect.signature` shouldn't 

704 # use the `__signature__` attribute and instead generate from `__call__`). 

705 cls.__signature__ = LazyClassAttribute( 

706 '__signature__', 

707 partial( 

708 generate_pydantic_signature, 

709 init=cls.__init__, 

710 fields=cls.__pydantic_fields__, 

711 validate_by_name=config_wrapper.validate_by_name, 

712 extra=config_wrapper.extra, 

713 ), 

714 ) 

715 

716 cls.__pydantic_complete__ = True 

717 

718 if call_on_complete_hook: 

719 cls.__pydantic_on_complete__() 

720 

721 return True 

722 

723 

724def set_deprecated_descriptors(cls: type[BaseModel]) -> None: 

725 """Set data descriptors on the class for deprecated fields.""" 

726 for field, field_info in cls.__pydantic_fields__.items(): 

727 if (msg := field_info.deprecation_message) is not None: 

728 desc = _DeprecatedFieldDescriptor(msg) 

729 desc.__set_name__(cls, field) 

730 setattr(cls, field, desc) 

731 

732 for field, computed_field_info in cls.__pydantic_computed_fields__.items(): 

733 if ( 

734 (msg := computed_field_info.deprecation_message) is not None 

735 # Avoid having two warnings emitted: 

736 and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__') 

737 ): 

738 desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property) 

739 desc.__set_name__(cls, field) 

740 setattr(cls, field, desc) 

741 

742 

743class _DeprecatedFieldDescriptor: 

744 """Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field. 

745 

746 Attributes: 

747 msg: The deprecation message to be emitted. 

748 wrapped_property: The property instance if the deprecated field is a computed field, or `None`. 

749 field_name: The name of the field being deprecated. 

750 """ 

751 

752 field_name: str 

753 

754 def __init__(self, msg: str, wrapped_property: property | None = None) -> None: 

755 self.msg = msg 

756 self.wrapped_property = wrapped_property 

757 

758 def __set_name__(self, cls: type[BaseModel], name: str) -> None: 

759 self.field_name = name 

760 

761 def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any: 

762 if obj is None: 

763 if self.wrapped_property is not None: 

764 return self.wrapped_property.__get__(None, obj_type) 

765 raise AttributeError(self.field_name) 

766 

767 warnings.warn(self.msg, DeprecationWarning, stacklevel=2) 

768 

769 if self.wrapped_property is not None: 

770 return self.wrapped_property.__get__(obj, obj_type) 

771 return obj.__dict__[self.field_name] 

772 

773 # Defined to make it a data descriptor and take precedence over the instance's dictionary. 

774 # Note that it will not be called when setting a value on a model instance 

775 # as `BaseModel.__setattr__` is defined and takes priority. 

776 def __set__(self, obj: Any, value: Any) -> NoReturn: 

777 raise AttributeError(self.field_name) 

778 

779 

780class _PydanticWeakRef: 

781 """Wrapper for `weakref.ref` that enables `pickle` serialization. 

782 

783 Cloudpickle fails to serialize weakref.ref objects due to an arcane error related to 

784 to abstract base classes (`abc.ABC`). This class works around the issue by wrapping 

785 `weakref.ref` instead of subclassing it. 

786 

787 See https://github.com/pydantic/pydantic/issues/6763 for context. 

788 

789 Semantics: 

790 - If not pickled, behaves the same as a `weakref.ref`. 

791 - If pickled along with the referenced object, the same `weakref.ref` behavior 

792 will be maintained between them after unpickling. 

793 - If pickled without the referenced object, after unpickling the underlying 

794 reference will be cleared (`__call__` will always return `None`). 

795 """ 

796 

797 def __init__(self, obj: Any): 

798 if obj is None: 

799 # The object will be `None` upon deserialization if the serialized weakref 

800 # had lost its underlying object. 

801 self._wr = None 

802 else: 

803 self._wr = weakref.ref(obj) 

804 

805 def __call__(self) -> Any: 

806 if self._wr is None: 

807 return None 

808 else: 

809 return self._wr() 

810 

811 def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]: 

812 return _PydanticWeakRef, (self(),) 

813 

814 

815def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None: 

816 """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs. 

817 

818 We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values 

819 in a WeakValueDictionary. 

820 

821 The `unpack_lenient_weakvaluedict` function can be used to reverse this operation. 

822 """ 

823 if d is None: 

824 return None 

825 result = {} 

826 for k, v in d.items(): 

827 try: 

828 proxy = _PydanticWeakRef(v) 

829 except TypeError: 

830 proxy = v 

831 result[k] = proxy 

832 return result 

833 

834 

835def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None: 

836 """Inverts the transform performed by `build_lenient_weakvaluedict`.""" 

837 if d is None: 

838 return None 

839 

840 result = {} 

841 for k, v in d.items(): 

842 if isinstance(v, _PydanticWeakRef): 

843 v = v() 

844 if v is not None: 

845 result[k] = v 

846 else: 

847 result[k] = v 

848 return result 

849 

850 

851@cache 

852def default_ignored_types() -> tuple[type[Any], ...]: 

853 from ..fields import ComputedFieldInfo 

854 

855 ignored_types = [ 

856 FunctionType, 

857 property, 

858 classmethod, 

859 staticmethod, 

860 PydanticDescriptorProxy, 

861 ComputedFieldInfo, 

862 TypeAliasType, # from `typing_extensions` 

863 ] 

864 

865 if sys.version_info >= (3, 12): 

866 ignored_types.append(typing.TypeAliasType) 

867 

868 return tuple(ignored_types)