fluent-checks 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from itertools import repeat
|
|
3
|
+
from threading import Event, Thread
|
|
4
|
+
import time
|
|
5
|
+
from typing import Callable, Optional, Self, final, override
|
|
6
|
+
|
|
7
|
+
type Condition = Callable[[], bool]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Check(ABC):
|
|
11
|
+
def __init__(self, condition: Condition) -> None:
|
|
12
|
+
super().__init__()
|
|
13
|
+
self._condition: Condition = condition
|
|
14
|
+
|
|
15
|
+
@final
|
|
16
|
+
def as_bool(self) -> bool:
|
|
17
|
+
return bool(self)
|
|
18
|
+
|
|
19
|
+
def with_delay(self, delay: float) -> "DelayedCheck":
|
|
20
|
+
return DelayedCheck(self, delay)
|
|
21
|
+
|
|
22
|
+
def succeeds_within(self, times: int) -> "RepeatingOrCheck":
|
|
23
|
+
return RepeatingOrCheck(self, times)
|
|
24
|
+
|
|
25
|
+
def is_consistent_for(self, times: int) -> "RepeatingAndCheck":
|
|
26
|
+
return RepeatingAndCheck(self, times)
|
|
27
|
+
|
|
28
|
+
def sometimes(self) -> "LoopingOrCheck":
|
|
29
|
+
return LoopingOrCheck(self)
|
|
30
|
+
|
|
31
|
+
def always(self) -> "LoopingAndCheck":
|
|
32
|
+
return LoopingAndCheck(self)
|
|
33
|
+
|
|
34
|
+
def as_waiting(self) -> "WaitingCheck":
|
|
35
|
+
return WaitingCheck(self)
|
|
36
|
+
|
|
37
|
+
def wait_for(self):
|
|
38
|
+
return self.as_waiting().wait()
|
|
39
|
+
|
|
40
|
+
def with_deadline(self, deadline: float) -> "DeadlineCheck":
|
|
41
|
+
return DeadlineCheck(self, deadline)
|
|
42
|
+
|
|
43
|
+
def with_timeout(self, timeout: float) -> "TimeoutCheck":
|
|
44
|
+
return TimeoutCheck(self, timeout)
|
|
45
|
+
|
|
46
|
+
def raises(self, exception: type[Exception]) -> "RaisesCheck":
|
|
47
|
+
return RaisesCheck(self, exception)
|
|
48
|
+
|
|
49
|
+
def __and__(self, other: Self) -> "Check":
|
|
50
|
+
return AndCheck(self, other)
|
|
51
|
+
|
|
52
|
+
def __or__(self, other: Self) -> "Check":
|
|
53
|
+
return OrCheck(self, other)
|
|
54
|
+
|
|
55
|
+
def __invert__(self) -> "Check":
|
|
56
|
+
return InvertedCheck(self)
|
|
57
|
+
|
|
58
|
+
def __bool__(self) -> bool:
|
|
59
|
+
return self._condition()
|
|
60
|
+
|
|
61
|
+
def __repr__(self) -> str:
|
|
62
|
+
try:
|
|
63
|
+
result = self.as_bool()
|
|
64
|
+
except Exception:
|
|
65
|
+
result = "<error>"
|
|
66
|
+
return f"Check({result})"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AllCheck(Check):
|
|
70
|
+
def __init__(self, *checks: Check) -> None:
|
|
71
|
+
super().__init__(condition=lambda: all(checks))
|
|
72
|
+
self._checks: tuple[Check, ...] = checks
|
|
73
|
+
|
|
74
|
+
def __repr__(self) -> str:
|
|
75
|
+
return " and ".join([check.__repr__() for check in self._checks])
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AnyCheck(Check):
|
|
79
|
+
def __init__(self, *checks: Check) -> None:
|
|
80
|
+
super().__init__(condition=lambda: any(checks))
|
|
81
|
+
self._checks: tuple[Check, ...] = checks
|
|
82
|
+
|
|
83
|
+
def __repr__(self) -> str:
|
|
84
|
+
return " or ".join([check.__repr__() for check in self._checks])
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class AndCheck(AllCheck):
|
|
88
|
+
def __init__(self, left: Check, right: Check) -> None:
|
|
89
|
+
super().__init__(*[left, right])
|
|
90
|
+
self._left: Check = left
|
|
91
|
+
self._right: Check = right
|
|
92
|
+
|
|
93
|
+
def __repr__(self) -> str:
|
|
94
|
+
return f"{self._left.__repr__()} and {self._right.__repr__()}"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class OrCheck(AnyCheck):
|
|
98
|
+
def __init__(self, left: Check, right: Check) -> None:
|
|
99
|
+
super().__init__(*[left, right])
|
|
100
|
+
self._left: Check = left
|
|
101
|
+
self._right: Check = right
|
|
102
|
+
|
|
103
|
+
def __repr__(self) -> str:
|
|
104
|
+
return f"{self._left.__repr__()} or {self._right.__repr__()}"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class InvertedCheck(Check):
|
|
108
|
+
def __init__(self, check: Check) -> None:
|
|
109
|
+
super().__init__(condition=lambda: not check)
|
|
110
|
+
self._inverted: Check = check
|
|
111
|
+
|
|
112
|
+
def __repr__(self) -> str:
|
|
113
|
+
return f"not {self._inverted.__repr__()}"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class DelayedCheck(Check):
|
|
117
|
+
def __init__(self, check: Check, delay: float) -> None:
|
|
118
|
+
super().__init__(condition=lambda: bool(check))
|
|
119
|
+
self._check: Check = check
|
|
120
|
+
self._delay: float = delay
|
|
121
|
+
|
|
122
|
+
@override
|
|
123
|
+
def __bool__(self) -> bool:
|
|
124
|
+
time.sleep(self._delay)
|
|
125
|
+
return self._condition()
|
|
126
|
+
|
|
127
|
+
def __repr__(self) -> str:
|
|
128
|
+
return f"DelayedCheck({self._check.__repr__()}, {self._delay})"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RepeatingAndCheck(AllCheck):
|
|
132
|
+
def __init__(self, check: Check, times: int) -> None:
|
|
133
|
+
super().__init__(*repeat[Check](check, times))
|
|
134
|
+
self._check: Check = check
|
|
135
|
+
self._times: int = times
|
|
136
|
+
|
|
137
|
+
def __repr__(self) -> str:
|
|
138
|
+
return f"RepeatingAndCheck({self._check.__repr__()}, {self._times})"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class RepeatingOrCheck(AnyCheck):
|
|
142
|
+
def __init__(self, check: Check, times: int) -> None:
|
|
143
|
+
super().__init__(*repeat[Check](check, times))
|
|
144
|
+
self._check: Check = check
|
|
145
|
+
self._times: int = times
|
|
146
|
+
|
|
147
|
+
def __repr__(self) -> str:
|
|
148
|
+
return f"RepeatingOrCheck({self._check.__repr__()}, {self._times})"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class LoopingCheck(Check):
|
|
152
|
+
def __init__(self, check: Check, initial_result: bool) -> None:
|
|
153
|
+
super().__init__(lambda: bool(check))
|
|
154
|
+
self._check: Check = check
|
|
155
|
+
self._result = initial_result
|
|
156
|
+
self._stop_event = Event()
|
|
157
|
+
self._thread: Optional[Thread] = None
|
|
158
|
+
|
|
159
|
+
@abstractmethod
|
|
160
|
+
def _loop(self) -> None:
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
def __enter__(self) -> Self:
|
|
164
|
+
if self._thread is None:
|
|
165
|
+
self._thread = Thread(target=self._loop)
|
|
166
|
+
self._thread.start()
|
|
167
|
+
return self
|
|
168
|
+
|
|
169
|
+
def __exit__(self, type, value, traceback) -> None:
|
|
170
|
+
if self._thread is not None and self._thread.is_alive():
|
|
171
|
+
self._stop_event.set()
|
|
172
|
+
self._thread.join()
|
|
173
|
+
self._thread = None
|
|
174
|
+
|
|
175
|
+
@override
|
|
176
|
+
def __bool__(self) -> bool:
|
|
177
|
+
return self._result
|
|
178
|
+
|
|
179
|
+
def __repr__(self) -> str:
|
|
180
|
+
return f"LoopingCheck({self._check.__repr__()})"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LoopingAndCheck(LoopingCheck):
|
|
184
|
+
def __init__(self, check: Check) -> None:
|
|
185
|
+
super().__init__(check, initial_result=True)
|
|
186
|
+
|
|
187
|
+
@override
|
|
188
|
+
def _loop(self) -> None:
|
|
189
|
+
while not self._stop_event.is_set():
|
|
190
|
+
if not self._check.as_bool():
|
|
191
|
+
self._result = False
|
|
192
|
+
time.sleep(0.01)
|
|
193
|
+
|
|
194
|
+
def __repr__(self) -> str:
|
|
195
|
+
return f"LoopingAndCheck({self._check.__repr__()})"
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class LoopingOrCheck(LoopingCheck):
|
|
199
|
+
def __init__(self, check: Check) -> None:
|
|
200
|
+
super().__init__(check, initial_result=False)
|
|
201
|
+
|
|
202
|
+
@override
|
|
203
|
+
def _loop(self) -> None:
|
|
204
|
+
while not self._stop_event.is_set():
|
|
205
|
+
if self._check.as_bool():
|
|
206
|
+
self._result = True
|
|
207
|
+
time.sleep(0.01)
|
|
208
|
+
|
|
209
|
+
def __repr__(self) -> str:
|
|
210
|
+
return f"LoopingOrCheck({self._check.__repr__()})"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class WaitingCheck(Check):
|
|
214
|
+
def __init__(self, check: Check) -> None:
|
|
215
|
+
super().__init__(lambda: bool(check))
|
|
216
|
+
self._check: Check = check
|
|
217
|
+
|
|
218
|
+
@override
|
|
219
|
+
def __bool__(self) -> bool:
|
|
220
|
+
while not self._check:
|
|
221
|
+
continue
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
def wait(self) -> None:
|
|
225
|
+
bool(self)
|
|
226
|
+
|
|
227
|
+
def __repr__(self) -> str:
|
|
228
|
+
return f"WatingCheck({self._check.__repr__()})"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class DeadlineCheck(Check):
|
|
232
|
+
def __init__(self, check: Check, deadline: float) -> None:
|
|
233
|
+
super().__init__(lambda: bool(check) if time.time() < deadline else False)
|
|
234
|
+
self._check: Check = check
|
|
235
|
+
self._deadline: float = deadline
|
|
236
|
+
|
|
237
|
+
def __repr__(self) -> str:
|
|
238
|
+
return f"DeadlineCheck({self._check.__repr__()}, {self._deadline})"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class TimeoutCheck(DeadlineCheck):
|
|
242
|
+
def __init__(self, check: Check, timeout: float) -> None:
|
|
243
|
+
super().__init__(check, time.time() + timeout)
|
|
244
|
+
self._timeout: float = timeout
|
|
245
|
+
|
|
246
|
+
def __repr__(self) -> str:
|
|
247
|
+
return f"TimeoutCheck({self._check.__repr__()}, {self._timeout})"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class RaisesCheck(Check):
|
|
251
|
+
def __init__(self, check: Check, exception: type[Exception]) -> None:
|
|
252
|
+
super().__init__(condition=lambda: bool(check))
|
|
253
|
+
self._check: Check = check
|
|
254
|
+
self._exception: type[Exception] = exception
|
|
255
|
+
|
|
256
|
+
def __bool__(self) -> bool:
|
|
257
|
+
try:
|
|
258
|
+
self._condition()
|
|
259
|
+
return False
|
|
260
|
+
except self._exception:
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
def __repr__(self) -> str:
|
|
264
|
+
return f"RaisesCheck({self._check.__repr__()}, {self._exception.__name__})"
|
fluent_checks/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fluent-checks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A library for creating fluent, readable, and composable checks for your tests or application logic.
|
|
5
|
+
Keywords: fluent,checks,testing,assertions,validation
|
|
6
|
+
Author: vantorrewannes
|
|
7
|
+
Author-email: vantorrewannes <vantorrewannes@gmail.com>
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Software Development :: Testing
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Project-URL: Bug Tracker, https://github.com/VantorreWannes/fluent-checks/issues
|
|
17
|
+
Project-URL: Homepage, https://github.com/VantorreWannes/fluent-checks
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# fluent-checks
|
|
21
|
+
A simple, lightweight library for creating fluent, readable, and chainable checks in Python.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
fluent_checks/__init__.py,sha256=PASUV-AhQlKCxoINTREjcmJhhViBUqim3OM6HOMqoTg,7720
|
|
2
|
+
fluent_checks/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
fluent_checks-0.1.0.dist-info/WHEEL,sha256=4n27za1eEkOnA7dNjN6C5-O2rUiw6iapszm14Uj-Qmk,79
|
|
4
|
+
fluent_checks-0.1.0.dist-info/METADATA,sha256=_8MUtV5lmMr7iSVzhKe-CEei31zUW6pLw22LGIOiUH8,943
|
|
5
|
+
fluent_checks-0.1.0.dist-info/RECORD,,
|