codekingpro/portable-devtools
114k
1"""RootModel class and type definitions."""2 3from __future__ import annotations as _annotations4 5from copy import copy, deepcopy6from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar7 8from pydantic_core import PydanticUndefined9from typing_extensions import Self, dataclass_transform10 11from . import PydanticUserError12from ._internal import _model_construction, _repr13from .main import BaseModel, _object_setattr14 15if TYPE_CHECKING:16 from .fields import Field as PydanticModelField17 from .fields import PrivateAttr as PydanticModelPrivateAttr18 19 # dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform20 # takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform21 # on a new metaclass.22 @dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr))23 class _RootModelMetaclass(_model_construction.ModelMetaclass): ...24else:25 _RootModelMetaclass = _model_construction.ModelMetaclass26 27__all__ = ('RootModel',)28 29RootModelRootType = TypeVar('RootModelRootType')30 31 32class RootModel(BaseModel, Generic[RootModelRootType], metaclass=_RootModelMetaclass):33 """!!! abstract "Usage Documentation"34 [`RootModel` and Custom Root Types](../concepts/models.md#rootmodel-and-custom-root-types)35 36 A Pydantic `BaseModel` for the root object of the model.37 38 Attributes:39 root: The root object of the model.40 __pydantic_root_model__: Whether the model is a RootModel.41 __pydantic_private__: Private fields in the model.42 __pydantic_extra__: Extra fields in the model.43 44 """45 46 __pydantic_root_model__ = True47 __pydantic_private__ = None48 __pydantic_extra__ = None49 50 root: RootModelRootType51 52 def __init_subclass__(cls, **kwargs):53 extra = cls.model_config.get('extra')54 if extra is not None:55 raise PydanticUserError(56 "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra'57 )58 super().__init_subclass__(**kwargs)59 60 def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore61 __tracebackhide__ = True62 if data:63 if root is not PydanticUndefined:64 raise ValueError(65 '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments'66 )67 root = data # type: ignore68 self.__pydantic_validator__.validate_python(root, self_instance=self)69 70 __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess]71 72 @classmethod73 def model_construct(cls, root: RootModelRootType, _fields_set: set[str] | None = None) -> Self: # type: ignore74 """Create a new model using the provided root object and update fields set.75 76 Args:77 root: The root object of the model.78 _fields_set: The set of fields to be updated.79 80 Returns:81 The new model.82 83 Raises:84 NotImplemented: If the model is not a subclass of `RootModel`.85 """86 return super().model_construct(root=root, _fields_set=_fields_set)87 88 def __getstate__(self) -> dict[Any, Any]:89 return {90 '__dict__': self.__dict__,91 '__pydantic_fields_set__': self.__pydantic_fields_set__,92 }93 94 def __setstate__(self, state: dict[Any, Any]) -> None:95 _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__'])96 _object_setattr(self, '__dict__', state['__dict__'])97 98 def __copy__(self) -> Self:99 """Returns a shallow copy of the model."""100 cls = type(self)101 m = cls.__new__(cls)102 new_dict = copy(self.__dict__)103 new_dict['root'] = copy(self.__dict__['root'])104 _object_setattr(m, '__dict__', new_dict)105 _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))106 return m107 108 def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:109 """Returns a deep copy of the model."""110 cls = type(self)111 m = cls.__new__(cls)112 _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))113 # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],114 # and attempting a deepcopy would be marginally slower.115 _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))116 return m117 118 if TYPE_CHECKING:119 120 def model_dump( # type: ignore121 self,122 *,123 mode: Literal['json', 'python'] | str = 'python',124 include: Any = None,125 exclude: Any = None,126 context: dict[str, Any] | None = None,127 by_alias: bool | None = None,128 exclude_unset: bool = False,129 exclude_defaults: bool = False,130 exclude_none: bool = False,131 exclude_computed_fields: bool = False,132 round_trip: bool = False,133 warnings: bool | Literal['none', 'warn', 'error'] = True,134 serialize_as_any: bool = False,135 ) -> Any:136 """This method is included just to get a more accurate return type for type checkers.137 It is included in this `if TYPE_CHECKING:` block since no override is actually necessary.138 139 See the documentation of `BaseModel.model_dump` for more details about the arguments.140 141 Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is142 not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return143 type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could144 even be something different, in the case of a custom serializer.145 Thus, `Any` is used here to catch all of these cases.146 """147 ...148 149 def __eq__(self, other: Any) -> bool:150 if not isinstance(other, RootModel):151 return NotImplemented152 return self.__pydantic_fields__['root'].annotation == other.__pydantic_fields__[153 'root'154 ].annotation and super().__eq__(other)155 156 def __repr_args__(self) -> _repr.ReprArgs:157 yield 'root', self.root158 