Coverage for pydantic/_internal/_mock_val_ser.py: 84.48%
82 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-06-21 17:00 +0000
1from __future__ import annotations 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
3from typing import TYPE_CHECKING, Any, Callable, Generic, Iterator, Mapping, TypeVar 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
5from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
6from typing_extensions import Literal 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
8from ..errors import PydanticErrorCodes, PydanticUserError 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
10if TYPE_CHECKING: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
11 from ..dataclasses import PydanticDataclass
12 from ..main import BaseModel
15ValSer = TypeVar('ValSer', SchemaValidator, SchemaSerializer) 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
16T = TypeVar('T') 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
19class MockCoreSchema(Mapping[str, Any]): 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 """
24 __slots__ = '_error_message', '_code', '_attempt_rebuild', '_built_memo' 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
26 def __init__( 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
34 self._code: PydanticErrorCodes = code 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
35 self._attempt_rebuild = attempt_rebuild 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
36 self._built_memo: CoreSchema | None = None 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
38 def __getitem__(self, key: str) -> Any: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
39 return self._get_built().__getitem__(key) 1abcdefghijEFklmnopqrstuvwxyzABCD
41 def __len__(self) -> int: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
42 return self._get_built().__len__()
44 def __iter__(self) -> Iterator[str]: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
45 return self._get_built().__iter__()
47 def _get_built(self) -> CoreSchema: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 true1abcdefghijEFklmnopqrstuvwxyzABCD
49 return self._built_memo
51 if self._attempt_rebuild: 51 ↛ 56line 51 didn't jump to line 56, because the condition on line 51 was always true1abcdefghijEFklmnopqrstuvwxyzABCD
52 schema = self._attempt_rebuild() 1abcdefghijEFklmnopqrstuvwxyzABCD
53 if schema is not None: 1abcdefghijEFklmnopqrstuvwxyzABCD
54 self._built_memo = schema 1abcdefghijEFklmnopqrstuvwxyzABCD
55 return schema 1abcdefghijEFklmnopqrstuvwxyzABCD
56 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijEFklmnopqrstuvwxyzABCD
58 def rebuild(self) -> CoreSchema | None: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
59 self._built_memo = None 1abcdefghijEFklmnopqrstuvwxyzABCD
60 if self._attempt_rebuild: 60 ↛ 66line 60 didn't jump to line 66, because the condition on line 60 was always true1abcdefghijEFklmnopqrstuvwxyzABCD
61 val_ser = self._attempt_rebuild() 1abcdefghijEFklmnopqrstuvwxyzABCD
62 if val_ser is not None: 1abcdefghijEFklmnopqrstuvwxyzABCD
63 return val_ser 1abcdefghijEFklmnopqrstuvwxyzABCD
64 else:
65 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijEFklmnopqrstuvwxyzABCD
66 return None
69class MockValSer(Generic[ValSer]): 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 """
74 __slots__ = '_error_message', '_code', '_val_or_ser', '_attempt_rebuild' 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
76 def __init__( 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
85 self._val_or_ser = SchemaValidator if val_or_ser == 'validator' else SchemaSerializer 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
86 self._code: PydanticErrorCodes = code 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
87 self._attempt_rebuild = attempt_rebuild 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
89 def __getattr__(self, item: str) -> None: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
90 __tracebackhide__ = True 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
91 if self._attempt_rebuild: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
92 val_ser = self._attempt_rebuild() 1abcdefghijEFklmnopqrstuvwxyzABCD
93 if val_ser is not None: 1abcdefghijEFklmnopqrstuvwxyzABCD
94 return getattr(val_ser, item) 1abcdefghijEFklmnopqrstuvwxyzABCD
96 # raise an AttributeError if `item` doesn't exist
97 getattr(self._val_or_ser, item) 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
98 raise PydanticUserError(self._error_message, code=self._code) 1abcdefghijEFklmnopqrstuvwxyzABCD
100 def rebuild(self) -> ValSer | None: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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
110def set_model_mocks(cls: type[BaseModel], cls_name: str, undefined_name: str = 'all referenced types') -> None: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
111 """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a model.
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 = ( 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
119 f'`{cls_name}` is not fully defined; you should define {undefined_name},'
120 f' then call `{cls_name}.model_rebuild()`.'
121 )
123 def attempt_rebuild_fn(attr_fn: Callable[[type[BaseModel]], T]) -> Callable[[], T | None]: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
124 def handler() -> T | None: 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
125 if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False: 1abcdefghijEFklmnopqrstuvwxyzABCD
126 return attr_fn(cls) 1abcdefghijEFklmnopqrstuvwxyzABCD
127 else:
128 return None 1abcdefghijEFklmnopqrstuvwxyzABCD
130 return handler 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
132 cls.__pydantic_core_schema__ = MockCoreSchema( # type: ignore[assignment] 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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] 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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] 1abcdefghijEFklmnopqrstGHIJKLMNOuvwxyzABCD
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 )
151def set_dataclass_mocks( 1abcdefghijklmnopqrstGHIJKLMNOuvwxyzABCD
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.
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 1abcdefghijEFklmnopqrstuvwxyzABCD
163 undefined_type_error_message = ( 1abcdefghijEFklmnopqrstuvwxyzABCD
164 f'`{cls_name}` is not fully defined; you should define {undefined_name},'
165 f' then call `pydantic.dataclasses.rebuild_dataclass({cls_name})`.'
166 )
168 def attempt_rebuild_fn(attr_fn: Callable[[type[PydanticDataclass]], T]) -> Callable[[], T | None]: 1abcdefghijEFklmnopqrstuvwxyzABCD
169 def handler() -> T | None: 1abcdefghijEFklmnopqrstuvwxyzABCD
170 if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False: 1abcdefghijEFklmnopqrstuvwxyzABCD
171 return attr_fn(cls) 1abcdefghijEFklmnopqrstuvwxyzABCD
172 else:
173 return None 1abcdefghijEFklmnopqrstuvwxyzABCD
175 return handler 1abcdefghijEFklmnopqrstuvwxyzABCD
177 cls.__pydantic_core_schema__ = MockCoreSchema( # type: ignore[assignment] 1abcdefghijEFklmnopqrstuvwxyzABCD
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] 1abcdefghijEFklmnopqrstuvwxyzABCD
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 exit1abcdefghijEFklmnopqrstuvwxyzABCD
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 )