Coverage for pydantic/_internal/_mock_val_ser.py: 84.48%

82 statements  

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

1from __future__ import annotations 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

2 

3from typing import TYPE_CHECKING, Any, Callable, Generic, Iterator, Mapping, TypeVar 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

4 

5from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

6from typing_extensions import Literal 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

7 

8from ..errors import PydanticErrorCodes, PydanticUserError 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

9 

10if TYPE_CHECKING: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

11 from ..dataclasses import PydanticDataclass 

12 from ..main import BaseModel 

13 

14 

15ValSer = TypeVar('ValSer', SchemaValidator, SchemaSerializer) 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

16T = TypeVar('T') 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

17 

18 

19class MockCoreSchema(Mapping[str, Any]): 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

20 """Mocker for `pydantic_core.CoreSchema` which optionally attempts to 

21 rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails. 

22 """ 

23 

24 __slots__ = '_error_message', '_code', '_attempt_rebuild', '_built_memo' 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

25 

26 def __init__( 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

27 self, 

28 error_message: str, 

29 *, 

30 code: PydanticErrorCodes, 

31 attempt_rebuild: Callable[[], CoreSchema | None] | None = None, 

32 ) -> None: 

33 self._error_message = error_message 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

34 self._code: PydanticErrorCodes = code 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

35 self._attempt_rebuild = attempt_rebuild 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

36 self._built_memo: CoreSchema | None = None 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

37 

38 def __getitem__(self, key: str) -> Any: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

39 return self._get_built().__getitem__(key) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

40 

41 def __len__(self) -> int: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

42 return self._get_built().__len__() 

43 

44 def __iter__(self) -> Iterator[str]: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

45 return self._get_built().__iter__() 

46 

47 def _get_built(self) -> CoreSchema: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

48 if self._built_memo is not None: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

49 return self._built_memo 

50 

51 if self._attempt_rebuild: 51 ↛ 56line 51 didn't jump to line 56 because the condition on line 51 was always true1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

52 schema = self._attempt_rebuild() 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

53 if schema is not None: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

54 self._built_memo = schema 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

55 return schema 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

56 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

57 

58 def rebuild(self) -> CoreSchema | None: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

59 self._built_memo = None 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

60 if self._attempt_rebuild: 60 ↛ 66line 60 didn't jump to line 66 because the condition on line 60 was always true1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

61 val_ser = self._attempt_rebuild() 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

62 if val_ser is not None: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

63 return val_ser 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

64 else: 

65 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

66 return None 

67 

68 

69class MockValSer(Generic[ValSer]): 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

70 """Mocker for `pydantic_core.SchemaValidator` or `pydantic_core.SchemaSerializer` which optionally attempts to 

71 rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails. 

72 """ 

73 

74 __slots__ = '_error_message', '_code', '_val_or_ser', '_attempt_rebuild' 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

75 

76 def __init__( 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

77 self, 

78 error_message: str, 

79 *, 

80 code: PydanticErrorCodes, 

81 val_or_ser: Literal['validator', 'serializer'], 

82 attempt_rebuild: Callable[[], ValSer | None] | None = None, 

83 ) -> None: 

84 self._error_message = error_message 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

85 self._val_or_ser = SchemaValidator if val_or_ser == 'validator' else SchemaSerializer 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

86 self._code: PydanticErrorCodes = code 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

87 self._attempt_rebuild = attempt_rebuild 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

88 

89 def __getattr__(self, item: str) -> None: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

90 __tracebackhide__ = True 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

91 if self._attempt_rebuild: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

92 val_ser = self._attempt_rebuild() 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

93 if val_ser is not None: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

94 return getattr(val_ser, item) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

95 

96 # raise an AttributeError if `item` doesn't exist 

97 getattr(self._val_or_ser, item) 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

98 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

99 

100 def rebuild(self) -> ValSer | None: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

101 if self._attempt_rebuild: 

102 val_ser = self._attempt_rebuild() 

103 if val_ser is not None: 

104 return val_ser 

105 else: 

106 raise PydanticUserError(self._error_message, code=self._code) 

107 return None 

108 

109 

110def set_model_mocks(cls: type[BaseModel], cls_name: str, undefined_name: str = 'all referenced types') -> None: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

111 """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a model. 

112 

113 Args: 

114 cls: The model class to set the mocks on 

115 cls_name: Name of the model class, used in error messages 

116 undefined_name: Name of the undefined thing, used in error messages 

117 """ 

118 undefined_type_error_message = ( 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

119 f'`{cls_name}` is not fully defined; you should define {undefined_name},' 

120 f' then call `{cls_name}.model_rebuild()`.' 

121 ) 

122 

123 def attempt_rebuild_fn(attr_fn: Callable[[type[BaseModel]], T]) -> Callable[[], T | None]: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

124 def handler() -> T | None: 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

125 if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

126 return attr_fn(cls) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

127 else: 

128 return None 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

129 

130 return handler 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

131 

132 cls.__pydantic_core_schema__ = MockCoreSchema( # type: ignore[assignment] 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

133 undefined_type_error_message, 

134 code='class-not-fully-defined', 

135 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__), 

136 ) 

137 cls.__pydantic_validator__ = MockValSer( # type: ignore[assignment] 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

138 undefined_type_error_message, 

139 code='class-not-fully-defined', 

140 val_or_ser='validator', 

141 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__), 

142 ) 

143 cls.__pydantic_serializer__ = MockValSer( # type: ignore[assignment] 1abcdefghijklKLmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

144 undefined_type_error_message, 

145 code='class-not-fully-defined', 

146 val_or_ser='serializer', 

147 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__), 

148 ) 

149 

150 

151def set_dataclass_mocks( 1abcdefghijklmnopqrstuvwxMNOPQRSTUVyzABCDEFGHIJ

152 cls: type[PydanticDataclass], cls_name: str, undefined_name: str = 'all referenced types' 

153) -> None: 

154 """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a dataclass. 

155 

156 Args: 

157 cls: The model class to set the mocks on 

158 cls_name: Name of the model class, used in error messages 

159 undefined_name: Name of the undefined thing, used in error messages 

160 """ 

161 from ..dataclasses import rebuild_dataclass 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

162 

163 undefined_type_error_message = ( 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

164 f'`{cls_name}` is not fully defined; you should define {undefined_name},' 

165 f' then call `pydantic.dataclasses.rebuild_dataclass({cls_name})`.' 

166 ) 

167 

168 def attempt_rebuild_fn(attr_fn: Callable[[type[PydanticDataclass]], T]) -> Callable[[], T | None]: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

169 def handler() -> T | None: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

170 if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False: 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

171 return attr_fn(cls) 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

172 else: 

173 return None 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

174 

175 return handler 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

176 

177 cls.__pydantic_core_schema__ = MockCoreSchema( # type: ignore[assignment] 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

178 undefined_type_error_message, 

179 code='class-not-fully-defined', 

180 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__), 

181 ) 

182 cls.__pydantic_validator__ = MockValSer( # type: ignore[assignment] 1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

183 undefined_type_error_message, 

184 code='class-not-fully-defined', 

185 val_or_ser='validator', 

186 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__), 

187 ) 

188 cls.__pydantic_serializer__ = MockValSer( # type: ignore[assignment] 188 ↛ exitline 188 didn't jump to the function exit1abcdefghijklKLmnopqrstuvwxyzABCDEFGHIJ

189 undefined_type_error_message, 

190 code='class-not-fully-defined', 

191 val_or_ser='validator', 

192 attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__), 

193 )