This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -2,31 +2,36 @@
from __future__ import annotations as _annotations
import typing
from copy import copy, deepcopy
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
from pydantic_core import PydanticUndefined
from typing_extensions import Self, dataclass_transform
from . import PydanticUserError
from ._internal import _repr
from ._internal import _model_construction, _repr
from .main import BaseModel, _object_setattr
if typing.TYPE_CHECKING:
from typing import Any
from typing_extensions import Literal
Model = typing.TypeVar('Model', bound='BaseModel')
if TYPE_CHECKING:
from .fields import Field as PydanticModelField
from .fields import PrivateAttr as PydanticModelPrivateAttr
# dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform
# takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform
# on a new metaclass.
@dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr))
class _RootModelMetaclass(_model_construction.ModelMetaclass): ...
else:
_RootModelMetaclass = _model_construction.ModelMetaclass
__all__ = ('RootModel',)
RootModelRootType = typing.TypeVar('RootModelRootType')
RootModelRootType = TypeVar('RootModelRootType')
class RootModel(BaseModel, typing.Generic[RootModelRootType]):
"""Usage docs: https://docs.pydantic.dev/2.5/concepts/models/#rootmodel-and-custom-root-types
class RootModel(BaseModel, Generic[RootModelRootType], metaclass=_RootModelMetaclass):
"""!!! abstract "Usage Documentation"
[`RootModel` and Custom Root Types](../concepts/models.md#rootmodel-and-custom-root-types)
A Pydantic `BaseModel` for the root object of the model.
@@ -52,7 +57,7 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]):
)
super().__init_subclass__(**kwargs)
def __init__(__pydantic_self__, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore
def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore
__tracebackhide__ = True
if data:
if root is not PydanticUndefined:
@@ -60,12 +65,12 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]):
'"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments'
)
root = data # type: ignore
__pydantic_self__.__pydantic_validator__.validate_python(root, self_instance=__pydantic_self__)
self.__pydantic_validator__.validate_python(root, self_instance=self)
__init__.__pydantic_base_init__ = True
__init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess]
@classmethod
def model_construct(cls: type[Model], root: RootModelRootType, _fields_set: set[str] | None = None) -> Model:
def model_construct(cls, root: RootModelRootType, _fields_set: set[str] | None = None) -> Self: # type: ignore
"""Create a new model using the provided root object and update fields set.
Args:
@@ -90,7 +95,7 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]):
_object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__'])
_object_setattr(self, '__dict__', state['__dict__'])
def __copy__(self: Model) -> Model:
def __copy__(self) -> Self:
"""Returns a shallow copy of the model."""
cls = type(self)
m = cls.__new__(cls)
@@ -98,7 +103,7 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]):
_object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
return m
def __deepcopy__(self: Model, memo: dict[int, Any] | None = None) -> Model:
def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:
"""Returns a deep copy of the model."""
cls = type(self)
m = cls.__new__(cls)
@@ -108,32 +113,43 @@ class RootModel(BaseModel, typing.Generic[RootModelRootType]):
_object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
return m
if typing.TYPE_CHECKING:
if TYPE_CHECKING:
def model_dump(
def model_dump( # type: ignore
self,
*,
mode: Literal['json', 'python'] | str = 'python',
include: Any = None,
exclude: Any = None,
by_alias: bool = False,
context: dict[str, Any] | None = None,
by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool = True,
) -> RootModelRootType:
warnings: bool | Literal['none', 'warn', 'error'] = True,
serialize_as_any: bool = False,
) -> Any:
"""This method is included just to get a more accurate return type for type checkers.
It is included in this `if TYPE_CHECKING:` block since no override is actually necessary.
See the documentation of `BaseModel.model_dump` for more details about the arguments.
Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is
not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return
type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could
even be something different, in the case of a custom serializer.
Thus, `Any` is used here to catch all of these cases.
"""
...
def __eq__(self, other: Any) -> bool:
if not isinstance(other, RootModel):
return NotImplemented
return self.model_fields['root'].annotation == other.model_fields['root'].annotation and super().__eq__(other)
return self.__pydantic_fields__['root'].annotation == other.__pydantic_fields__[
'root'
].annotation and super().__eq__(other)
def __repr_args__(self) -> _repr.ReprArgs:
yield 'root', self.root