codekingpro/portable-devtools
114k
1"""Logic for creating models."""2 3# Because `dict` is in the local namespace of the `BaseModel` class, we use `Dict` for annotations.4# TODO v3 fallback to `dict` when the deprecated `dict` method gets removed.5# ruff: noqa: UP0356 7from __future__ import annotations as _annotations8 9import operator10import sys11import types12import warnings13from collections.abc import Generator, Mapping14from copy import copy, deepcopy15from functools import cached_property16from typing import (17 TYPE_CHECKING,18 Any,19 Callable,20 ClassVar,21 Dict,22 Generic,23 Literal,24 TypeVar,25 Union,26 cast,27 overload,28)29 30import pydantic_core31import typing_extensions32from pydantic_core import PydanticUndefined, ValidationError33from typing_extensions import Self, TypeAlias, Unpack34 35from . import PydanticDeprecatedSince20, PydanticDeprecatedSince21136from ._internal import (37 _config,38 _decorators,39 _fields,40 _forward_ref,41 _generics,42 _mock_val_ser,43 _model_construction,44 _namespace_utils,45 _repr,46 _typing_extra,47 _utils,48)49from ._migration import getattr_migration50from .aliases import AliasChoices, AliasPath51from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler52from .config import ConfigDict, ExtraValues53from .errors import PydanticUndefinedAnnotation, PydanticUserError54from .json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema, JsonSchemaMode, JsonSchemaValue, model_json_schema55from .plugin._schema_validator import PluggableSchemaValidator56 57if TYPE_CHECKING:58 from inspect import Signature59 from pathlib import Path60 61 from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator62 63 from ._internal._fields import PydanticExtraInfo64 from ._internal._namespace_utils import MappingNamespace65 from ._internal._utils import AbstractSetIntStr, MappingIntStrAny66 from .deprecated.parse import Protocol as DeprecatedParseProtocol67 from .fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr68 69 70__all__ = 'BaseModel', 'create_model'71 72# Keep these type aliases available at runtime:73TupleGenerator: TypeAlias = Generator[tuple[str, Any], None, None]74# NOTE: In reality, `bool` should be replaced by `Literal[True]` but mypy fails to correctly apply bidirectional75# type inference (e.g. when using `{'a': {'b': True}}`):76# NOTE: Keep this type alias in sync with the stub definition in `pydantic-core`:77IncEx: TypeAlias = Union[set[int], set[str], Mapping[int, Union['IncEx', bool]], Mapping[str, Union['IncEx', bool]]]78 79_object_setattr = _model_construction.object_setattr80 81 82def _check_frozen(model_cls: type[BaseModel], name: str, value: Any) -> None:83 if model_cls.model_config.get('frozen'):84 error_type = 'frozen_instance'85 elif getattr(model_cls.__pydantic_fields__.get(name), 'frozen', False):86 error_type = 'frozen_field'87 else:88 return89 90 raise ValidationError.from_exception_data(91 model_cls.__name__, [{'type': error_type, 'loc': (name,), 'input': value}]92 )93 94 95def _model_field_setattr_handler(model: BaseModel, name: str, val: Any) -> None:96 model.__dict__[name] = val97 model.__pydantic_fields_set__.add(name)98 99 100def _private_setattr_handler(model: BaseModel, name: str, val: Any) -> None:101 if getattr(model, '__pydantic_private__', None) is None:102 # While the attribute should be present at this point, this may not be the case if103 # users do unusual stuff with `model_post_init()` (which is where the `__pydantic_private__`104 # is initialized, by wrapping the user-defined `model_post_init()`), e.g. if they mock105 # the `model_post_init()` call. Ideally we should find a better way to init private attrs.106 object.__setattr__(model, '__pydantic_private__', {})107 model.__pydantic_private__[name] = val # pyright: ignore[reportOptionalSubscript]108 109 110_SIMPLE_SETATTR_HANDLERS: Mapping[str, Callable[[BaseModel, str, Any], None]] = {111 'model_field': _model_field_setattr_handler,112 'validate_assignment': lambda model, name, val: model.__pydantic_validator__.validate_assignment(model, name, val), # pyright: ignore[reportAssignmentType]113 'private': _private_setattr_handler,114 'cached_property': lambda model, name, val: model.__dict__.__setitem__(name, val),115 'extra_known': lambda model, name, val: _object_setattr(model, name, val),116}117 118 119class BaseModel(metaclass=_model_construction.ModelMetaclass):120 """!!! abstract "Usage Documentation"121 [Models](../concepts/models.md)122 123 A base class for creating Pydantic models.124 125 Attributes:126 __class_vars__: The names of the class variables defined on the model.127 __private_attributes__: Metadata about the private attributes of the model.128 __signature__: The synthesized `__init__` [`Signature`][inspect.Signature] of the model.129 130 __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.131 __pydantic_core_schema__: The core schema of the model.132 __pydantic_custom_init__: Whether the model has a custom `__init__` function.133 __pydantic_decorators__: Metadata containing the decorators defined on the model.134 This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.135 __pydantic_generic_metadata__: A dictionary containing metadata about generic Pydantic models.136 The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]137 and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],138 and the `parameter` item maps to the `__parameter__` attribute of generic classes.139 __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.140 __pydantic_post_init__: The name of the post-init method for the model, if defined.141 __pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].142 __pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.143 __pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.144 145 __pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.146 __pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.147 148 __pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]149 is set to `'allow'`.150 __pydantic_fields_set__: The names of fields explicitly set during instantiation.151 __pydantic_private__: Values of private attributes set on the model instance.152 """153 154 # Note: Many of the below class vars are defined in the metaclass, but we define them here for type checking purposes.155 156 model_config: ClassVar[ConfigDict] = ConfigDict()157 """158 Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].159 """160 161 __class_vars__: ClassVar[set[str]]162 """The names of the class variables defined on the model."""163 164 __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]] # noqa: UP006165 """Metadata about the private attributes of the model."""166 167 __signature__: ClassVar[Signature]168 """The synthesized `__init__` [`Signature`][inspect.Signature] of the model."""169 170 __pydantic_complete__: ClassVar[bool] = False171 """Whether model building is completed, or if there are still undefined fields."""172 173 __pydantic_core_schema__: ClassVar[CoreSchema]174 """The core schema of the model."""175 176 __pydantic_custom_init__: ClassVar[bool]177 """Whether the model has a custom `__init__` method."""178 179 # Must be set for `GenerateSchema.model_schema` to work for a plain `BaseModel` annotation.180 __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] = _decorators.DecoratorInfos()181 """Metadata containing the decorators defined on the model.182 This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1."""183 184 __pydantic_generic_metadata__: ClassVar[_generics.PydanticGenericMetadata]185 """A dictionary containing metadata about generic Pydantic models.186 187 The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]188 and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],189 and the `parameter` item maps to the `__parameter__` attribute of generic classes.190 """191 192 __pydantic_parent_namespace__: ClassVar[Dict[str, Any] | None] = None # noqa: UP006193 """Parent namespace of the model, used for automatic rebuilding of models."""194 195 __pydantic_post_init__: ClassVar[None | Literal['model_post_init']]196 """The name of the post-init method for the model, if defined."""197 198 __pydantic_root_model__: ClassVar[bool] = False199 """Whether the model is a [`RootModel`][pydantic.root_model.RootModel]."""200 201 __pydantic_serializer__: ClassVar[SchemaSerializer]202 """The `pydantic-core` `SchemaSerializer` used to dump instances of the model."""203 204 __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator]205 """The `pydantic-core` `SchemaValidator` used to validate instances of the model."""206 207 __pydantic_fields__: ClassVar[Dict[str, FieldInfo]] # noqa: UP006208 """A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.209 This replaces `Model.__fields__` from Pydantic V1.210 """211 212 __pydantic_setattr_handlers__: ClassVar[Dict[str, Callable[[BaseModel, str, Any], None]]] # noqa: UP006213 """`__setattr__` handlers. Memoizing the handlers leads to a dramatic performance improvement in `__setattr__`"""214 215 __pydantic_computed_fields__: ClassVar[Dict[str, ComputedFieldInfo]] # noqa: UP006216 """A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects."""217 218 __pydantic_extra_info__: ClassVar[PydanticExtraInfo | None]219 """A wrapper around the `__pydantic_extra__` annotation, if explicitly annotated on a model.220 221 This is a private attribute, not meant to be used outside Pydantic.222 """223 224 __pydantic_extra__: Dict[str, Any] | None = _model_construction.NoInitField(init=False) # noqa: UP006225 """A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra] is set to `'allow'`."""226 227 __pydantic_fields_set__: set[str] = _model_construction.NoInitField(init=False)228 """The names of fields explicitly set during instantiation."""229 230 __pydantic_private__: Dict[str, Any] | None = _model_construction.NoInitField(init=False) # noqa: UP006231 """Values of private attributes set on the model instance."""232 233 if not TYPE_CHECKING:234 # Prevent `BaseModel` from being instantiated directly235 # (defined in an `if not TYPE_CHECKING` block for clarity and to avoid type checking errors):236 __pydantic_core_schema__ = _mock_val_ser.MockCoreSchema(237 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',238 code='base-model-instantiated',239 )240 __pydantic_validator__ = _mock_val_ser.MockValSer(241 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',242 val_or_ser='validator',243 code='base-model-instantiated',244 )245 __pydantic_serializer__ = _mock_val_ser.MockValSer(246 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',247 val_or_ser='serializer',248 code='base-model-instantiated',249 )250 251 __slots__ = '__dict__', '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__'252 253 def __init__(self, /, **data: Any) -> None:254 """Create a new model by parsing and validating input data from keyword arguments.255 256 Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be257 validated to form a valid model.258 259 `self` is explicitly positional-only to allow `self` as a field name.260 """261 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks262 __tracebackhide__ = True263 validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)264 if self is not validated_self:265 warnings.warn(266 'A custom validator is returning a value other than `self`.\n'267 "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"268 'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',269 stacklevel=2,270 )271 272 # The following line sets a flag that we use to determine when `__init__` gets overridden by the user273 __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess]274 275 @_utils.deprecated_instance_property276 @classmethod277 def model_fields(cls) -> dict[str, FieldInfo]:278 """A mapping of field names to their respective [`FieldInfo`][pydantic.fields.FieldInfo] instances.279 280 !!! warning281 Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3.282 Instead, you should access this attribute from the model class.283 """284 return getattr(cls, '__pydantic_fields__', {})285 286 @_utils.deprecated_instance_property287 @classmethod288 def model_computed_fields(cls) -> dict[str, ComputedFieldInfo]:289 """A mapping of computed field names to their respective [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] instances.290 291 !!! warning292 Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3.293 Instead, you should access this attribute from the model class.294 """295 return getattr(cls, '__pydantic_computed_fields__', {})296 297 @property298 def model_extra(self) -> dict[str, Any] | None:299 """Get extra fields set during validation.300 301 Returns:302 A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`.303 """304 return self.__pydantic_extra__305 306 @property307 def model_fields_set(self) -> set[str]:308 """Returns the set of fields that have been explicitly set on this model instance.309 310 Returns:311 A set of strings representing the fields that have been set,312 i.e. that were not filled from defaults.313 """314 return self.__pydantic_fields_set__315 316 @classmethod317 def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self: # noqa: C901318 """Creates a new instance of the `Model` class with validated data.319 320 Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data.321 Default values are respected, but no other validation is performed.322 323 !!! note324 `model_construct()` generally respects the `model_config.extra` setting on the provided model.325 That is, if `model_config.extra == 'allow'`, then all extra passed values are added to the model instance's `__dict__`326 and `__pydantic_extra__` fields. If `model_config.extra == 'ignore'` (the default), then all extra passed values are ignored.327 Because no validation is performed with a call to `model_construct()`, having `model_config.extra == 'forbid'` does not result in328 an error if extra values are passed, but they will be ignored.329 330 Args:331 _fields_set: A set of field names that were originally explicitly set during instantiation. If provided,332 this is directly used for the [`model_fields_set`][pydantic.BaseModel.model_fields_set] attribute.333 Otherwise, the field names from the `values` argument will be used.334 values: Trusted or pre-validated data dictionary.335 336 Returns:337 A new instance of the `Model` class with validated data.338 """339 m = cls.__new__(cls)340 fields_values: dict[str, Any] = {}341 fields_set = set()342 343 for name, field in cls.__pydantic_fields__.items():344 if field.alias is not None and field.alias in values:345 fields_values[name] = values.pop(field.alias)346 fields_set.add(name)347 348 if (name not in fields_set) and (field.validation_alias is not None):349 validation_aliases: list[str | AliasPath] = (350 field.validation_alias.choices351 if isinstance(field.validation_alias, AliasChoices)352 else [field.validation_alias]353 )354 355 for alias in validation_aliases:356 if isinstance(alias, str) and alias in values:357 fields_values[name] = values.pop(alias)358 fields_set.add(name)359 break360 elif isinstance(alias, AliasPath):361 value = alias.search_dict_for_path(values)362 if value is not PydanticUndefined:363 fields_values[name] = value364 fields_set.add(name)365 break366 367 if name not in fields_set:368 if name in values:369 fields_values[name] = values.pop(name)370 fields_set.add(name)371 elif not field.is_required():372 fields_values[name] = field.get_default(call_default_factory=True, validated_data=fields_values)373 if _fields_set is None:374 _fields_set = fields_set375 376 _extra: dict[str, Any] | None = values if cls.model_config.get('extra') == 'allow' else None377 _object_setattr(m, '__dict__', fields_values)378 _object_setattr(m, '__pydantic_fields_set__', _fields_set)379 if not cls.__pydantic_root_model__:380 _object_setattr(m, '__pydantic_extra__', _extra)381 _object_setattr(m, '__pydantic_private__', None)382 383 if cls.__pydantic_post_init__:384 m.model_post_init(None)385 # update private attributes with values set386 if hasattr(m, '__pydantic_private__') and m.__pydantic_private__ is not None:387 for k, v in values.items():388 if k in m.__private_attributes__:389 m.__pydantic_private__[k] = v390 391 return m392 393 def model_copy(self, *, update: Mapping[str, Any] | None = None, deep: bool = False) -> Self:394 """!!! abstract "Usage Documentation"395 [`model_copy`](../concepts/models.md#model-copy)396 397 Returns a copy of the model.398 399 !!! note400 The underlying instance's [`__dict__`][object.__dict__] attribute is copied. This401 might have unexpected side effects if you store anything in it, on top of the model402 fields (e.g. the value of [cached properties][functools.cached_property]).403 404 Args:405 update: Values to change/add in the new model. Note: the data is not validated406 before creating the new model. You should trust this data.407 deep: Set to `True` to make a deep copy of the model.408 409 Returns:410 New model instance.411 """412 copied = self.__deepcopy__() if deep else self.__copy__()413 if update:414 if self.model_config.get('extra') == 'allow':415 for k, v in update.items():416 if k in self.__pydantic_fields__:417 copied.__dict__[k] = v418 else:419 if copied.__pydantic_extra__ is None:420 copied.__pydantic_extra__ = {}421 copied.__pydantic_extra__[k] = v422 else:423 copied.__dict__.update(update)424 copied.__pydantic_fields_set__.update(update.keys())425 return copied426 427 def model_dump(428 self,429 *,430 mode: Literal['json', 'python'] | str = 'python',431 include: IncEx | None = None,432 exclude: IncEx | None = None,433 context: Any | None = None,434 by_alias: bool | None = None,435 exclude_unset: bool = False,436 exclude_defaults: bool = False,437 exclude_none: bool = False,438 exclude_computed_fields: bool = False,439 round_trip: bool = False,440 warnings: bool | Literal['none', 'warn', 'error'] = True,441 fallback: Callable[[Any], Any] | None = None,442 serialize_as_any: bool = False,443 polymorphic_serialization: bool | None = None,444 ) -> dict[str, Any]:445 """!!! abstract "Usage Documentation"446 [`model_dump`](../concepts/serialization.md#python-mode)447 448 Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.449 450 Args:451 mode: The mode in which `to_python` should run.452 If mode is 'json', the output will only contain JSON serializable types.453 If mode is 'python', the output may contain non-JSON-serializable Python objects.454 include: A set of fields to include in the output.455 exclude: A set of fields to exclude from the output.456 context: Additional context to pass to the serializer.457 by_alias: Whether to use the field's alias in the dictionary key if defined.458 exclude_unset: Whether to exclude fields that have not been explicitly set.459 exclude_defaults: Whether to exclude fields that are set to their default value.460 exclude_none: Whether to exclude fields that have a value of `None`.461 exclude_computed_fields: Whether to exclude computed fields.462 While this can be useful for round-tripping, it is usually recommended to use the dedicated463 `round_trip` parameter instead.464 round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].465 warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,466 "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].467 fallback: A function to call when an unknown value is encountered. If not provided,468 a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.469 serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.470 polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.471 472 Returns:473 A dictionary representation of the model.474 """475 return self.__pydantic_serializer__.to_python(476 self,477 mode=mode,478 by_alias=by_alias,479 include=include,480 exclude=exclude,481 context=context,482 exclude_unset=exclude_unset,483 exclude_defaults=exclude_defaults,484 exclude_none=exclude_none,485 exclude_computed_fields=exclude_computed_fields,486 round_trip=round_trip,487 warnings=warnings,488 fallback=fallback,489 serialize_as_any=serialize_as_any,490 polymorphic_serialization=polymorphic_serialization,491 )492 493 def model_dump_json(494 self,495 *,496 indent: int | None = None,497 ensure_ascii: bool = False,498 include: IncEx | None = None,499 exclude: IncEx | None = None,500 context: Any | None = None,501 by_alias: bool | None = None,502 exclude_unset: bool = False,503 exclude_defaults: bool = False,504 exclude_none: bool = False,505 exclude_computed_fields: bool = False,506 round_trip: bool = False,507 warnings: bool | Literal['none', 'warn', 'error'] = True,508 fallback: Callable[[Any], Any] | None = None,509 serialize_as_any: bool = False,510 polymorphic_serialization: bool | None = None,511 ) -> str:512 """!!! abstract "Usage Documentation"513 [`model_dump_json`](../concepts/serialization.md#json-mode)514 515 Generates a JSON representation of the model using Pydantic's `to_json` method.516 517 Args:518 indent: Indentation to use in the JSON output. If None is passed, the output will be compact.519 ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.520 If `False` (the default), these characters will be output as-is.521 include: Field(s) to include in the JSON output.522 exclude: Field(s) to exclude from the JSON output.523 context: Additional context to pass to the serializer.524 by_alias: Whether to serialize using field aliases.525 exclude_unset: Whether to exclude fields that have not been explicitly set.526 exclude_defaults: Whether to exclude fields that are set to their default value.527 exclude_none: Whether to exclude fields that have a value of `None`.528 exclude_computed_fields: Whether to exclude computed fields.529 While this can be useful for round-tripping, it is usually recommended to use the dedicated530 `round_trip` parameter instead.531 round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].532 warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,533 "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].534 fallback: A function to call when an unknown value is encountered. If not provided,535 a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.536 serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.537 polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.538 539 Returns:540 A JSON string representation of the model.541 """542 return self.__pydantic_serializer__.to_json(543 self,544 indent=indent,545 ensure_ascii=ensure_ascii,546 include=include,547 exclude=exclude,548 context=context,549 by_alias=by_alias,550 exclude_unset=exclude_unset,551 exclude_defaults=exclude_defaults,552 exclude_none=exclude_none,553 exclude_computed_fields=exclude_computed_fields,554 round_trip=round_trip,555 warnings=warnings,556 fallback=fallback,557 serialize_as_any=serialize_as_any,558 polymorphic_serialization=polymorphic_serialization,559 ).decode()560 561 @classmethod562 def model_json_schema(563 cls,564 by_alias: bool = True,565 ref_template: str = DEFAULT_REF_TEMPLATE,566 schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,567 mode: JsonSchemaMode = 'validation',568 *,569 union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',570 ) -> dict[str, Any]:571 """Generates a JSON schema for a model class.572 573 Args:574 by_alias: Whether to use attribute aliases or not.575 ref_template: The reference template.576 union_format: The format to use when combining schemas from unions together. Can be one of:577 578 - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)579 keyword to combine schemas (the default).580 - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)581 keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive582 type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to583 `any_of`.584 schema_generator: To override the logic used to generate the JSON schema, as a subclass of585 `GenerateJsonSchema` with your desired modifications586 mode: The mode in which to generate the schema.587 588 Returns:589 The JSON schema for the given model class.590 """591 return model_json_schema(592 cls,593 by_alias=by_alias,594 ref_template=ref_template,595 union_format=union_format,596 schema_generator=schema_generator,597 mode=mode,598 )599 600 @classmethod601 def model_parametrized_name(cls, params: tuple[type[Any], ...]) -> str:602 """Compute the class name for parametrizations of generic classes.603 604 This method can be overridden to achieve a custom naming scheme for generic BaseModels.605 606 Args:607 params: Tuple of types of the class. Given a generic class608 `Model` with 2 type variables and a concrete model `Model[str, int]`,609 the value `(str, int)` would be passed to `params`.610 611 Returns:612 String representing the new class where `params` are passed to `cls` as type variables.613 614 Raises:615 TypeError: Raised when trying to generate concrete names for non-generic models.616 """617 if not issubclass(cls, Generic):618 raise TypeError('Concrete names should only be generated for generic models.')619 620 # Any strings received should represent forward references, so we handle them specially below.621 # If we eventually move toward wrapping them in a ForwardRef in __class_getitem__ in the future,622 # we may be able to remove this special case.623 param_names = [param if isinstance(param, str) else _repr.display_as_type(param) for param in params]624 params_component = ', '.join(param_names)625 return f'{cls.__name__}[{params_component}]'626 627 def model_post_init(self, context: Any, /) -> None:628 """Override this method to perform additional initialization after `__init__` and `model_construct`.629 This is useful if you want to do some validation that requires the entire model to be initialized.630 """631 632 @classmethod633 def model_rebuild(634 cls,635 *,636 force: bool = False,637 raise_errors: bool = True,638 _parent_namespace_depth: int = 2,639 _types_namespace: MappingNamespace | None = None,640 ) -> bool | None:641 """Try to rebuild the pydantic-core schema for the model.642 643 This may be necessary when one of the annotations is a ForwardRef which could not be resolved during644 the initial attempt to build the schema, and automatic rebuilding fails.645 646 Args:647 force: Whether to force the rebuilding of the model schema, defaults to `False`.648 raise_errors: Whether to raise errors, defaults to `True`.649 _parent_namespace_depth: The depth level of the parent namespace, defaults to 2.650 _types_namespace: The types namespace, defaults to `None`.651 652 Returns:653 Returns `None` if the schema is already "complete" and rebuilding was not required.654 If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.655 """656 already_complete = cls.__pydantic_complete__657 if already_complete and not force:658 return None659 660 cls.__pydantic_complete__ = False661 662 for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'):663 if attr in cls.__dict__ and not isinstance(getattr(cls, attr), _mock_val_ser.MockValSer):664 # Deleting the validator/serializer is necessary as otherwise they can get reused in665 # pydantic-core. We do so only if they aren't mock instances, otherwise — as `model_rebuild()`666 # isn't thread-safe — concurrent model instantiations can lead to the parent validator being used.667 # Same applies for the core schema that can be reused in schema generation.668 delattr(cls, attr)669 670 if _types_namespace is not None:671 rebuild_ns = _types_namespace672 elif _parent_namespace_depth > 0:673 rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}674 else:675 rebuild_ns = {}676 677 parent_ns = _model_construction.unpack_lenient_weakvaluedict(cls.__pydantic_parent_namespace__) or {}678 679 ns_resolver = _namespace_utils.NsResolver(680 parent_namespace={**rebuild_ns, **parent_ns},681 )682 683 return _model_construction.complete_model_class(684 cls,685 _config.ConfigWrapper(cls.model_config, check=False),686 ns_resolver,687 raise_errors=raise_errors,688 # If the model was already complete, we don't need to call the hook again.689 call_on_complete_hook=not already_complete,690 is_force_rebuild=force,691 )692 693 @classmethod694 def model_validate(695 cls,696 obj: Any,697 *,698 strict: bool | None = None,699 extra: ExtraValues | None = None,700 from_attributes: bool | None = None,701 context: Any | None = None,702 by_alias: bool | None = None,703 by_name: bool | None = None,704 ) -> Self:705 """Validate a pydantic model instance.706 707 Args:708 obj: The object to validate.709 strict: Whether to enforce types strictly.710 extra: Whether to ignore, allow, or forbid extra data during model validation.711 See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.712 from_attributes: Whether to extract data from object attributes.713 context: Additional context to pass to the validator.714 by_alias: Whether to use the field's alias when validating against the provided input data.715 by_name: Whether to use the field's name when validating against the provided input data.716 717 Raises:718 ValidationError: If the object could not be validated.719 720 Returns:721 The validated model instance.722 """723 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks724 __tracebackhide__ = True725 726 if by_alias is False and by_name is not True:727 raise PydanticUserError(728 'At least one of `by_alias` or `by_name` must be set to True.',729 code='validate-by-alias-and-name-false',730 )731 732 return cls.__pydantic_validator__.validate_python(733 obj,734 strict=strict,735 extra=extra,736 from_attributes=from_attributes,737 context=context,738 by_alias=by_alias,739 by_name=by_name,740 )741 742 @classmethod743 def model_validate_json(744 cls,745 json_data: str | bytes | bytearray,746 *,747 strict: bool | None = None,748 extra: ExtraValues | None = None,749 context: Any | None = None,750 by_alias: bool | None = None,751 by_name: bool | None = None,752 ) -> Self:753 """!!! abstract "Usage Documentation"754 [JSON Parsing](../concepts/json.md#json-parsing)755 756 Validate the given JSON data against the Pydantic model.757 758 Args:759 json_data: The JSON data to validate.760 strict: Whether to enforce types strictly.761 extra: Whether to ignore, allow, or forbid extra data during model validation.762 See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.763 context: Extra variables to pass to the validator.764 by_alias: Whether to use the field's alias when validating against the provided input data.765 by_name: Whether to use the field's name when validating against the provided input data.766 767 Returns:768 The validated Pydantic model.769 770 Raises:771 ValidationError: If `json_data` is not a JSON string or the object could not be validated.772 """773 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks774 __tracebackhide__ = True775 776 if by_alias is False and by_name is not True:777 raise PydanticUserError(778 'At least one of `by_alias` or `by_name` must be set to True.',779 code='validate-by-alias-and-name-false',780 )781 782 return cls.__pydantic_validator__.validate_json(783 json_data, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name784 )785 786 @classmethod787 def model_validate_strings(788 cls,789 obj: Any,790 *,791 strict: bool | None = None,792 extra: ExtraValues | None = None,793 context: Any | None = None,794 by_alias: bool | None = None,795 by_name: bool | None = None,796 ) -> Self:797 """Validate the given object with string data against the Pydantic model.798 799 Args:800 obj: The object containing string data to validate.801 strict: Whether to enforce types strictly.802 extra: Whether to ignore, allow, or forbid extra data during model validation.803 See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.804 context: Extra variables to pass to the validator.805 by_alias: Whether to use the field's alias when validating against the provided input data.806 by_name: Whether to use the field's name when validating against the provided input data.807 808 Returns:809 The validated Pydantic model.810 """811 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks812 __tracebackhide__ = True813 814 if by_alias is False and by_name is not True:815 raise PydanticUserError(816 'At least one of `by_alias` or `by_name` must be set to True.',817 code='validate-by-alias-and-name-false',818 )819 820 return cls.__pydantic_validator__.validate_strings(821 obj, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name822 )823 824 @classmethod825 def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreSchemaHandler, /) -> CoreSchema:826 # This warning is only emitted when calling `super().__get_pydantic_core_schema__` from a model subclass.827 # In the generate schema logic, this method (`BaseModel.__get_pydantic_core_schema__`) is special cased to828 # *not* be called if not overridden.829 warnings.warn(830 'The `__get_pydantic_core_schema__` method of the `BaseModel` class is deprecated. If you are calling '831 '`super().__get_pydantic_core_schema__` when overriding the method on a Pydantic model, consider using '832 '`handler(source)` instead. However, note that overriding this method on models can lead to unexpected '833 'side effects.',834 PydanticDeprecatedSince211,835 stacklevel=2,836 )837 # Logic copied over from `GenerateSchema._model_schema`:838 schema = cls.__dict__.get('__pydantic_core_schema__')839 if schema is not None and not isinstance(schema, _mock_val_ser.MockCoreSchema):840 return cls.__pydantic_core_schema__841 842 return handler(source)843 844 @classmethod845 def __get_pydantic_json_schema__(846 cls,847 core_schema: CoreSchema,848 handler: GetJsonSchemaHandler,849 /,850 ) -> JsonSchemaValue:851 """Hook into generating the model's JSON schema.852 853 Args:854 core_schema: A `pydantic-core` CoreSchema.855 You can ignore this argument and call the handler with a new CoreSchema,856 wrap this CoreSchema (`{'type': 'nullable', 'schema': current_schema}`),857 or just call the handler with the original schema.858 handler: Call into Pydantic's internal JSON schema generation.859 This will raise a `pydantic.errors.PydanticInvalidForJsonSchema` if JSON schema860 generation fails.861 Since this gets called by `BaseModel.model_json_schema` you can override the862 `schema_generator` argument to that function to change JSON schema generation globally863 for a type.864 865 Returns:866 A JSON schema, as a Python object.867 """868 return handler(core_schema)869 870 @classmethod871 def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:872 """This is intended to behave just like `__init_subclass__`, but is called by `ModelMetaclass`873 only after basic class initialization is complete. In particular, attributes like `model_fields` will874 be present when this is called, but forward annotations are not guaranteed to be resolved yet,875 meaning that creating an instance of the class may fail.876 877 This is necessary because `__init_subclass__` will always be called by `type.__new__`,878 and it would require a prohibitively large refactor to the `ModelMetaclass` to ensure that879 `type.__new__` was called in such a manner that the class would already be sufficiently initialized.880 881 This will receive the same `kwargs` that would be passed to the standard `__init_subclass__`, namely,882 any kwargs passed to the class definition that aren't used internally by Pydantic.883 884 Args:885 **kwargs: Any keyword arguments passed to the class definition that aren't used internally886 by Pydantic.887 888 Note:889 You may want to override [`__pydantic_on_complete__()`][pydantic.main.BaseModel.__pydantic_on_complete__]890 instead, which is called once the class and its fields are fully initialized and ready for validation.891 """892 893 @classmethod894 def __pydantic_on_complete__(cls) -> None:895 """This is called once the class and its fields are fully initialized and ready to be used.896 897 This typically happens when the class is created (just before898 [`__pydantic_init_subclass__()`][pydantic.main.BaseModel.__pydantic_init_subclass__] is called on the superclass),899 except when forward annotations are used that could not immediately be resolved.900 In that case, it will be called later, when the model is rebuilt automatically or explicitly using901 [`model_rebuild()`][pydantic.main.BaseModel.model_rebuild].902 """903 904 def __class_getitem__(905 cls, typevar_values: type[Any] | tuple[type[Any], ...]906 ) -> type[BaseModel] | _forward_ref.PydanticRecursiveRef:907 cached = _generics.get_cached_generic_type_early(cls, typevar_values)908 if cached is not None:909 return cached910 911 if cls is BaseModel:912 raise TypeError('Type parameters should be placed on typing.Generic, not BaseModel')913 if not hasattr(cls, '__parameters__'):914 raise TypeError(f'{cls} cannot be parametrized because it does not inherit from typing.Generic')915 if not cls.__pydantic_generic_metadata__['parameters'] and Generic not in cls.__bases__:916 raise TypeError(f'{cls} is not a generic class')917 918 if not isinstance(typevar_values, tuple):919 typevar_values = (typevar_values,)920 921 # For a model `class Model[T, U, V = int](BaseModel): ...` parametrized with `(str, bool)`,922 # this gives us `{T: str, U: bool, V: int}`:923 typevars_map = _generics.map_generic_model_arguments(cls, typevar_values)924 # We also update the provided args to use defaults values (`(str, bool)` becomes `(str, bool, int)`):925 typevar_values = tuple(v for v in typevars_map.values())926 927 if _utils.all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map:928 submodel = cls # if arguments are equal to parameters it's the same object929 _generics.set_cached_generic_type(cls, typevar_values, submodel)930 else:931 parent_args = cls.__pydantic_generic_metadata__['args']932 if not parent_args:933 args = typevar_values934 else:935 args = tuple(_generics.replace_types(arg, typevars_map) for arg in parent_args)936 937 origin = cls.__pydantic_generic_metadata__['origin'] or cls938 model_name = origin.model_parametrized_name(args)939 params = tuple(940 dict.fromkeys(_generics.iter_contained_typevars(typevars_map.values()))941 ) # use dict as ordered set942 943 with _generics.generic_recursion_self_type(origin, args) as maybe_self_type:944 cached = _generics.get_cached_generic_type_late(cls, typevar_values, origin, args)945 if cached is not None:946 return cached947 948 if maybe_self_type is not None:949 return maybe_self_type950 951 # Attempt to rebuild the origin in case new types have been defined952 try:953 # depth 2 gets you above this __class_getitem__ call.954 # Note that we explicitly provide the parent ns, otherwise955 # `model_rebuild` will use the parent ns no matter if it is the ns of a module.956 # We don't want this here, as this has unexpected effects when a model957 # is being parametrized during a forward annotation evaluation.958 parent_ns = _typing_extra.parent_frame_namespace(parent_depth=2) or {}959 origin.model_rebuild(_types_namespace=parent_ns)960 except PydanticUndefinedAnnotation:961 # It's okay if it fails, it just means there are still undefined types962 # that could be evaluated later.963 pass964 965 submodel = _generics.create_generic_submodel(model_name, origin, args, params)966 967 _generics.set_cached_generic_type(cls, typevar_values, submodel, origin, args)968 969 return submodel970 971 def __copy__(self) -> Self:972 """Returns a shallow copy of the model."""973 cls = type(self)974 m = cls.__new__(cls)975 _object_setattr(m, '__dict__', copy(self.__dict__))976 _object_setattr(m, '__pydantic_extra__', copy(self.__pydantic_extra__))977 _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))978 979 if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:980 _object_setattr(m, '__pydantic_private__', None)981 else:982 _object_setattr(983 m,984 '__pydantic_private__',985 {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined},986 )987 988 return m989 990 def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:991 """Returns a deep copy of the model."""992 cls = type(self)993 m = cls.__new__(cls)994 _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))995 _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo))996 # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],997 # and attempting a deepcopy would be marginally slower.998 _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))999 1000 if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:1001 _object_setattr(m, '__pydantic_private__', None)1002 else:1003 _object_setattr(1004 m,1005 '__pydantic_private__',1006 deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo),1007 )1008 1009 return m1010 1011 if not TYPE_CHECKING:1012 # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access1013 # The same goes for __setattr__ and __delattr__, see: https://github.com/pydantic/pydantic/issues/86431014 1015 def __getattr__(self, item: str) -> Any:1016 private_attributes = object.__getattribute__(self, '__private_attributes__')1017 if item in private_attributes:1018 attribute = private_attributes[item]1019 if hasattr(attribute, '__get__'):1020 return attribute.__get__(self, type(self)) # type: ignore1021 1022 try:1023 # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items1024 return self.__pydantic_private__[item] # type: ignore1025 except KeyError as exc:1026 raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc1027 else:1028 # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.1029 # See `BaseModel.__repr_args__` for more details1030 try:1031 pydantic_extra = object.__getattribute__(self, '__pydantic_extra__')1032 except AttributeError:1033 pydantic_extra = None1034 1035 if pydantic_extra and item in pydantic_extra:1036 return pydantic_extra[item]1037 else:1038 if hasattr(self.__class__, item):1039 return super().__getattribute__(item) # Raises AttributeError if appropriate1040 else:1041 # this is the current error1042 raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')1043 1044 def __setattr__(self, name: str, value: Any) -> None:1045 if (setattr_handler := self.__pydantic_setattr_handlers__.get(name)) is not None:1046 setattr_handler(self, name, value)1047 # if None is returned from _setattr_handler, the attribute was set directly1048 elif (setattr_handler := self._setattr_handler(name, value)) is not None:1049 setattr_handler(self, name, value) # call here to not memo on possibly unknown fields1050 self.__pydantic_setattr_handlers__[name] = setattr_handler # memoize the handler for faster access1051 1052 def _setattr_handler(self, name: str, value: Any) -> Callable[[BaseModel, str, Any], None] | None:1053 """Get a handler for setting an attribute on the model instance.1054 1055 Returns:1056 A handler for setting an attribute on the model instance. Used for memoization of the handler.1057 Memoizing the handlers leads to a dramatic performance improvement in `__setattr__`1058 Returns `None` when memoization is not safe, then the attribute is set directly.1059 """1060 cls = self.__class__1061 if name in cls.__class_vars__:1062 raise AttributeError(1063 f'{name!r} is a ClassVar of `{cls.__name__}` and cannot be set on an instance. '1064 f'If you want to set a value on the class, use `{cls.__name__}.{name} = value`.'1065 )1066 elif not _fields.is_valid_field_name(name):1067 if (attribute := cls.__private_attributes__.get(name)) is not None:1068 if hasattr(attribute, '__set__'):1069 return lambda model, _name, val: attribute.__set__(model, val)1070 else:1071 return _SIMPLE_SETATTR_HANDLERS['private']1072 else:1073 _object_setattr(self, name, value)1074 return None # Can not return memoized handler with possibly freeform attr names1075 1076 attr = getattr(cls, name, None)1077 # NOTE: We currently special case properties and `cached_property`, but we might need1078 # to generalize this to all data/non-data descriptors at some point. For non-data descriptors1079 # (such as `cached_property`), it isn't obvious though. `cached_property` caches the value1080 # to the instance's `__dict__`, but other non-data descriptors might do things differently.1081 if isinstance(attr, cached_property):1082 return _SIMPLE_SETATTR_HANDLERS['cached_property']1083 1084 _check_frozen(cls, name, value)1085 1086 # We allow properties to be set only on non frozen models for now (to match dataclasses).1087 # This can be changed if it ever gets requested.1088 if isinstance(attr, property):1089 return lambda model, _name, val: attr.__set__(model, val)1090 elif cls.model_config.get('validate_assignment'):1091 return _SIMPLE_SETATTR_HANDLERS['validate_assignment']1092 elif name not in cls.__pydantic_fields__:1093 if cls.model_config.get('extra') != 'allow':1094 # TODO - matching error1095 raise ValueError(f'"{cls.__name__}" object has no field "{name}"')1096 elif attr is None:1097 # attribute does not exist, so put it in extra1098 self.__pydantic_extra__[name] = value1099 self.__pydantic_fields_set__.add(name)1100 return None # Can not return memoized handler with possibly freeform attr names1101 else:1102 # attribute _does_ exist, and was not in extra, so update it1103 return _SIMPLE_SETATTR_HANDLERS['extra_known']1104 else:1105 return _SIMPLE_SETATTR_HANDLERS['model_field']1106 1107 def __delattr__(self, item: str) -> Any:1108 cls = self.__class__1109 1110 if item in self.__private_attributes__:1111 attribute = self.__private_attributes__[item]1112 if hasattr(attribute, '__delete__'):1113 attribute.__delete__(self) # type: ignore1114 return1115 1116 try:1117 # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items1118 del self.__pydantic_private__[item] # type: ignore1119 return1120 except KeyError as exc:1121 raise AttributeError(f'{cls.__name__!r} object has no attribute {item!r}') from exc1122 1123 # Allow cached properties to be deleted (even if the class is frozen):1124 attr = getattr(cls, item, None)1125 if isinstance(attr, cached_property):1126 return object.__delattr__(self, item)1127 1128 _check_frozen(cls, name=item, value=None)1129 1130 if item in self.__pydantic_fields__:1131 object.__delattr__(self, item)1132 elif self.__pydantic_extra__ is not None and item in self.__pydantic_extra__:1133 del self.__pydantic_extra__[item]1134 else:1135 try:1136 object.__delattr__(self, item)1137 except AttributeError:1138 raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')1139 1140 # Because we make use of `@dataclass_transform()`, `__replace__` is already synthesized by1141 # type checkers, so we define the implementation in this `if not TYPE_CHECKING:` block:1142 def __replace__(self, **changes: Any) -> Self:1143 return self.model_copy(update=changes)1144 1145 def __getstate__(self) -> dict[Any, Any]:1146 private = self.__pydantic_private__1147 if private:1148 private = {k: v for k, v in private.items() if v is not PydanticUndefined}1149 return {1150 '__dict__': self.__dict__,1151 '__pydantic_extra__': self.__pydantic_extra__,1152 '__pydantic_fields_set__': self.__pydantic_fields_set__,1153 '__pydantic_private__': private,1154 }1155 1156 def __setstate__(self, state: dict[Any, Any]) -> None:1157 _object_setattr(self, '__pydantic_fields_set__', state.get('__pydantic_fields_set__', {}))1158 _object_setattr(self, '__pydantic_extra__', state.get('__pydantic_extra__', {}))1159 _object_setattr(self, '__pydantic_private__', state.get('__pydantic_private__', {}))1160 _object_setattr(self, '__dict__', state.get('__dict__', {}))1161 1162 if not TYPE_CHECKING:1163 1164 def __eq__(self, other: Any) -> bool:1165 if isinstance(other, BaseModel):1166 # When comparing instances of generic types for equality, as long as all field values are equal,1167 # only require their generic origin types to be equal, rather than exact type equality.1168 # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1).1169 self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__1170 other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__1171 1172 # Perform common checks first1173 if not (1174 self_type is other_type1175 and getattr(self, '__pydantic_private__', None) == getattr(other, '__pydantic_private__', None)1176 # We need to assume `None` and `{}` are equivalent, because extra behavior1177 # can be controlled at validation time:1178 and (self.__pydantic_extra__ or {}) == (other.__pydantic_extra__ or {})1179 ):1180 return False1181 1182 # We only want to compare pydantic fields but ignoring fields is costly.1183 # We'll perform a fast check first, and fallback only when needed1184 # See GH-7444 and GH-7825 for rationale and a performance benchmark1185 1186 # First, do the fast (and sometimes faulty) __dict__ comparison1187 if self.__dict__ == other.__dict__:1188 # If the check above passes, then pydantic fields are equal, we can return early1189 return True1190 1191 # We don't want to trigger unnecessary costly filtering of __dict__ on all unequal objects, so we return1192 # early if there are no keys to ignore (we would just return False later on anyway)1193 model_fields = type(self).__pydantic_fields__.keys()1194 if self.__dict__.keys() <= model_fields and other.__dict__.keys() <= model_fields:1195 return False1196 1197 # If we reach here, there are non-pydantic-fields keys, mapped to unequal values, that we need to ignore1198 # Resort to costly filtering of the __dict__ objects1199 # We use operator.itemgetter because it is much faster than dict comprehensions1200 # NOTE: Contrary to standard python class and instances, when the Model class has a default value for an