Coverage for pydantic/root_model.py: 100.00%

54 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-03 19:29 +0000

1"""RootModel class and type definitions.""" 

2 

3from __future__ import annotations as _annotations 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

4 

5import typing 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

6from copy import copy, deepcopy 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

7 

8from pydantic_core import PydanticUndefined 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

9 

10from . import PydanticUserError 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

11from ._internal import _model_construction, _repr 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

12from .main import BaseModel, _object_setattr 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

13 

14if typing.TYPE_CHECKING: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

15 from typing import Any 

16 

17 from typing_extensions import Literal, Self, dataclass_transform 

18 

19 from .fields import Field as PydanticModelField 

20 from .fields import PrivateAttr as PydanticModelPrivateAttr 

21 

22 # dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform 

23 # takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform 

24 # on a new metaclass. 

25 @dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr)) 

26 class _RootModelMetaclass(_model_construction.ModelMetaclass): ... 

27else: 

28 _RootModelMetaclass = _model_construction.ModelMetaclass 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

29 

30__all__ = ('RootModel',) 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

31 

32RootModelRootType = typing.TypeVar('RootModelRootType') 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

33 

34 

35class RootModel(BaseModel, typing.Generic[RootModelRootType], metaclass=_RootModelMetaclass): 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

36 """Usage docs: https://docs.pydantic.dev/2.8/concepts/models/#rootmodel-and-custom-root-types 

37 

38 A Pydantic `BaseModel` for the root object of the model. 

39 

40 Attributes: 

41 root: The root object of the model. 

42 __pydantic_root_model__: Whether the model is a RootModel. 

43 __pydantic_private__: Private fields in the model. 

44 __pydantic_extra__: Extra fields in the model. 

45 

46 """ 

47 

48 __pydantic_root_model__ = True 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

49 __pydantic_private__ = None 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

50 __pydantic_extra__ = None 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

51 

52 root: RootModelRootType 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

53 

54 def __init_subclass__(cls, **kwargs): 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

55 extra = cls.model_config.get('extra') 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

56 if extra is not None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

57 raise PydanticUserError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

58 "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra' 

59 ) 

60 super().__init_subclass__(**kwargs) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

61 

62 def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

63 __tracebackhide__ = True 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

64 if data: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

65 if root is not PydanticUndefined: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

66 raise ValueError( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

67 '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments' 

68 ) 

69 root = data # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

70 self.__pydantic_validator__.validate_python(root, self_instance=self) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

71 

72 __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess] 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

73 

74 @classmethod 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

75 def model_construct(cls, root: RootModelRootType, _fields_set: set[str] | None = None) -> Self: # type: ignore 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

76 """Create a new model using the provided root object and update fields set. 

77 

78 Args: 

79 root: The root object of the model. 

80 _fields_set: The set of fields to be updated. 

81 

82 Returns: 

83 The new model. 

84 

85 Raises: 

86 NotImplemented: If the model is not a subclass of `RootModel`. 

87 """ 

88 return super().model_construct(root=root, _fields_set=_fields_set) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

89 

90 def __getstate__(self) -> dict[Any, Any]: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

91 return { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

92 '__dict__': self.__dict__, 

93 '__pydantic_fields_set__': self.__pydantic_fields_set__, 

94 } 

95 

96 def __setstate__(self, state: dict[Any, Any]) -> None: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

97 _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__']) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

98 _object_setattr(self, '__dict__', state['__dict__']) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

99 

100 def __copy__(self) -> Self: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

101 """Returns a shallow copy of the model.""" 

102 cls = type(self) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

103 m = cls.__new__(cls) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

104 _object_setattr(m, '__dict__', copy(self.__dict__)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

105 _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

106 return m 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

107 

108 def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

109 """Returns a deep copy of the model.""" 

110 cls = type(self) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

111 m = cls.__new__(cls) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

112 _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

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__)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

116 return m 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

117 

118 if typing.TYPE_CHECKING: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

119 

120 def model_dump( # type: ignore 

121 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 = False, 

128 exclude_unset: bool = False, 

129 exclude_defaults: bool = False, 

130 exclude_none: bool = False, 

131 round_trip: bool = False, 

132 warnings: bool | Literal['none', 'warn', 'error'] = True, 

133 serialize_as_any: bool = False, 

134 ) -> Any: 

135 """This method is included just to get a more accurate return type for type checkers. 

136 It is included in this `if TYPE_CHECKING:` block since no override is actually necessary. 

137 

138 See the documentation of `BaseModel.model_dump` for more details about the arguments. 

139 

140 Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is 

141 not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return 

142 type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could 

143 even be something different, in the case of a custom serializer. 

144 Thus, `Any` is used here to catch all of these cases. 

145 """ 

146 ... 

147 

148 def __eq__(self, other: Any) -> bool: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

149 if not isinstance(other, RootModel): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

150 return NotImplemented 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

151 return self.model_fields['root'].annotation == other.model_fields['root'].annotation and super().__eq__(other) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

152 

153 def __repr_args__(self) -> _repr.ReprArgs: 1abcdefghijklmnopqrstuvwxyzMNOPQRSTUVABCDEFGHIJKL

154 yield 'root', self.root 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL