Coverage for pydantic/experimental/pipeline.py: 93.41%
356 statements
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-03 19:29 +0000
« prev ^ index » next coverage.py v7.5.4, created at 2024-07-03 19:29 +0000
1"""Experimental pipeline API functionality. Be careful with this API, it's subject to change."""
3from __future__ import annotations 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
5import datetime 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
6import operator 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
7import re 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
8import sys 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
9from collections import deque 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
10from collections.abc import Container 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
11from dataclasses import dataclass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
12from decimal import Decimal 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
13from functools import cached_property, partial 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
14from typing import TYPE_CHECKING, Any, Callable, Generic, Pattern, Protocol, TypeVar, Union, overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
16import annotated_types 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
17from typing_extensions import Annotated 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
19if TYPE_CHECKING: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
20 from pydantic_core import core_schema as cs
22 from pydantic import GetCoreSchemaHandler
24from pydantic._internal._internal_dataclass import slots_true as _slots_true 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
26if sys.version_info < (3, 10): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
27 EllipsisType = type(Ellipsis) 1uwoxcyzABvCpD
28else:
29 from types import EllipsisType 1qErFaefgdGHIJhijksKtLblmn
31__all__ = ['validate_as', 'validate_as_deferred', 'transform'] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
33_slots_frozen = {**_slots_true, 'frozen': True} 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
36@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
37class _ValidateAs: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
38 tp: type[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
39 strict: bool = False 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
42@dataclass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
43class _ValidateAsDefer: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
44 func: Callable[[], type[Any]] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
46 @cached_property 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
47 def tp(self) -> type[Any]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
48 return self.func() 1oqrapstb
51@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
52class _Transform: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
53 func: Callable[[Any], Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
56@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
57class _PipelineOr: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
58 left: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
59 right: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
62@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
63class _PipelineAnd: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
64 left: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
65 right: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
68@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
69class _Eq: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
70 value: Any 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
73@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
74class _NotEq: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
75 value: Any 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
78@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
79class _In: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
80 values: Container[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
83@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
84class _NotIn: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
85 values: Container[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
88_ConstraintAnnotation = Union[ 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
89 annotated_types.Le,
90 annotated_types.Ge,
91 annotated_types.Lt,
92 annotated_types.Gt,
93 annotated_types.Len,
94 annotated_types.MultipleOf,
95 annotated_types.Timezone,
96 annotated_types.Interval,
97 annotated_types.Predicate,
98 # common predicates not included in annotated_types
99 _Eq,
100 _NotEq,
101 _In,
102 _NotIn,
103 # regular expressions
104 Pattern[str],
105]
108@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
109class _Constraint: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
110 constraint: _ConstraintAnnotation 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
113_Step = Union[_ValidateAs, _ValidateAsDefer, _Transform, _PipelineOr, _PipelineAnd, _Constraint] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
115_InT = TypeVar('_InT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
116_OutT = TypeVar('_OutT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
117_NewOutT = TypeVar('_NewOutT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
120class _FieldTypeMarker: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
121 pass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
124# TODO: ultimately, make this public, see https://github.com/pydantic/pydantic/pull/9459#discussion_r1628197626
125# Also, make this frozen eventually, but that doesn't work right now because of the generic base
126# Which attempts to modify __orig_base__ and such.
127# We could go with a manual freeze, but that seems overkill for now.
128@dataclass(**_slots_true) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
129class _Pipeline(Generic[_InT, _OutT]): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
130 """Abstract representation of a chain of validation, transformation, and parsing steps."""
132 _steps: tuple[_Step, ...] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
134 def transform( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
135 self,
136 func: Callable[[_OutT], _NewOutT],
137 ) -> _Pipeline[_InT, _NewOutT]:
138 """Transform the output of the previous step.
140 If used as the first step in a pipeline, the type of the field is used.
141 That is, the transformation is applied to after the value is parsed to the field's type.
142 """
143 return _Pipeline[_InT, _NewOutT](self._steps + (_Transform(func),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
145 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
146 def validate_as(self, tp: type[_NewOutT], *, strict: bool = ...) -> _Pipeline[_InT, _NewOutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
148 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
149 def validate_as(self, tp: EllipsisType, *, strict: bool = ...) -> _Pipeline[_InT, Any]: # type: ignore 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
150 ...
152 def validate_as(self, tp: type[_NewOutT] | EllipsisType, *, strict: bool = False) -> _Pipeline[_InT, Any]: # type: ignore 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
153 """Validate / parse the input into a new type.
155 If no type is provided, the type of the field is used.
157 Types are parsed in Pydantic's `lax` mode by default,
158 but you can enable `strict` mode by passing `strict=True`.
159 """
160 if isinstance(tp, EllipsisType): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
161 return _Pipeline[_InT, Any](self._steps + (_ValidateAs(_FieldTypeMarker, strict=strict),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
162 return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAs(tp, strict=strict),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
164 def validate_as_deferred(self, func: Callable[[], type[_NewOutT]]) -> _Pipeline[_InT, _NewOutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
165 """Parse the input into a new type, deferring resolution of the type until the current class
166 is fully defined.
168 This is useful when you need to reference the class in it's own type annotations.
169 """
170 return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAsDefer(func),)) 1oqrapstb
172 # constraints
173 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
174 def constrain(self: _Pipeline[_InT, _NewOutGe], constraint: annotated_types.Ge) -> _Pipeline[_InT, _NewOutGe]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
176 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
177 def constrain(self: _Pipeline[_InT, _NewOutGt], constraint: annotated_types.Gt) -> _Pipeline[_InT, _NewOutGt]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
179 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
180 def constrain(self: _Pipeline[_InT, _NewOutLe], constraint: annotated_types.Le) -> _Pipeline[_InT, _NewOutLe]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
182 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
183 def constrain(self: _Pipeline[_InT, _NewOutLt], constraint: annotated_types.Lt) -> _Pipeline[_InT, _NewOutLt]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
185 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
186 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
187 self: _Pipeline[_InT, _NewOutLen], constraint: annotated_types.Len 1aefgcdhijkblmn
188 ) -> _Pipeline[_InT, _NewOutLen]: ... 1aefgcdhijkblmn
190 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
191 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
192 self: _Pipeline[_InT, _NewOutT], constraint: annotated_types.MultipleOf 1aefgcdhijkblmn
193 ) -> _Pipeline[_InT, _NewOutT]: ... 1aefgcdhijkblmn
195 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
196 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
197 self: _Pipeline[_InT, _NewOutDatetime], constraint: annotated_types.Timezone 1aefgcdhijkblmn
198 ) -> _Pipeline[_InT, _NewOutDatetime]: ... 1aefgcdhijkblmn
200 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
201 def constrain(self: _Pipeline[_InT, _OutT], constraint: annotated_types.Predicate) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
203 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
204 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
205 self: _Pipeline[_InT, _NewOutInterval], constraint: annotated_types.Interval 1aefgcdhijkblmn
206 ) -> _Pipeline[_InT, _NewOutInterval]: ... 1aefgcdhijkblmn
208 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
209 def constrain(self: _Pipeline[_InT, _OutT], constraint: _Eq) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
211 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
212 def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotEq) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
214 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
215 def constrain(self: _Pipeline[_InT, _OutT], constraint: _In) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
217 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
218 def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotIn) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
220 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
221 def constrain(self: _Pipeline[_InT, _NewOutT], constraint: Pattern[str]) -> _Pipeline[_InT, _NewOutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
223 def constrain(self, constraint: _ConstraintAnnotation) -> Any: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
224 """Constrain a value to meet a certain condition.
226 We support most conditions from `annotated_types`, as well as regular expressions.
228 Most of the time you'll be calling a shortcut method like `gt`, `lt`, `len`, etc
229 so you don't need to call this directly.
230 """
231 return _Pipeline[_InT, _OutT](self._steps + (_Constraint(constraint),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
233 def predicate(self: _Pipeline[_InT, _NewOutT], func: Callable[[_NewOutT], bool]) -> _Pipeline[_InT, _NewOutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
234 """Constrain a value to meet a certain predicate."""
235 return self.constrain(annotated_types.Predicate(func)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
237 def gt(self: _Pipeline[_InT, _NewOutGt], gt: _NewOutGt) -> _Pipeline[_InT, _NewOutGt]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
238 """Constrain a value to be greater than a certain value."""
239 return self.constrain(annotated_types.Gt(gt)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
241 def lt(self: _Pipeline[_InT, _NewOutLt], lt: _NewOutLt) -> _Pipeline[_InT, _NewOutLt]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
242 """Constrain a value to be less than a certain value."""
243 return self.constrain(annotated_types.Lt(lt)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
245 def ge(self: _Pipeline[_InT, _NewOutGe], ge: _NewOutGe) -> _Pipeline[_InT, _NewOutGe]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
246 """Constrain a value to be greater than or equal to a certain value."""
247 return self.constrain(annotated_types.Ge(ge)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
249 def le(self: _Pipeline[_InT, _NewOutLe], le: _NewOutLe) -> _Pipeline[_InT, _NewOutLe]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
250 """Constrain a value to be less than or equal to a certain value."""
251 return self.constrain(annotated_types.Le(le)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
253 def len(self: _Pipeline[_InT, _NewOutLen], min_len: int, max_len: int | None = None) -> _Pipeline[_InT, _NewOutLen]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
254 """Constrain a value to have a certain length."""
255 return self.constrain(annotated_types.Len(min_len, max_len)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
257 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
258 def multiple_of(self: _Pipeline[_InT, _NewOutDiv], multiple_of: _NewOutDiv) -> _Pipeline[_InT, _NewOutDiv]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
260 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
261 def multiple_of(self: _Pipeline[_InT, _NewOutMod], multiple_of: _NewOutMod) -> _Pipeline[_InT, _NewOutMod]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
263 def multiple_of(self: _Pipeline[_InT, Any], multiple_of: Any) -> _Pipeline[_InT, Any]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
264 """Constrain a value to be a multiple of a certain number."""
265 return self.constrain(annotated_types.MultipleOf(multiple_of)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
267 def eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
268 """Constrain a value to be equal to a certain value."""
269 return self.constrain(_Eq(value)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
271 def not_eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
272 """Constrain a value to not be equal to a certain value."""
273 return self.constrain(_NotEq(value)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
275 def in_(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
276 """Constrain a value to be in a certain set."""
277 return self.constrain(_In(values)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
279 def not_in(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
280 """Constrain a value to not be in a certain set."""
281 return self.constrain(_NotIn(values)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
283 # timezone methods
284 def datetime_tz_naive(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
285 return self.constrain(annotated_types.Timezone(None)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
287 def datetime_tz_aware(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
288 return self.constrain(annotated_types.Timezone(...)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
290 def datetime_tz( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
291 self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo
292 ) -> _Pipeline[_InT, datetime.datetime]:
293 return self.constrain(annotated_types.Timezone(tz)) # type: ignore
295 def datetime_with_tz( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
296 self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo | None
297 ) -> _Pipeline[_InT, datetime.datetime]:
298 return self.transform(partial(datetime.datetime.replace, tzinfo=tz))
300 # string methods
301 def str_lower(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
302 return self.transform(str.lower) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
304 def str_upper(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
305 return self.transform(str.upper) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
307 def str_title(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
308 return self.transform(str.title) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
310 def str_strip(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
311 return self.transform(str.strip) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
313 def str_pattern(self: _Pipeline[_InT, str], pattern: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
314 return self.constrain(re.compile(pattern)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
316 def str_contains(self: _Pipeline[_InT, str], substring: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
317 return self.predicate(lambda v: substring in v) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
319 def str_starts_with(self: _Pipeline[_InT, str], prefix: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
320 return self.predicate(lambda v: v.startswith(prefix)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
322 def str_ends_with(self: _Pipeline[_InT, str], suffix: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
323 return self.predicate(lambda v: v.endswith(suffix)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
325 # operators
326 def otherwise(self, other: _Pipeline[_OtherIn, _OtherOut]) -> _Pipeline[_InT | _OtherIn, _OutT | _OtherOut]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
327 """Combine two validation chains, returning the result of the first chain if it succeeds, and the second chain if it fails."""
328 return _Pipeline((_PipelineOr(self, other),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
330 __or__ = otherwise 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
332 def then(self, other: _Pipeline[_OutT, _OtherOut]) -> _Pipeline[_InT, _OtherOut]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
333 """Pipe the result of one validation chain into another."""
334 return _Pipeline((_PipelineAnd(self, other),)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
336 __and__ = then 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
338 def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> cs.CoreSchema: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
339 from pydantic_core import core_schema as cs 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
341 queue = deque(self._steps) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
343 s = None 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
345 while queue: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
346 step = queue.popleft() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
347 s = _apply_step(step, s, handler, source_type) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
349 s = s or cs.any_schema() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
350 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
352 def __supports_type__(self, _: _OutT) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
353 raise NotImplementedError
356validate_as = _Pipeline[Any, Any](()).validate_as 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
357validate_as_deferred = _Pipeline[Any, Any](()).validate_as_deferred 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
358transform = _Pipeline[Any, Any]((_ValidateAs(_FieldTypeMarker),)).transform 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
361def _check_func( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
362 func: Callable[[Any], bool], predicate_err: str | Callable[[], str], s: cs.CoreSchema | None
363) -> cs.CoreSchema:
364 from pydantic_core import core_schema as cs 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
366 def handler(v: Any) -> Any: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
367 if func(v): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
368 return v 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
369 raise ValueError(f'Expected {predicate_err if isinstance(predicate_err, str) else predicate_err()}') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
371 if s is None: 371 ↛ 372line 371 didn't jump to line 372 because the condition on line 371 was never true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
372 return cs.no_info_plain_validator_function(handler)
373 else:
374 return cs.no_info_after_validator_function(handler, s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
377def _apply_step(step: _Step, s: cs.CoreSchema | None, handler: GetCoreSchemaHandler, source_type: Any) -> cs.CoreSchema: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
378 from pydantic_core import core_schema as cs 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
380 if isinstance(step, _ValidateAs): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
381 s = _apply_parse(s, step.tp, step.strict, handler, source_type) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
382 elif isinstance(step, _ValidateAsDefer): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
383 s = _apply_parse(s, step.tp, False, handler, source_type) 1oqrapstb
384 elif isinstance(step, _Transform): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
385 s = _apply_transform(s, step.func, handler) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
386 elif isinstance(step, _Constraint): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
387 s = _apply_constraint(s, step.constraint) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
388 elif isinstance(step, _PipelineOr): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
389 s = cs.union_schema([handler(step.left), handler(step.right)]) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
390 else:
391 assert isinstance(step, _PipelineAnd) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
392 s = cs.chain_schema([handler(step.left), handler(step.right)]) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
393 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
396def _apply_parse( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
397 s: cs.CoreSchema | None,
398 tp: type[Any],
399 strict: bool,
400 handler: GetCoreSchemaHandler,
401 source_type: Any,
402) -> cs.CoreSchema:
403 from pydantic_core import core_schema as cs 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
405 from pydantic import Strict 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
407 if tp is _FieldTypeMarker: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
408 return handler(source_type) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
410 if strict: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
411 tp = Annotated[tp, Strict()] # type: ignore 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
413 if s and s['type'] == 'any': 413 ↛ 414line 413 didn't jump to line 414 because the condition on line 413 was never true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
414 return handler(tp)
415 else:
416 return cs.chain_schema([s, handler(tp)]) if s else handler(tp) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
419def _apply_transform( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
420 s: cs.CoreSchema | None, func: Callable[[Any], Any], handler: GetCoreSchemaHandler
421) -> cs.CoreSchema:
422 from pydantic_core import core_schema as cs 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
424 if s is None: 424 ↛ 425line 424 didn't jump to line 425 because the condition on line 424 was never true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
425 return cs.no_info_plain_validator_function(func)
427 if s['type'] == 'str': 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
428 if func is str.strip: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
429 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
430 s['strip_whitespace'] = True 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
431 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
432 elif func is str.lower: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
433 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
434 s['to_lower'] = True 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
435 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
436 elif func is str.upper: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
437 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
438 s['to_upper'] = True 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
439 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
441 return cs.no_info_after_validator_function(func, s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
444def _apply_constraint( # noqa: C901 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
445 s: cs.CoreSchema | None, constraint: _ConstraintAnnotation
446) -> cs.CoreSchema:
447 """Apply a single constraint to a schema."""
448 if isinstance(constraint, annotated_types.Gt): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
449 gt = constraint.gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
450 if s and s['type'] in {'int', 'float', 'decimal'}: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
451 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
452 if s['type'] == 'int' and isinstance(gt, int): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
453 s['gt'] = gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
454 elif s['type'] == 'float' and isinstance(gt, float): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
455 s['gt'] = gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
456 elif s['type'] == 'decimal' and isinstance(gt, Decimal): 456 ↛ 648line 456 didn't jump to line 648 because the condition on line 456 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
457 s['gt'] = gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
458 else:
460 def check_gt(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
461 return v > gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
463 s = _check_func(check_gt, f'> {gt}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
464 elif isinstance(constraint, annotated_types.Ge): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
465 ge = constraint.ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
466 if s and s['type'] in {'int', 'float', 'decimal'}: 466 ↛ 475line 466 didn't jump to line 475 because the condition on line 466 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
467 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
468 if s['type'] == 'int' and isinstance(ge, int): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
469 s['ge'] = ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
470 elif s['type'] == 'float' and isinstance(ge, float): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
471 s['ge'] = ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
472 elif s['type'] == 'decimal' and isinstance(ge, Decimal): 472 ↛ 475line 472 didn't jump to line 475 because the condition on line 472 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
473 s['ge'] = ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
475 def check_ge(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
476 return v >= ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
478 s = _check_func(check_ge, f'>= {ge}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
479 elif isinstance(constraint, annotated_types.Lt): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
480 lt = constraint.lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
481 if s and s['type'] in {'int', 'float', 'decimal'}: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
482 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
483 if s['type'] == 'int' and isinstance(lt, int): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
484 s['lt'] = lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
485 elif s['type'] == 'float' and isinstance(lt, float): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
486 s['lt'] = lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
487 elif s['type'] == 'decimal' and isinstance(lt, Decimal): 487 ↛ 490line 487 didn't jump to line 490 because the condition on line 487 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
488 s['lt'] = lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
490 def check_lt(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
491 return v < lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
493 s = _check_func(check_lt, f'< {lt}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
494 elif isinstance(constraint, annotated_types.Le): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
495 le = constraint.le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
496 if s and s['type'] in {'int', 'float', 'decimal'}: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
497 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
498 if s['type'] == 'int' and isinstance(le, int): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
499 s['le'] = le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
500 elif s['type'] == 'float' and isinstance(le, float): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
501 s['le'] = le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
502 elif s['type'] == 'decimal' and isinstance(le, Decimal): 502 ↛ 505line 502 didn't jump to line 505 because the condition on line 502 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
503 s['le'] = le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
505 def check_le(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
506 return v <= le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
508 s = _check_func(check_le, f'<= {le}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
509 elif isinstance(constraint, annotated_types.Len): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
510 min_len = constraint.min_length 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
511 max_len = constraint.max_length 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
513 if s and s['type'] in {'str', 'list', 'tuple', 'set', 'frozenset', 'dict'}: 513 ↛ 528line 513 didn't jump to line 528 because the condition on line 513 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
514 assert ( 1uwoxcdyzABvCpD
515 s['type'] == 'str'
516 or s['type'] == 'list'
517 or s['type'] == 'tuple'
518 or s['type'] == 'set'
519 or s['type'] == 'dict'
520 or s['type'] == 'frozenset'
521 )
522 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
523 if min_len != 0: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
524 s['min_length'] = min_len 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
525 if max_len is not None: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
526 s['max_length'] = max_len 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
528 def check_len(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
529 if max_len is not None: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
530 return (min_len <= len(v)) and (len(v) <= max_len) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
531 return min_len <= len(v) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
533 s = _check_func(check_len, f'length >= {min_len} and length <= {max_len}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
534 elif isinstance(constraint, annotated_types.MultipleOf): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
535 multiple_of = constraint.multiple_of 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
536 if s and s['type'] in {'int', 'float', 'decimal'}: 536 ↛ 545line 536 didn't jump to line 545 because the condition on line 536 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
537 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
538 if s['type'] == 'int' and isinstance(multiple_of, int): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
539 s['multiple_of'] = multiple_of 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
540 elif s['type'] == 'float' and isinstance(multiple_of, float): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
541 s['multiple_of'] = multiple_of 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
542 elif s['type'] == 'decimal' and isinstance(multiple_of, Decimal): 542 ↛ 545line 542 didn't jump to line 545 because the condition on line 542 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
543 s['multiple_of'] = multiple_of 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
545 def check_multiple_of(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
546 return v % multiple_of == 0 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
548 s = _check_func(check_multiple_of, f'% {multiple_of} == 0', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
549 elif isinstance(constraint, annotated_types.Timezone): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
550 tz = constraint.tz 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
552 if tz is ...: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
553 if s and s['type'] == 'datetime': 553 ↛ 558line 553 didn't jump to line 558 because the condition on line 553 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
554 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
555 s['tz_constraint'] = 'aware' 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
556 else:
558 def check_tz_aware(v: object) -> bool:
559 assert isinstance(v, datetime.datetime)
560 return v.tzinfo is not None
562 s = _check_func(check_tz_aware, 'timezone aware', s)
563 elif tz is None: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
564 if s and s['type'] == 'datetime': 564 ↛ 569line 564 didn't jump to line 569 because the condition on line 564 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
565 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
566 s['tz_constraint'] = 'naive' 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
567 else:
569 def check_tz_naive(v: object) -> bool:
570 assert isinstance(v, datetime.datetime)
571 return v.tzinfo is None
573 s = _check_func(check_tz_naive, 'timezone naive', s)
574 else:
575 raise NotImplementedError('Constraining to a specific timezone is not yet supported')
576 elif isinstance(constraint, annotated_types.Interval): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
577 if constraint.ge: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
578 s = _apply_constraint(s, annotated_types.Ge(constraint.ge)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
579 if constraint.gt: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
580 s = _apply_constraint(s, annotated_types.Gt(constraint.gt)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
581 if constraint.le: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
582 s = _apply_constraint(s, annotated_types.Le(constraint.le)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
583 if constraint.lt: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
584 s = _apply_constraint(s, annotated_types.Lt(constraint.lt)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
585 assert s is not None 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
586 elif isinstance(constraint, annotated_types.Predicate): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
587 func = constraint.func 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
589 if func.__name__ == '<lambda>': 589 ↛ 607line 589 didn't jump to line 607 because the condition on line 589 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
590 # attempt to extract the source code for a lambda function
591 # to use as the function name in error messages
592 # TODO: is there a better way? should we just not do this?
593 import inspect 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
595 try: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
596 # remove ')' suffix, can use removesuffix once we drop 3.8
597 source = inspect.getsource(func).strip() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
598 if source.endswith(')'): 598 ↛ 600line 598 didn't jump to line 600 because the condition on line 598 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
599 source = source[:-1] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
600 lambda_source_code = '`' + ''.join(''.join(source.split('lambda ')[1:]).split(':')[1:]).strip() + '`' 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
601 except OSError: 1uoqravpstb
602 # stringified annotations
603 lambda_source_code = 'lambda' 1uoqravpstb
605 s = _check_func(func, lambda_source_code, s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
606 else:
607 s = _check_func(func, func.__name__, s)
608 elif isinstance(constraint, _NotEq): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
609 value = constraint.value 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
611 def check_not_eq(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
612 return operator.__ne__(v, value) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
614 s = _check_func(check_not_eq, f'!= {value}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
615 elif isinstance(constraint, _Eq): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
616 value = constraint.value 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
618 def check_eq(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
619 return operator.__eq__(v, value) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
621 s = _check_func(check_eq, f'== {value}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
622 elif isinstance(constraint, _In): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
623 values = constraint.values 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
625 def check_in(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
626 return operator.__contains__(values, v) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
628 s = _check_func(check_in, f'in {values}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
629 elif isinstance(constraint, _NotIn): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
630 values = constraint.values 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
632 def check_not_in(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
633 return operator.__not__(operator.__contains__(values, v)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
635 s = _check_func(check_not_in, f'not in {values}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
636 else:
637 assert isinstance(constraint, Pattern) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
638 if s and s['type'] == 'str': 638 ↛ 643line 638 didn't jump to line 643 because the condition on line 638 was always true1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
639 s = s.copy() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
640 s['pattern'] = constraint.pattern 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
641 else:
643 def check_pattern(v: object) -> bool:
644 assert isinstance(v, str)
645 return constraint.match(v) is not None
647 s = _check_func(check_pattern, f'~ {constraint.pattern}', s)
648 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
651class _SupportsRange(annotated_types.SupportsLe, annotated_types.SupportsGe, Protocol): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
652 pass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
655class _SupportsLen(Protocol): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
656 def __len__(self) -> int: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
659_NewOutGt = TypeVar('_NewOutGt', bound=annotated_types.SupportsGt) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
660_NewOutGe = TypeVar('_NewOutGe', bound=annotated_types.SupportsGe) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
661_NewOutLt = TypeVar('_NewOutLt', bound=annotated_types.SupportsLt) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
662_NewOutLe = TypeVar('_NewOutLe', bound=annotated_types.SupportsLe) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
663_NewOutLen = TypeVar('_NewOutLen', bound=_SupportsLen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
664_NewOutDiv = TypeVar('_NewOutDiv', bound=annotated_types.SupportsDiv) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
665_NewOutMod = TypeVar('_NewOutMod', bound=annotated_types.SupportsMod) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
666_NewOutDatetime = TypeVar('_NewOutDatetime', bound=datetime.datetime) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
667_NewOutInterval = TypeVar('_NewOutInterval', bound=_SupportsRange) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
668_OtherIn = TypeVar('_OtherIn') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn
669_OtherOut = TypeVar('_OtherOut') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn