DefStream 0.1.16__56-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.
Files changed (75) hide show
  1. DefStream/Broker/Configuration.py +18 -0
  2. DefStream/Broker/Connection.py +141 -0
  3. DefStream/Broker/Consumer.py +59 -0
  4. DefStream/Broker/Errors.py +14 -0
  5. DefStream/Broker/Event.py +35 -0
  6. DefStream/Broker/Implements/RabbitMQ/Configuration.py +48 -0
  7. DefStream/Broker/Implements/RabbitMQ/Connection.py +542 -0
  8. DefStream/Broker/Implements/RabbitMQ/Consumer.py +359 -0
  9. DefStream/Broker/Implements/RabbitMQ/Event.py +127 -0
  10. DefStream/Broker/Implements/RabbitMQ/Exchange.py +278 -0
  11. DefStream/Broker/Implements/RabbitMQ/Policies.py +9 -0
  12. DefStream/Broker/Implements/RabbitMQ/Queue.py +319 -0
  13. DefStream/Broker/Implements/RabbitMQ/Serializer.py +61 -0
  14. DefStream/Broker/Implements/RabbitMQ/Stream.py +179 -0
  15. DefStream/Broker/Implements/RabbitMQ/Subscription.py +74 -0
  16. DefStream/Broker/Implements/RabbitMQ/__init__.py +47 -0
  17. DefStream/Broker/Implements/Redis/Configuration.py +59 -0
  18. DefStream/Broker/Implements/Redis/Connection.py +265 -0
  19. DefStream/Broker/Implements/Redis/Consumer.py +364 -0
  20. DefStream/Broker/Implements/Redis/Event.py +191 -0
  21. DefStream/Broker/Implements/Redis/Queue.py +284 -0
  22. DefStream/Broker/Implements/Redis/Serializer.py +48 -0
  23. DefStream/Broker/Implements/Redis/Subscription.py +46 -0
  24. DefStream/Broker/Implements/Redis/__init__.py +29 -0
  25. DefStream/Broker/Implements/__init__.py +1 -0
  26. DefStream/Broker/Policies.py +25 -0
  27. DefStream/Broker/Priority.py +26 -0
  28. DefStream/Broker/Protocol.py +45 -0
  29. DefStream/Broker/__init__.py +59 -0
  30. DefStream/Descriptor/DataObject.py +409 -0
  31. DefStream/Descriptor/Types.py +248 -0
  32. DefStream/Descriptor/Value.py +188 -0
  33. DefStream/Descriptor/__init__.py +172 -0
  34. DefStream/Inspector/Extension.py +69 -0
  35. DefStream/Inspector/Extensions/Console.html +373 -0
  36. DefStream/Inspector/Extensions/Console.py +415 -0
  37. DefStream/Inspector/Extensions/Metrics.py +316 -0
  38. DefStream/Inspector/Extensions/Ping.py +107 -0
  39. DefStream/Inspector/Extensions/Specifications.py +176 -0
  40. DefStream/Inspector/Extensions/Status.html +206 -0
  41. DefStream/Inspector/Extensions/Status.py +124 -0
  42. DefStream/Inspector/Extensions/__init__.py +13 -0
  43. DefStream/Inspector/Inspector.py +347 -0
  44. DefStream/Inspector/Response.py +14 -0
  45. DefStream/Inspector/__init__.py +9 -0
  46. DefStream/Validator/Pattern.py +36 -0
  47. DefStream/Validator/Patterns/Array.py +30 -0
  48. DefStream/Validator/Patterns/Common.py +92 -0
  49. DefStream/Validator/Patterns/Compare.py +180 -0
  50. DefStream/Validator/Patterns/Condition.py +60 -0
  51. DefStream/Validator/Patterns/DataObject.py +85 -0
  52. DefStream/Validator/Patterns/DateTime.py +111 -0
  53. DefStream/Validator/Patterns/Number.py +43 -0
  54. DefStream/Validator/Patterns/Object.py +150 -0
  55. DefStream/Validator/Patterns/Size.py +110 -0
  56. DefStream/Validator/Patterns/String.py +85 -0
  57. DefStream/Validator/Patterns/Type.py +350 -0
  58. DefStream/Validator/Patterns/__init__.py +137 -0
  59. DefStream/Validator/Validate.py +29 -0
  60. DefStream/Validator/Validator.py +30 -0
  61. DefStream/Validator/__init__.py +11 -0
  62. DefStream/Worker/Executor.py +371 -0
  63. DefStream/Worker/Message.py +180 -0
  64. DefStream/Worker/Pool.py +245 -0
  65. DefStream/Worker/Run.py +357 -0
  66. DefStream/Worker/Specification.py +87 -0
  67. DefStream/Worker/Startup.py +174 -0
  68. DefStream/Worker/Worker.py +421 -0
  69. DefStream/Worker/__init__.py +47 -0
  70. DefStream/__init__.py +0 -0
  71. defstream-0.1.16.dist-info/METADATA +1152 -0
  72. defstream-0.1.16.dist-info/RECORD +75 -0
  73. defstream-0.1.16.dist-info/WHEEL +6 -0
  74. defstream-0.1.16.dist-info/licenses/LICENSE +674 -0
  75. defstream-0.1.16.dist-info/top_level.txt +1 -0
@@ -0,0 +1,137 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # COMMON
4
+ from .Common import (
5
+ SetDefault,
6
+ IsOptional,
7
+ IsNotToNone,
8
+ IsNotEmpty,
9
+ )
10
+
11
+ # SIZE
12
+ from .Size import (
13
+ IsSizeOf,
14
+ IsSizeIn,
15
+ IsMinSizeOf,
16
+ IsMaxSizeOf,
17
+ )
18
+
19
+ # COMPARE
20
+ from .Compare import (
21
+ IsEqualTo,
22
+ IsNotEqualTo,
23
+ IsGreaterThan,
24
+ IsGreaterEqualTo,
25
+ IsLessEqualTo,
26
+ IsLessThan,
27
+ IsIn,
28
+ IsNotIn,
29
+ )
30
+
31
+ # CONDITION
32
+ from .Condition import (
33
+ All,
34
+ Any,
35
+ )
36
+
37
+ # TYPE
38
+ from .Type import (
39
+ IsTypeOf,
40
+ IsBool,
41
+ IsInteger,
42
+ IsFloat,
43
+ IsNumber,
44
+ IsString,
45
+ IsList,
46
+ IsTuple,
47
+ IsSet,
48
+ IsArray,
49
+ IsObject,
50
+ IsByteString,
51
+ IsByteArray,
52
+ IsDecimal,
53
+ IsDataObject,
54
+ ToTypeOf,
55
+ ToBool,
56
+ ToInteger,
57
+ ToFloat,
58
+ ToString,
59
+ ToList,
60
+ ToTuple,
61
+ ToSet,
62
+ ToObject,
63
+ ToByteString,
64
+ ToByteArray,
65
+ ToDecimal,
66
+ ToDataObject,
67
+ )
68
+ from .DateTime import (
69
+ IsDateTime,
70
+ IsDate,
71
+ IsTime,
72
+ ToDateTime,
73
+ ToDate,
74
+ ToTime,
75
+ )
76
+
77
+
78
+ __all__ = (
79
+ # COMMON
80
+ 'SetDefault',
81
+ 'IsOptional',
82
+ 'IsNotToNone',
83
+ 'IsNotEmpty',
84
+ # SIZE
85
+ 'IsSizeOf',
86
+ 'IsSizeIn',
87
+ 'IsMinSizeOf',
88
+ 'IsMaxSizeOf',
89
+ # COMPARE
90
+ 'IsEqualTo',
91
+ 'IsNotEqualTo',
92
+ 'IsGreaterThan',
93
+ 'IsGreaterEqualTo',
94
+ 'IsLessEqualTo',
95
+ 'IsLessThan',
96
+ 'IsIn',
97
+ 'IsNotIn',
98
+ # CONDITION
99
+ 'All',
100
+ 'Any',
101
+ # TYPE
102
+ 'IsTypeOf',
103
+ 'IsBool',
104
+ 'IsInteger',
105
+ 'IsFloat',
106
+ 'IsNumber',
107
+ 'IsString',
108
+ 'IsList',
109
+ 'IsTuple',
110
+ 'IsSet',
111
+ 'IsArray',
112
+ 'IsObject',
113
+ 'IsByteString',
114
+ 'IsByteArray',
115
+ 'IsDecimal',
116
+ 'IsDataObject',
117
+ 'ToTypeOf',
118
+ 'ToBool',
119
+ 'ToInteger',
120
+ 'ToFloat',
121
+ 'ToString',
122
+ 'ToList',
123
+ 'ToTuple',
124
+ 'ToSet',
125
+ 'ToObject',
126
+ 'ToByteString',
127
+ 'ToByteArray',
128
+ 'ToDecimal',
129
+ 'ToDataObject',
130
+ # DateTime
131
+ 'IsDateTime',
132
+ 'IsDate',
133
+ 'IsTime',
134
+ 'ToDateTime',
135
+ 'ToDate',
136
+ 'ToTime',
137
+ )
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Validator import Validator
4
+ from .Patterns import IsObject
5
+ from .Patterns.Object import IsMappingOf
6
+
7
+ from functools import wraps
8
+ from inspect import signature
9
+
10
+ __all__ = (
11
+ 'Validate',
12
+ )
13
+
14
+
15
+ class Validate(object):
16
+ """Decorator for Validator of Function and Method"""
17
+ def __init__(self, mappings: dict):
18
+ self.mappings = mappings
19
+ return
20
+
21
+ def __call__(self, fn: callable):
22
+ @wraps(fn)
23
+ def wrapper(*args, **kwargs):
24
+ validator = Validator(IsObject(IsMappingOf(self.mappings)))
25
+ bound = signature(fn).bind(*args, **kwargs)
26
+ bound.apply_defaults()
27
+ bound.arguments = validator(dict(bound.arguments))
28
+ return fn(*bound.args, **bound.kwargs)
29
+ return wrapper
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Pattern import Pattern
4
+
5
+ __all__ = (
6
+ 'Validator',
7
+ )
8
+
9
+
10
+ class Validator(object):
11
+ """
12
+ Validator Class
13
+ """
14
+ def __init__(self, *args):
15
+ for pattern in args:
16
+ if not isinstance(pattern, Pattern):
17
+ raise RuntimeError('{} must be based {}'.format(pattern, Pattern))
18
+ self.patterns = args
19
+ return
20
+
21
+ def __call__(self, parameter):
22
+ for pattern in self.patterns:
23
+ parameter = pattern(parameter)
24
+ return parameter
25
+
26
+ def __repr__(self):
27
+ return 'Validator({})'.format(
28
+ ', '.join([repr(p) for p in self.patterns])
29
+ )
30
+
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Validator import Validator
4
+ from .Pattern import Pattern
5
+ from .Validate import Validate
6
+
7
+ __all__ = (
8
+ 'Validator',
9
+ 'Pattern',
10
+ 'Validate',
11
+ )
@@ -0,0 +1,371 @@
1
+ from concurrent.futures import (
2
+ Executor as PyExecutor,
3
+ Future,
4
+ ProcessPoolExecutor,
5
+ ThreadPoolExecutor,
6
+ )
7
+ from multiprocessing import get_context
8
+ from logging import getLogger
9
+ from multiprocessing.managers import SyncManager
10
+ from pickle import (
11
+ dumps,
12
+ loads,
13
+ )
14
+ from queue import (
15
+ Empty,
16
+ Queue,
17
+ )
18
+ from signal import (
19
+ SIGINT,
20
+ SIG_IGN,
21
+ signal,
22
+ )
23
+ from threading import (
24
+ Lock,
25
+ Thread,
26
+ )
27
+ from traceback import format_exc
28
+ from typing import (
29
+ Any,
30
+ Callable,
31
+ Dict,
32
+ List,
33
+ Optional,
34
+ Sequence,
35
+ TypeVar,
36
+ )
37
+
38
+ __all__ = (
39
+ 'Executor',
40
+ )
41
+
42
+ Result = TypeVar('Result')
43
+
44
+
45
+ def initialize_process() -> None:
46
+ # 부모 프로세스가 종료 요청과 정리를 조정한다.
47
+ signal(
48
+ SIGINT,
49
+ SIG_IGN,
50
+ )
51
+
52
+
53
+ def execute_loop(
54
+ incoming: Any,
55
+ outgoing: Any,
56
+ setups: Sequence[Callable[[], None]],
57
+ cleanups: Sequence[Callable[[], None]],
58
+ serialized: bool,
59
+ ) -> None:
60
+ logger = Executor.logger
61
+ ready = False
62
+ try:
63
+ for setup in setups:
64
+ logger.debug(
65
+ 'Starting execution environment setup: hook=%s',
66
+ type(setup).__qualname__,
67
+ )
68
+ setup()
69
+ logger.debug(
70
+ 'Execution environment setup completed: hook=%s',
71
+ type(setup).__qualname__,
72
+ )
73
+ ready = True
74
+ logger.debug(
75
+ 'Execution unit ready: serialized=%s',
76
+ serialized,
77
+ )
78
+ while True:
79
+ item = incoming.get()
80
+ if item is None:
81
+ return
82
+ key, payload = item
83
+ logger.debug(
84
+ 'Execution unit task received: key=%s',
85
+ key,
86
+ )
87
+ try:
88
+ function, args, kwargs = loads(payload) if serialized else payload
89
+ result = function(
90
+ *args,
91
+ **kwargs,
92
+ )
93
+ except BaseException as error:
94
+ logger.debug(
95
+ 'Execution unit task failed: key=%s error_type=%s',
96
+ key,
97
+ type(error).__qualname__,
98
+ )
99
+ outcome = (
100
+ False,
101
+ error,
102
+ format_exc() if serialized else None,
103
+ )
104
+ else:
105
+ outcome = (
106
+ True,
107
+ result,
108
+ None,
109
+ )
110
+ if serialized:
111
+ try:
112
+ outcome = dumps(outcome)
113
+ except Exception as error:
114
+ logger.error(
115
+ 'Task outcome serialization failed: key=%s error_type=%s',
116
+ key,
117
+ type(error).__qualname__,
118
+ )
119
+ outcome = dumps((
120
+ False,
121
+ RuntimeError('Cannot serialize task outcome: ' + repr(error)),
122
+ format_exc(),
123
+ ))
124
+ outgoing.put((
125
+ key,
126
+ outcome,
127
+ ))
128
+ logger.debug(
129
+ 'Execution unit result sent: key=%s',
130
+ key,
131
+ )
132
+ finally:
133
+ if ready:
134
+ logger.debug(
135
+ 'Starting execution environment cleanup: hooks=%s',
136
+ len(cleanups),
137
+ )
138
+ run_cleanups(cleanups)
139
+ logger.debug('Execution environment cleanup completed')
140
+
141
+
142
+ def run_cleanups(
143
+ cleanups: Sequence[Callable[..., None]],
144
+ *args: Any,
145
+ ) -> None:
146
+ failure: Optional[BaseException] = None
147
+ for cleanup in cleanups:
148
+ try:
149
+ cleanup(*args)
150
+ except BaseException as error:
151
+ if failure is not None:
152
+ error.__context__ = failure
153
+ failure = error
154
+ if failure is not None:
155
+ raise failure
156
+
157
+
158
+ class Executor(PyExecutor):
159
+ logger = getLogger('DefStream.Executor')
160
+
161
+ def __init__(
162
+ self,
163
+ size: int,
164
+ setups: Sequence[Callable[[], None]],
165
+ cleanups: Sequence[Callable[[], None]],
166
+ process: bool = False,
167
+ ):
168
+ self.size = size
169
+ self.setups = setups
170
+ self.cleanups = cleanups
171
+ self.process = process
172
+ self.lock = Lock()
173
+ self.pending: Dict[int, Future[Any]] = {}
174
+ self.workers: List[Future] = []
175
+ self.closed = False
176
+ self.failure: Optional[BaseException] = None
177
+ self.manager: Optional[SyncManager] = None
178
+ self.executor: Optional[PyExecutor] = None
179
+ self.monitor: Optional[Thread] = None
180
+ self.incoming: Any = None
181
+ self.outgoing: Any = None
182
+ self.sequence = 0
183
+
184
+ def start(self) -> None:
185
+ self.logger.info(
186
+ 'Starting Executor: concurrency=%s process=%s',
187
+ self.size,
188
+ self.process,
189
+ )
190
+ try:
191
+ if self.process:
192
+ context = get_context('spawn')
193
+ self.manager = context.Manager()
194
+ self.incoming = self.manager.Queue()
195
+ self.outgoing = self.manager.Queue()
196
+ executor: PyExecutor = ProcessPoolExecutor(
197
+ self.size,
198
+ mp_context=context,
199
+ initializer=initialize_process,
200
+ )
201
+ else:
202
+ self.incoming = Queue()
203
+ self.outgoing = Queue()
204
+ executor = ThreadPoolExecutor(self.size)
205
+ self.executor = executor
206
+ for index in range(self.size):
207
+ self.workers.append(executor.submit(
208
+ execute_loop,
209
+ self.incoming,
210
+ self.outgoing,
211
+ self.setups,
212
+ self.cleanups,
213
+ self.process,
214
+ ))
215
+ monitor = Thread(target=self.collect)
216
+ monitor.start()
217
+ self.monitor = monitor
218
+ self.logger.info('Executor execution environment ready')
219
+ except BaseException:
220
+ self.logger.exception('Executor startup failed')
221
+ self.closed = True
222
+ try:
223
+ for worker in self.workers:
224
+ self.incoming.put(None)
225
+ finally:
226
+ try:
227
+ if self.executor is not None:
228
+ self.executor.shutdown(wait=True)
229
+ finally:
230
+ if self.manager is not None:
231
+ self.manager.shutdown()
232
+ raise
233
+
234
+ def submit(
235
+ self,
236
+ fn: Callable[..., Result],
237
+ /,
238
+ *args: Any,
239
+ **kwargs: Any,
240
+ ) -> Future[Result]:
241
+ payload = (
242
+ fn,
243
+ args,
244
+ kwargs,
245
+ )
246
+ if self.process:
247
+ payload = dumps(payload)
248
+ with self.lock:
249
+ if self.closed:
250
+ raise RuntimeError('Pool is closed')
251
+ if self.failure is not None:
252
+ raise RuntimeError('Pool execution environment failed') from self.failure
253
+ if self.executor is None:
254
+ self.start()
255
+ key = self.sequence
256
+ self.sequence += 1
257
+ future: Future[Result] = Future()
258
+ future.set_running_or_notify_cancel()
259
+ self.pending[key] = future
260
+ self.logger.debug(
261
+ 'Dispatching task to execution environment: key=%s pending=%s',
262
+ key,
263
+ len(self.pending),
264
+ )
265
+ try:
266
+ self.incoming.put((
267
+ key,
268
+ payload,
269
+ ))
270
+ except BaseException:
271
+ del self.pending[key]
272
+ raise
273
+ return future
274
+
275
+ def collect(self) -> None:
276
+ try:
277
+ self.collect_results()
278
+ except BaseException as error:
279
+ self.fail(error)
280
+
281
+ def fail(
282
+ self,
283
+ error: BaseException,
284
+ ) -> None:
285
+ with self.lock:
286
+ first_failure = self.failure is None
287
+ self.failure = error
288
+ pending = tuple(self.pending.values())
289
+ self.pending.clear()
290
+ if first_failure:
291
+ self.logger.error(
292
+ 'Executor execution environment failed: pending=%s',
293
+ len(pending),
294
+ exc_info=(
295
+ type(error),
296
+ error,
297
+ error.__traceback__,
298
+ ),
299
+ )
300
+ for future in pending:
301
+ future.set_exception(error)
302
+
303
+ def collect_results(self) -> None:
304
+ while True:
305
+ try:
306
+ key, outcome = self.outgoing.get(timeout=0.05)
307
+ except Empty:
308
+ pass
309
+ else:
310
+ success, value, traceback = loads(outcome) if self.process else outcome
311
+ self.logger.debug(
312
+ 'Execution result collected: key=%s success=%s',
313
+ key,
314
+ success,
315
+ )
316
+ with self.lock:
317
+ future = self.pending.pop(
318
+ key,
319
+ None,
320
+ )
321
+ if future is not None:
322
+ if success:
323
+ future.set_result(value)
324
+ else:
325
+ if traceback is not None:
326
+ value.__cause__ = RuntimeError('Remote execution traceback:\n' + traceback)
327
+ future.set_exception(value)
328
+ failure = next(
329
+ (
330
+ worker.exception()
331
+ for worker in self.workers
332
+ if worker.done() and worker.exception() is not None
333
+ ),
334
+ None,
335
+ )
336
+ if failure is not None:
337
+ self.fail(failure)
338
+ with self.lock:
339
+ finished = not self.pending and all(worker.done() for worker in self.workers)
340
+ if finished:
341
+ return
342
+
343
+ def shutdown(
344
+ self,
345
+ wait: bool = True,
346
+ *,
347
+ cancel_futures: bool = False,
348
+ ) -> None:
349
+ self.logger.info(
350
+ 'Executor shutdown requested: wait=%s',
351
+ wait,
352
+ )
353
+ with self.lock:
354
+ if not self.closed:
355
+ self.closed = True
356
+ if self.executor is not None:
357
+ for worker in self.workers:
358
+ self.incoming.put(None)
359
+ executor = self.executor
360
+ if executor is None:
361
+ self.logger.debug('Closing an unstarted Executor')
362
+ return
363
+ executor.shutdown(wait=wait)
364
+ if wait:
365
+ if self.monitor is not None:
366
+ self.monitor.join()
367
+ if self.manager is not None:
368
+ self.manager.shutdown()
369
+ if self.failure is not None:
370
+ raise RuntimeError('Pool execution environment failed') from self.failure
371
+ self.logger.info('Executor shutdown completed')