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

1"""Experimental pipeline API functionality. Be careful with this API, it's subject to change.""" 

2 

3from __future__ import annotations 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

4 

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

15 

16import annotated_types 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

17from typing_extensions import Annotated 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

18 

19if TYPE_CHECKING: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

20 from pydantic_core import core_schema as cs 

21 

22 from pydantic import GetCoreSchemaHandler 

23 

24from pydantic._internal._internal_dataclass import slots_true as _slots_true 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

25 

26if sys.version_info < (3, 10): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

27 EllipsisType = type(Ellipsis) 1uwoxcyzABvCpD

28else: 

29 from types import EllipsisType 1qErFaefgdGHIJhijksKtLblmn

30 

31__all__ = ['validate_as', 'validate_as_deferred', 'transform'] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

32 

33_slots_frozen = {**_slots_true, 'frozen': True} 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

34 

35 

36@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

37class _ValidateAs: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

38 tp: type[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

39 strict: bool = False 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

40 

41 

42@dataclass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

43class _ValidateAsDefer: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

44 func: Callable[[], type[Any]] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

45 

46 @cached_property 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

47 def tp(self) -> type[Any]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

48 return self.func() 1oqrapstb

49 

50 

51@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

52class _Transform: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

53 func: Callable[[Any], Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

54 

55 

56@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

57class _PipelineOr: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

58 left: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

59 right: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

60 

61 

62@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

63class _PipelineAnd: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

64 left: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

65 right: _Pipeline[Any, Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

66 

67 

68@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

69class _Eq: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

70 value: Any 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

71 

72 

73@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

74class _NotEq: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

75 value: Any 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

76 

77 

78@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

79class _In: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

80 values: Container[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

81 

82 

83@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

84class _NotIn: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

85 values: Container[Any] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

86 

87 

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] 

106 

107 

108@dataclass(**_slots_frozen) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

109class _Constraint: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

110 constraint: _ConstraintAnnotation 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

111 

112 

113_Step = Union[_ValidateAs, _ValidateAsDefer, _Transform, _PipelineOr, _PipelineAnd, _Constraint] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

114 

115_InT = TypeVar('_InT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

116_OutT = TypeVar('_OutT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

117_NewOutT = TypeVar('_NewOutT') 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

118 

119 

120class _FieldTypeMarker: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

121 pass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

122 

123 

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.""" 

131 

132 _steps: tuple[_Step, ...] 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

133 

134 def transform( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

135 self, 

136 func: Callable[[_OutT], _NewOutT], 

137 ) -> _Pipeline[_InT, _NewOutT]: 

138 """Transform the output of the previous step. 

139 

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

144 

145 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

146 def validate_as(self, tp: type[_NewOutT], *, strict: bool = ...) -> _Pipeline[_InT, _NewOutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

147 

148 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

149 def validate_as(self, tp: EllipsisType, *, strict: bool = ...) -> _Pipeline[_InT, Any]: # type: ignore 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

150 ... 

151 

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. 

154 

155 If no type is provided, the type of the field is used. 

156 

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

163 

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. 

167 

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

171 

172 # constraints 

173 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

174 def constrain(self: _Pipeline[_InT, _NewOutGe], constraint: annotated_types.Ge) -> _Pipeline[_InT, _NewOutGe]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

175 

176 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

177 def constrain(self: _Pipeline[_InT, _NewOutGt], constraint: annotated_types.Gt) -> _Pipeline[_InT, _NewOutGt]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

178 

179 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

180 def constrain(self: _Pipeline[_InT, _NewOutLe], constraint: annotated_types.Le) -> _Pipeline[_InT, _NewOutLe]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

181 

182 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

183 def constrain(self: _Pipeline[_InT, _NewOutLt], constraint: annotated_types.Lt) -> _Pipeline[_InT, _NewOutLt]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

184 

185 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

186 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

187 self: _Pipeline[_InT, _NewOutLen], constraint: annotated_types.Len 1aefgcdhijkblmn

188 ) -> _Pipeline[_InT, _NewOutLen]: ... 1aefgcdhijkblmn

189 

190 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

191 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

192 self: _Pipeline[_InT, _NewOutT], constraint: annotated_types.MultipleOf 1aefgcdhijkblmn

193 ) -> _Pipeline[_InT, _NewOutT]: ... 1aefgcdhijkblmn

194 

195 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

196 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

197 self: _Pipeline[_InT, _NewOutDatetime], constraint: annotated_types.Timezone 1aefgcdhijkblmn

198 ) -> _Pipeline[_InT, _NewOutDatetime]: ... 1aefgcdhijkblmn

199 

200 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

201 def constrain(self: _Pipeline[_InT, _OutT], constraint: annotated_types.Predicate) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

202 

203 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

204 def constrain( 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

205 self: _Pipeline[_InT, _NewOutInterval], constraint: annotated_types.Interval 1aefgcdhijkblmn

206 ) -> _Pipeline[_InT, _NewOutInterval]: ... 1aefgcdhijkblmn

207 

208 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

209 def constrain(self: _Pipeline[_InT, _OutT], constraint: _Eq) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

210 

211 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

212 def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotEq) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

213 

214 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

215 def constrain(self: _Pipeline[_InT, _OutT], constraint: _In) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

216 

217 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

218 def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotIn) -> _Pipeline[_InT, _OutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

219 

220 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

221 def constrain(self: _Pipeline[_InT, _NewOutT], constraint: Pattern[str]) -> _Pipeline[_InT, _NewOutT]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

222 

223 def constrain(self, constraint: _ConstraintAnnotation) -> Any: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

224 """Constrain a value to meet a certain condition. 

225 

226 We support most conditions from `annotated_types`, as well as regular expressions. 

227 

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

232 

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

236 

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

240 

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

244 

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

248 

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

252 

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

256 

257 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

258 def multiple_of(self: _Pipeline[_InT, _NewOutDiv], multiple_of: _NewOutDiv) -> _Pipeline[_InT, _NewOutDiv]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

259 

260 @overload 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

261 def multiple_of(self: _Pipeline[_InT, _NewOutMod], multiple_of: _NewOutMod) -> _Pipeline[_InT, _NewOutMod]: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

262 

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

266 

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

270 

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

274 

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

278 

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

282 

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

286 

287 def datetime_tz_aware(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

288 return self.constrain(annotated_types.Timezone(...)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

289 

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 

294 

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)) 

299 

300 # string methods 

301 def str_lower(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

302 return self.transform(str.lower) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

303 

304 def str_upper(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

305 return self.transform(str.upper) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

306 

307 def str_title(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

308 return self.transform(str.title) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

309 

310 def str_strip(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

311 return self.transform(str.strip) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

312 

313 def str_pattern(self: _Pipeline[_InT, str], pattern: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

314 return self.constrain(re.compile(pattern)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

315 

316 def str_contains(self: _Pipeline[_InT, str], substring: str) -> _Pipeline[_InT, str]: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

317 return self.predicate(lambda v: substring in v) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

318 

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

321 

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

324 

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

329 

330 __or__ = otherwise 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

331 

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

335 

336 __and__ = then 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

337 

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

340 

341 queue = deque(self._steps) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

342 

343 s = None 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

344 

345 while queue: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

346 step = queue.popleft() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

347 s = _apply_step(step, s, handler, source_type) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

348 

349 s = s or cs.any_schema() 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

350 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

351 

352 def __supports_type__(self, _: _OutT) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

353 raise NotImplementedError 

354 

355 

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

359 

360 

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

365 

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

370 

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

375 

376 

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

379 

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

394 

395 

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

404 

405 from pydantic import Strict 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

406 

407 if tp is _FieldTypeMarker: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

408 return handler(source_type) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

409 

410 if strict: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

411 tp = Annotated[tp, Strict()] # type: ignore 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

412 

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

417 

418 

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

423 

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) 

426 

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

440 

441 return cs.no_info_after_validator_function(func, s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

442 

443 

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: 

459 

460 def check_gt(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

461 return v > gt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

462 

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

474 

475 def check_ge(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

476 return v >= ge 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

477 

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

489 

490 def check_lt(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

491 return v < lt 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

492 

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

504 

505 def check_le(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

506 return v <= le 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

507 

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

512 

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

527 

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

532 

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

544 

545 def check_multiple_of(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

546 return v % multiple_of == 0 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

547 

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

551 

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: 

557 

558 def check_tz_aware(v: object) -> bool: 

559 assert isinstance(v, datetime.datetime) 

560 return v.tzinfo is not None 

561 

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: 

568 

569 def check_tz_naive(v: object) -> bool: 

570 assert isinstance(v, datetime.datetime) 

571 return v.tzinfo is None 

572 

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

588 

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

594 

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

604 

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

610 

611 def check_not_eq(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

612 return operator.__ne__(v, value) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

613 

614 s = _check_func(check_not_eq, f'!= {value}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

615 elif isinstance(constraint, _Eq): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

616 value = constraint.value 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

617 

618 def check_eq(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

619 return operator.__eq__(v, value) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

620 

621 s = _check_func(check_eq, f'== {value}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

622 elif isinstance(constraint, _In): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

623 values = constraint.values 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

624 

625 def check_in(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

626 return operator.__contains__(values, v) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

627 

628 s = _check_func(check_in, f'in {values}', s) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

629 elif isinstance(constraint, _NotIn): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

630 values = constraint.values 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

631 

632 def check_not_in(v: Any) -> bool: 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

633 return operator.__not__(operator.__contains__(values, v)) 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

634 

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: 

642 

643 def check_pattern(v: object) -> bool: 

644 assert isinstance(v, str) 

645 return constraint.match(v) is not None 

646 

647 s = _check_func(check_pattern, f'~ {constraint.pattern}', s) 

648 return s 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

649 

650 

651class _SupportsRange(annotated_types.SupportsLe, annotated_types.SupportsGe, Protocol): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

652 pass 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

653 

654 

655class _SupportsLen(Protocol): 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

656 def __len__(self) -> int: ... 1uwoxqErFaefgcdyzABGHIJhijkvCpDsKtLblmn

657 

658 

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