DefStream 0.1.13__44-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.
- DefStream/Broker/Configuration.py +18 -0
- DefStream/Broker/Connection.py +141 -0
- DefStream/Broker/Consumer.py +59 -0
- DefStream/Broker/Errors.py +14 -0
- DefStream/Broker/Event.py +35 -0
- DefStream/Broker/Implements/__init__.py +1 -0
- DefStream/Broker/Policies.py +25 -0
- DefStream/Broker/Priority.py +26 -0
- DefStream/Broker/Protocol.py +45 -0
- DefStream/Broker/__init__.py +59 -0
- DefStream/Descriptor/DataObject.py +409 -0
- DefStream/Descriptor/Types.py +248 -0
- DefStream/Descriptor/Value.py +188 -0
- DefStream/Descriptor/__init__.py +172 -0
- DefStream/Inspector/Extension.py +69 -0
- DefStream/Inspector/Extensions/Console.html +373 -0
- DefStream/Inspector/Extensions/Console.py +415 -0
- DefStream/Inspector/Extensions/Dashboard.html +206 -0
- DefStream/Inspector/Extensions/Dashboard.py +124 -0
- DefStream/Inspector/Extensions/HealthCheck.py +107 -0
- DefStream/Inspector/Extensions/Metrics.py +316 -0
- DefStream/Inspector/Extensions/Specifications.py +176 -0
- DefStream/Inspector/Extensions/__init__.py +13 -0
- DefStream/Inspector/Inspector.py +347 -0
- DefStream/Inspector/Response.py +14 -0
- DefStream/Inspector/__init__.py +9 -0
- DefStream/Validator/Pattern.py +36 -0
- DefStream/Validator/Patterns/Array.py +30 -0
- DefStream/Validator/Patterns/Common.py +93 -0
- DefStream/Validator/Patterns/Compare.py +180 -0
- DefStream/Validator/Patterns/Condition.py +60 -0
- DefStream/Validator/Patterns/DataObject.py +85 -0
- DefStream/Validator/Patterns/DateTime.py +111 -0
- DefStream/Validator/Patterns/Number.py +43 -0
- DefStream/Validator/Patterns/Object.py +150 -0
- DefStream/Validator/Patterns/Size.py +110 -0
- DefStream/Validator/Patterns/String.py +85 -0
- DefStream/Validator/Patterns/Type.py +350 -0
- DefStream/Validator/Patterns/__init__.py +137 -0
- DefStream/Validator/Validate.py +29 -0
- DefStream/Validator/Validator.py +30 -0
- DefStream/Validator/__init__.py +11 -0
- DefStream/Worker/Executor.py +371 -0
- DefStream/Worker/Message.py +180 -0
- DefStream/Worker/Pool.py +245 -0
- DefStream/Worker/Run.py +357 -0
- DefStream/Worker/Specification.py +87 -0
- DefStream/Worker/Startup.py +174 -0
- DefStream/Worker/Worker.py +421 -0
- DefStream/Worker/__init__.py +47 -0
- DefStream/__init__.py +0 -0
- defstream-0.1.13.dist-info/METADATA +679 -0
- defstream-0.1.13.dist-info/RECORD +56 -0
- defstream-0.1.13.dist-info/WHEEL +6 -0
- defstream-0.1.13.dist-info/licenses/LICENSE +674 -0
- defstream-0.1.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .HealthCheck import HealthCheck
|
|
2
|
+
from .Metrics import Metrics
|
|
3
|
+
from .Specifications import Specifications
|
|
4
|
+
from .Console import Console
|
|
5
|
+
from .Dashboard import Dashboard
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'HealthCheck',
|
|
9
|
+
'Metrics',
|
|
10
|
+
'Specifications',
|
|
11
|
+
'Console',
|
|
12
|
+
'Dashboard',
|
|
13
|
+
)
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
from socketserver import ThreadingMixIn
|
|
3
|
+
from sys import exc_info
|
|
4
|
+
from threading import Thread
|
|
5
|
+
from time import monotonic
|
|
6
|
+
from traceback import extract_tb
|
|
7
|
+
from typing import (
|
|
8
|
+
Callable,
|
|
9
|
+
Dict,
|
|
10
|
+
Iterable,
|
|
11
|
+
Optional,
|
|
12
|
+
Sequence,
|
|
13
|
+
TYPE_CHECKING,
|
|
14
|
+
Tuple,
|
|
15
|
+
)
|
|
16
|
+
from wsgiref.simple_server import (
|
|
17
|
+
WSGIRequestHandler,
|
|
18
|
+
ServerHandler,
|
|
19
|
+
WSGIServer,
|
|
20
|
+
make_server,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from .Extension import Extension
|
|
24
|
+
from .Response import Response
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from ..Worker.Worker import Worker
|
|
28
|
+
|
|
29
|
+
__all__ = (
|
|
30
|
+
'Inspector',
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Server(
|
|
35
|
+
ThreadingMixIn,
|
|
36
|
+
WSGIServer,
|
|
37
|
+
):
|
|
38
|
+
logger = getLogger('DefStream.Inspector')
|
|
39
|
+
|
|
40
|
+
daemon_threads = True
|
|
41
|
+
block_on_close = False
|
|
42
|
+
|
|
43
|
+
def get_request(self):
|
|
44
|
+
connection, address = super().get_request()
|
|
45
|
+
connection.settimeout(5)
|
|
46
|
+
return connection, address
|
|
47
|
+
|
|
48
|
+
def handle_error(
|
|
49
|
+
self,
|
|
50
|
+
request,
|
|
51
|
+
client_address,
|
|
52
|
+
) -> None:
|
|
53
|
+
_, error, traceback = exc_info()
|
|
54
|
+
self.logger.error(
|
|
55
|
+
'HTTP connection handling failed: error_type=%s\n%s',
|
|
56
|
+
type(error).__name__,
|
|
57
|
+
''.join(
|
|
58
|
+
f' {frame.filename}:{frame.lineno} ({frame.name})\n'
|
|
59
|
+
for frame in extract_tb(traceback)
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class InspectorServerHandler(ServerHandler):
|
|
66
|
+
def log_exception(
|
|
67
|
+
self,
|
|
68
|
+
exc_info,
|
|
69
|
+
) -> None:
|
|
70
|
+
_, error, traceback = exc_info
|
|
71
|
+
self.request_handler.logger.error(
|
|
72
|
+
'WSGI request handling failed: error_type=%s\n%s',
|
|
73
|
+
type(error).__name__,
|
|
74
|
+
''.join(
|
|
75
|
+
f' {frame.filename}:{frame.lineno} ({frame.name})\n'
|
|
76
|
+
for frame in extract_tb(traceback)
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class RequestHandler(WSGIRequestHandler):
|
|
83
|
+
logger = getLogger('DefStream.Inspector')
|
|
84
|
+
|
|
85
|
+
def handle(self) -> None:
|
|
86
|
+
self.started_at = monotonic()
|
|
87
|
+
self.path = ''
|
|
88
|
+
self.raw_requestline = self.rfile.readline(65537)
|
|
89
|
+
if len(self.raw_requestline) > 65536:
|
|
90
|
+
self.requestline = ''
|
|
91
|
+
self.request_version = ''
|
|
92
|
+
self.command = ''
|
|
93
|
+
self.send_error(414)
|
|
94
|
+
return
|
|
95
|
+
if not self.raw_requestline:
|
|
96
|
+
self.close_connection = True
|
|
97
|
+
return
|
|
98
|
+
if not self.parse_request():
|
|
99
|
+
return
|
|
100
|
+
self.logger.debug(
|
|
101
|
+
'HTTP request received: method=%s path=%s',
|
|
102
|
+
self.command,
|
|
103
|
+
self.path.partition('?')[0],
|
|
104
|
+
)
|
|
105
|
+
handler = InspectorServerHandler(
|
|
106
|
+
self.rfile,
|
|
107
|
+
self.wfile,
|
|
108
|
+
self.get_stderr(),
|
|
109
|
+
self.get_environ(),
|
|
110
|
+
multithread=True,
|
|
111
|
+
)
|
|
112
|
+
handler.request_handler = self
|
|
113
|
+
handler.run(self.server.get_app())
|
|
114
|
+
|
|
115
|
+
def log_request(
|
|
116
|
+
self,
|
|
117
|
+
code='-',
|
|
118
|
+
size='-',
|
|
119
|
+
) -> None:
|
|
120
|
+
self.logger.info(
|
|
121
|
+
'HTTP response sent: method=%s path=%s status=%s bytes=%s duration=%s',
|
|
122
|
+
self.command,
|
|
123
|
+
self.path.partition('?')[0],
|
|
124
|
+
code,
|
|
125
|
+
size if size is not None else '-',
|
|
126
|
+
monotonic() - self.started_at,
|
|
127
|
+
)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
def log_message(
|
|
131
|
+
self,
|
|
132
|
+
format: str,
|
|
133
|
+
*args,
|
|
134
|
+
) -> None:
|
|
135
|
+
self.logger.warning('HTTP protocol error or request timeout')
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class Inspector:
|
|
140
|
+
logger = getLogger('DefStream.Inspector')
|
|
141
|
+
|
|
142
|
+
def __init__(
|
|
143
|
+
self,
|
|
144
|
+
host: str = '127.0.0.1',
|
|
145
|
+
port: int = 9999,
|
|
146
|
+
exts: Sequence[Extension] = (),
|
|
147
|
+
):
|
|
148
|
+
if isinstance(
|
|
149
|
+
port,
|
|
150
|
+
bool,
|
|
151
|
+
) or not isinstance(
|
|
152
|
+
port,
|
|
153
|
+
int,
|
|
154
|
+
) or not 0 <= port <= 65535:
|
|
155
|
+
raise ValueError('Inspector port must be between 0 and 65535')
|
|
156
|
+
self.host = host
|
|
157
|
+
self.port = port
|
|
158
|
+
self.exts: Dict[str, Extension] = {}
|
|
159
|
+
for ext in exts:
|
|
160
|
+
if not isinstance(
|
|
161
|
+
ext,
|
|
162
|
+
Extension,
|
|
163
|
+
):
|
|
164
|
+
raise TypeError('Inspector extensions must inherit Extension')
|
|
165
|
+
if ext.path in self.exts:
|
|
166
|
+
raise ValueError('Duplicate Inspector path: ' + ext.path)
|
|
167
|
+
self.exts[ext.path] = ext
|
|
168
|
+
self.logger.debug(
|
|
169
|
+
'Inspector extension registered: class=%s path=%s',
|
|
170
|
+
type(ext).__name__,
|
|
171
|
+
ext.path,
|
|
172
|
+
)
|
|
173
|
+
self.worker: Optional['Worker'] = None
|
|
174
|
+
self.server: Optional[WSGIServer] = None
|
|
175
|
+
self.thread: Optional[Thread] = None
|
|
176
|
+
|
|
177
|
+
def start(
|
|
178
|
+
self,
|
|
179
|
+
worker: 'Worker',
|
|
180
|
+
) -> None:
|
|
181
|
+
if self.worker is not None:
|
|
182
|
+
raise RuntimeError('Inspector can only be attached and started once')
|
|
183
|
+
self.worker = worker
|
|
184
|
+
for ext in self.exts.values():
|
|
185
|
+
self.logger.debug(
|
|
186
|
+
'Starting Inspector extension: class=%s path=%s',
|
|
187
|
+
type(ext).__name__,
|
|
188
|
+
ext.path,
|
|
189
|
+
)
|
|
190
|
+
try:
|
|
191
|
+
ext.start(self)
|
|
192
|
+
except Exception as error:
|
|
193
|
+
self.logger.error(
|
|
194
|
+
'Inspector extension startup failed: class=%s path=%s error=%s',
|
|
195
|
+
type(ext).__name__,
|
|
196
|
+
ext.path,
|
|
197
|
+
type(error).__name__,
|
|
198
|
+
)
|
|
199
|
+
raise
|
|
200
|
+
try:
|
|
201
|
+
server = make_server(
|
|
202
|
+
self.host,
|
|
203
|
+
self.port,
|
|
204
|
+
self,
|
|
205
|
+
server_class=Server,
|
|
206
|
+
handler_class=RequestHandler,
|
|
207
|
+
)
|
|
208
|
+
except Exception as error:
|
|
209
|
+
self.logger.error(
|
|
210
|
+
'Inspector HTTP server binding failed: error=%s',
|
|
211
|
+
type(error).__name__,
|
|
212
|
+
)
|
|
213
|
+
raise
|
|
214
|
+
self.server = server
|
|
215
|
+
thread = Thread(
|
|
216
|
+
target=self.serve,
|
|
217
|
+
daemon=True,
|
|
218
|
+
)
|
|
219
|
+
self.thread = thread
|
|
220
|
+
try:
|
|
221
|
+
thread.start()
|
|
222
|
+
except BaseException:
|
|
223
|
+
self.logger.error('Inspector HTTP server thread startup failed')
|
|
224
|
+
server.server_close()
|
|
225
|
+
raise
|
|
226
|
+
self.logger.info(
|
|
227
|
+
'Inspector HTTP server started: host=%s port=%s',
|
|
228
|
+
server.server_address[0],
|
|
229
|
+
server.server_port,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def serve(self) -> None:
|
|
233
|
+
server = self.server
|
|
234
|
+
if server is None:
|
|
235
|
+
raise RuntimeError('Inspector server is not initialized')
|
|
236
|
+
try:
|
|
237
|
+
server.serve_forever(poll_interval=0.1)
|
|
238
|
+
except Exception as error:
|
|
239
|
+
self.logger.error(
|
|
240
|
+
'Inspector HTTP server failed: error=%s',
|
|
241
|
+
type(error).__name__,
|
|
242
|
+
)
|
|
243
|
+
if self.worker is not None:
|
|
244
|
+
self.worker.stop()
|
|
245
|
+
raise
|
|
246
|
+
|
|
247
|
+
def stop(self) -> None:
|
|
248
|
+
server = self.server
|
|
249
|
+
thread = self.thread
|
|
250
|
+
if server is not None:
|
|
251
|
+
try:
|
|
252
|
+
if thread is not None and thread.is_alive():
|
|
253
|
+
self.logger.info('Stopping Inspector HTTP server')
|
|
254
|
+
server.shutdown()
|
|
255
|
+
thread.join()
|
|
256
|
+
finally:
|
|
257
|
+
server.server_close()
|
|
258
|
+
self.logger.info('Inspector HTTP server stopped')
|
|
259
|
+
|
|
260
|
+
def begin(
|
|
261
|
+
self,
|
|
262
|
+
task: str,
|
|
263
|
+
) -> Tuple[Tuple[Extension, object], ...]:
|
|
264
|
+
return tuple(
|
|
265
|
+
(
|
|
266
|
+
ext,
|
|
267
|
+
ext.begin(task),
|
|
268
|
+
)
|
|
269
|
+
for ext in self.exts.values()
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def end(
|
|
273
|
+
self,
|
|
274
|
+
task: str,
|
|
275
|
+
contexts: Tuple[Tuple[Extension, object], ...],
|
|
276
|
+
success: Optional[bool],
|
|
277
|
+
) -> None:
|
|
278
|
+
for ext, context in contexts:
|
|
279
|
+
ext.end(
|
|
280
|
+
task,
|
|
281
|
+
context,
|
|
282
|
+
success,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
def __call__(
|
|
286
|
+
self,
|
|
287
|
+
environ: dict,
|
|
288
|
+
start_response: Callable,
|
|
289
|
+
) -> Iterable[bytes]:
|
|
290
|
+
method = environ['REQUEST_METHOD']
|
|
291
|
+
environ['wsgi.multithread'] = True
|
|
292
|
+
headers = []
|
|
293
|
+
ext = self.exts.get(environ['PATH_INFO'])
|
|
294
|
+
self.logger.debug(
|
|
295
|
+
'Routing Inspector request: method=%s path=%r extension=%s',
|
|
296
|
+
method,
|
|
297
|
+
environ['PATH_INFO'],
|
|
298
|
+
type(ext).__name__ if ext is not None else 'none',
|
|
299
|
+
)
|
|
300
|
+
methods = ext.methods if ext is not None else (
|
|
301
|
+
'GET',
|
|
302
|
+
'HEAD',
|
|
303
|
+
)
|
|
304
|
+
if method not in methods:
|
|
305
|
+
response = Response(
|
|
306
|
+
'405 Method Not Allowed',
|
|
307
|
+
b'Method not allowed\n',
|
|
308
|
+
'text/plain; charset=utf-8',
|
|
309
|
+
)
|
|
310
|
+
headers.append((
|
|
311
|
+
'Allow',
|
|
312
|
+
', '.join(methods),
|
|
313
|
+
))
|
|
314
|
+
else:
|
|
315
|
+
response = ext.handle(
|
|
316
|
+
self,
|
|
317
|
+
environ,
|
|
318
|
+
) if ext is not None else Response(
|
|
319
|
+
'404 Not Found',
|
|
320
|
+
b'Not found\n',
|
|
321
|
+
'text/plain; charset=utf-8',
|
|
322
|
+
)
|
|
323
|
+
headers.extend(response.headers)
|
|
324
|
+
headers.extend((
|
|
325
|
+
(
|
|
326
|
+
'Content-Type',
|
|
327
|
+
response.content_type,
|
|
328
|
+
),
|
|
329
|
+
(
|
|
330
|
+
'Content-Length',
|
|
331
|
+
str(len(response.body)),
|
|
332
|
+
),
|
|
333
|
+
(
|
|
334
|
+
'Cache-Control',
|
|
335
|
+
'no-store',
|
|
336
|
+
),
|
|
337
|
+
))
|
|
338
|
+
self.logger.debug(
|
|
339
|
+
'Inspector response built: status=%s bytes=%d',
|
|
340
|
+
response.status,
|
|
341
|
+
len(response.body),
|
|
342
|
+
)
|
|
343
|
+
start_response(
|
|
344
|
+
response.status,
|
|
345
|
+
headers,
|
|
346
|
+
)
|
|
347
|
+
return [b'' if method == 'HEAD' else response.body]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from abc import (
|
|
4
|
+
ABC,
|
|
5
|
+
abstractmethod,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
'Pattern',
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Pattern(ABC):
|
|
14
|
+
"""
|
|
15
|
+
Pattern Interface Class for Validator
|
|
16
|
+
|
|
17
|
+
Sample:
|
|
18
|
+
from operator import eq
|
|
19
|
+
|
|
20
|
+
class SamplePattern(Pattern):
|
|
21
|
+
def __init__(self, compare):
|
|
22
|
+
super(SamplePattern, self).__init__(error)
|
|
23
|
+
self.compare = compare
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
def __call__(self, parameter):
|
|
27
|
+
if not eq(parameter, self.compare):
|
|
28
|
+
raise ValueError('{} is not equal to {}'.format(parameter, self.condition))
|
|
29
|
+
return parameter # if true, return parameter
|
|
30
|
+
"""
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def __call__(self, parameter):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def __repr__(self):
|
|
36
|
+
return '{}()'.format(self.__class__.__name__)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ..Pattern import Pattern
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'IsElementOf',
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IsElementOf(Pattern):
|
|
13
|
+
def __init__(self, *args):
|
|
14
|
+
self.patterns = args
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
def __call__(self, parameter: Any) -> Any:
|
|
18
|
+
_ = list(parameter)
|
|
19
|
+
for pattern in self.patterns:
|
|
20
|
+
for i, e in enumerate(_):
|
|
21
|
+
_[i] = pattern(e)
|
|
22
|
+
return type(parameter)(_)
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return '{}({})'.format(
|
|
26
|
+
self.__class__.__name__,
|
|
27
|
+
', '.join(
|
|
28
|
+
[repr(p) for p in self.patterns]
|
|
29
|
+
)
|
|
30
|
+
)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ..Pattern import Pattern
|
|
4
|
+
|
|
5
|
+
from operator import eq
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'SetDefault',
|
|
9
|
+
'IsToNone',
|
|
10
|
+
'IsNotToNone',
|
|
11
|
+
'IsNotEmpty',
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SetDefault(Pattern):
|
|
16
|
+
def __init__(self, default):
|
|
17
|
+
self.default = default
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
def __call__(self, parameter):
|
|
21
|
+
if parameter == None:
|
|
22
|
+
return self.default
|
|
23
|
+
return parameter
|
|
24
|
+
|
|
25
|
+
def __repr__(self):
|
|
26
|
+
return '{}({})'.format(
|
|
27
|
+
self.__class__.__name__,
|
|
28
|
+
self.default
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class IsToNone(Pattern):
|
|
33
|
+
def __init__(self, *args):
|
|
34
|
+
self.patterns = args
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
def __call__(self, parameter):
|
|
38
|
+
if parameter is None:
|
|
39
|
+
return parameter
|
|
40
|
+
for pattern in self.patterns:
|
|
41
|
+
parameter = pattern(parameter)
|
|
42
|
+
return parameter
|
|
43
|
+
|
|
44
|
+
def __repr__(self):
|
|
45
|
+
return '{}({})'.format(
|
|
46
|
+
self.__class__.__name__,
|
|
47
|
+
', '.join([p.__repr__() for p in self.patterns])
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class IsNotToNone(Pattern):
|
|
52
|
+
def __init__(self, *args, error=None):
|
|
53
|
+
self.patterns = args
|
|
54
|
+
self.error = error
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
def __call__(self, parameter):
|
|
58
|
+
if parameter is None:
|
|
59
|
+
if self.error:
|
|
60
|
+
raise self.error
|
|
61
|
+
raise ValueError('{} is None'.format(parameter))
|
|
62
|
+
for pattern in self.patterns:
|
|
63
|
+
parameter = pattern(parameter)
|
|
64
|
+
return parameter
|
|
65
|
+
|
|
66
|
+
def __repr__(self):
|
|
67
|
+
return '{}({})'.format(
|
|
68
|
+
self.__class__.__name__,
|
|
69
|
+
', '.join([p.__repr__() for p in self.patterns])
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IsNotEmpty(Pattern):
|
|
74
|
+
def __init__(self, *args, error=None):
|
|
75
|
+
self.patterns = args
|
|
76
|
+
self.error = error
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
def __call__(self, parameter):
|
|
80
|
+
if eq(len(parameter), 0):
|
|
81
|
+
if self.error:
|
|
82
|
+
raise self.error
|
|
83
|
+
raise ValueError('{} must be not empty'.format(parameter))
|
|
84
|
+
for pattern in self.patterns:
|
|
85
|
+
parameter = pattern(parameter)
|
|
86
|
+
return parameter
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
return '{}({})'.format(
|
|
90
|
+
self.__class__.__name__,
|
|
91
|
+
', '.join([p.__repr__() for p in self.patterns])
|
|
92
|
+
)
|
|
93
|
+
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from ..Pattern import Pattern
|
|
4
|
+
|
|
5
|
+
from operator import eq, ne, gt, ge, lt, le
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
'IsIn',
|
|
9
|
+
'IsNotIn',
|
|
10
|
+
'IsEqualTo',
|
|
11
|
+
'IsNotEqualTo',
|
|
12
|
+
'IsGreaterThan',
|
|
13
|
+
'IsGreaterEqualTo',
|
|
14
|
+
'IsLessThan',
|
|
15
|
+
'IsLessEqualTo',
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IsIn(Pattern):
|
|
20
|
+
def __init__(self, *args, error=None):
|
|
21
|
+
self.compares = args
|
|
22
|
+
self.error = error
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
def __call__(self, parameter):
|
|
26
|
+
if parameter not in self.compares:
|
|
27
|
+
if self.error:
|
|
28
|
+
raise self.error
|
|
29
|
+
raise ValueError('{} must be in {}'.format(parameter, self.compares))
|
|
30
|
+
return parameter
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
return '{}({})'.format(
|
|
34
|
+
self.__class__.__name__,
|
|
35
|
+
', '.join(
|
|
36
|
+
[repr(c) for c in self.compares]
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class IsNotIn(Pattern):
|
|
42
|
+
def __init__(self, *args, error=None):
|
|
43
|
+
self.compares = args
|
|
44
|
+
self.error = error
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
def __call__(self, parameter):
|
|
48
|
+
if parameter in self.compares:
|
|
49
|
+
if self.error:
|
|
50
|
+
raise self.error
|
|
51
|
+
raise ValueError('{} must be not in {}'.format(parameter, self.compares))
|
|
52
|
+
return parameter
|
|
53
|
+
|
|
54
|
+
def __repr__(self):
|
|
55
|
+
return '{}({})'.format(
|
|
56
|
+
self.__class__.__name__,
|
|
57
|
+
', '.join(
|
|
58
|
+
[repr(c) for c in self.compares]
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class IsEqualTo(Pattern):
|
|
64
|
+
def __init__(self, equal, error=None):
|
|
65
|
+
self.equal = equal
|
|
66
|
+
self.error = error
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
def __call__(self, parameter):
|
|
70
|
+
if not eq(parameter, self.equal):
|
|
71
|
+
if self.error:
|
|
72
|
+
raise self.error
|
|
73
|
+
raise ValueError('{} must equal to {}'.format(parameter, self.equal))
|
|
74
|
+
return parameter
|
|
75
|
+
|
|
76
|
+
def __repr__(self):
|
|
77
|
+
return '{}({})'.format(
|
|
78
|
+
self.__class__.__name__,
|
|
79
|
+
self.equal
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class IsNotEqualTo(Pattern):
|
|
84
|
+
def __init__(self, equal, error=None):
|
|
85
|
+
self.equal = equal
|
|
86
|
+
self.error = error
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
def __call__(self, parameter):
|
|
90
|
+
if not ne(parameter, self.equal):
|
|
91
|
+
if self.error:
|
|
92
|
+
raise self.error
|
|
93
|
+
raise ValueError('{} must not equal to {}'.format(parameter, self.equal))
|
|
94
|
+
return parameter
|
|
95
|
+
|
|
96
|
+
def __repr__(self):
|
|
97
|
+
return '{}({})'.format(
|
|
98
|
+
self.__class__.__name__,
|
|
99
|
+
self.equal
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class IsGreaterEqualTo(Pattern):
|
|
104
|
+
def __init__(self, compare, error=None):
|
|
105
|
+
self.compare = compare
|
|
106
|
+
self.error = error
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
def __call__(self, parameter):
|
|
110
|
+
if not ge(parameter, self.compare):
|
|
111
|
+
if self.error:
|
|
112
|
+
raise self.error
|
|
113
|
+
raise ValueError('{} must be greater equal to {}'.format(parameter, self.compare))
|
|
114
|
+
return parameter
|
|
115
|
+
|
|
116
|
+
def __repr__(self):
|
|
117
|
+
return '{}({})'.format(
|
|
118
|
+
self.__class__.__name__,
|
|
119
|
+
self.compare
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class IsGreaterThan(Pattern):
|
|
124
|
+
def __init__(self, compare, error=None):
|
|
125
|
+
self.compare = compare
|
|
126
|
+
self.error = error
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
def __call__(self, parameter):
|
|
130
|
+
if not gt(parameter, self.compare):
|
|
131
|
+
if self.error:
|
|
132
|
+
raise self.error
|
|
133
|
+
raise ValueError('{} must be greater than {}'.format(parameter, self.compare))
|
|
134
|
+
return parameter
|
|
135
|
+
|
|
136
|
+
def __repr__(self):
|
|
137
|
+
return '{}({})'.format(
|
|
138
|
+
self.__class__.__name__,
|
|
139
|
+
self.compare
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class IsLessEqualTo(Pattern):
|
|
144
|
+
def __init__(self, compare, error=None):
|
|
145
|
+
self.compare = compare
|
|
146
|
+
self.error = error
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
def __call__(self, parameter):
|
|
150
|
+
if not le(parameter, self.compare):
|
|
151
|
+
if self.error:
|
|
152
|
+
raise self.error
|
|
153
|
+
raise ValueError('{} must be less equal to {}'.format(parameter, self.compare))
|
|
154
|
+
return parameter
|
|
155
|
+
|
|
156
|
+
def __repr__(self):
|
|
157
|
+
return '{}({})'.format(
|
|
158
|
+
self.__class__.__name__,
|
|
159
|
+
self.compare
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class IsLessThan(Pattern):
|
|
164
|
+
def __init__(self, compare, error=None):
|
|
165
|
+
self.compare = compare
|
|
166
|
+
self.error = error
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
def __call__(self, parameter):
|
|
170
|
+
if not lt(parameter, self.compare):
|
|
171
|
+
if self.error:
|
|
172
|
+
raise self.error
|
|
173
|
+
raise ValueError('{} must be less than {}'.format(parameter, self.compare))
|
|
174
|
+
return parameter
|
|
175
|
+
|
|
176
|
+
def __repr__(self):
|
|
177
|
+
return '{}({})'.format(
|
|
178
|
+
self.__class__.__name__,
|
|
179
|
+
self.compare
|
|
180
|
+
)
|