DefStream 0.1.5__16-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 (56) hide show
  1. DefStream/Broker/Configuration.py +18 -0
  2. DefStream/Broker/Connection.py +125 -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/__init__.py +1 -0
  7. DefStream/Broker/Policies.py +25 -0
  8. DefStream/Broker/Priority.py +26 -0
  9. DefStream/Broker/Protocol.py +45 -0
  10. DefStream/Broker/__init__.py +57 -0
  11. DefStream/Descriptor/DataObject.py +384 -0
  12. DefStream/Descriptor/Types.py +229 -0
  13. DefStream/Descriptor/Value.py +132 -0
  14. DefStream/Descriptor/__init__.py +172 -0
  15. DefStream/Inspector/Extension.py +69 -0
  16. DefStream/Inspector/Extensions/Console.html +373 -0
  17. DefStream/Inspector/Extensions/Console.py +396 -0
  18. DefStream/Inspector/Extensions/Dashboard.html +206 -0
  19. DefStream/Inspector/Extensions/Dashboard.py +114 -0
  20. DefStream/Inspector/Extensions/HealthCheck.py +92 -0
  21. DefStream/Inspector/Extensions/Metrics.py +281 -0
  22. DefStream/Inspector/Extensions/Specifications.py +160 -0
  23. DefStream/Inspector/Extensions/__init__.py +13 -0
  24. DefStream/Inspector/Inspector.py +216 -0
  25. DefStream/Inspector/Response.py +14 -0
  26. DefStream/Inspector/__init__.py +9 -0
  27. DefStream/Validator/Pattern.py +36 -0
  28. DefStream/Validator/Patterns/Array.py +30 -0
  29. DefStream/Validator/Patterns/Common.py +93 -0
  30. DefStream/Validator/Patterns/Compare.py +180 -0
  31. DefStream/Validator/Patterns/Condition.py +60 -0
  32. DefStream/Validator/Patterns/DataObject.py +85 -0
  33. DefStream/Validator/Patterns/DateTime.py +111 -0
  34. DefStream/Validator/Patterns/Number.py +43 -0
  35. DefStream/Validator/Patterns/Object.py +150 -0
  36. DefStream/Validator/Patterns/Size.py +110 -0
  37. DefStream/Validator/Patterns/String.py +85 -0
  38. DefStream/Validator/Patterns/Type.py +350 -0
  39. DefStream/Validator/Patterns/__init__.py +137 -0
  40. DefStream/Validator/Validate.py +29 -0
  41. DefStream/Validator/Validator.py +30 -0
  42. DefStream/Validator/__init__.py +11 -0
  43. DefStream/Worker/Executor.py +298 -0
  44. DefStream/Worker/Message.py +179 -0
  45. DefStream/Worker/Pool.py +203 -0
  46. DefStream/Worker/Run.py +238 -0
  47. DefStream/Worker/Specification.py +91 -0
  48. DefStream/Worker/Startup.py +135 -0
  49. DefStream/Worker/Worker.py +323 -0
  50. DefStream/Worker/__init__.py +49 -0
  51. DefStream/__init__.py +0 -0
  52. defstream-0.1.5.dist-info/METADATA +603 -0
  53. defstream-0.1.5.dist-info/RECORD +56 -0
  54. defstream-0.1.5.dist-info/WHEEL +6 -0
  55. defstream-0.1.5.dist-info/licenses/LICENSE +674 -0
  56. defstream-0.1.5.dist-info/top_level.txt +1 -0
@@ -0,0 +1,323 @@
1
+ from abc import (
2
+ ABC,
3
+ abstractmethod,
4
+ )
5
+ from collections.abc import Mapping
6
+ from logging import getLogger
7
+ from threading import Event as PyEvent
8
+ from typing import (
9
+ Any,
10
+ List,
11
+ Callable,
12
+ Optional,
13
+ Sequence,
14
+ Type,
15
+ Tuple,
16
+ Union,
17
+ Protocol,
18
+ )
19
+
20
+ from ..Broker import (
21
+ Connection,
22
+ GetConsumer,
23
+ Consumer,
24
+ EventHandler,
25
+ )
26
+ from ..Broker.Event import Event
27
+ from .Message import task_parameters
28
+ from ..Inspector import Inspector
29
+ from .Pool import (
30
+ Pool,
31
+ PoolFull,
32
+ )
33
+ from .Run import Run
34
+ from .Executor import run_cleanups
35
+ from .Startup import startup
36
+
37
+ __all__ = (
38
+ 'Worker',
39
+ 'WorkerConnection',
40
+ 'WorkerSetup',
41
+ 'WorkerCleanup',
42
+ )
43
+
44
+ logger = getLogger(__name__)
45
+
46
+
47
+ class WorkerConnection(
48
+ Connection,
49
+ GetConsumer,
50
+ Protocol,
51
+ ):
52
+ pass
53
+
54
+
55
+ class WorkerSetup(ABC):
56
+ @abstractmethod
57
+ def __call__(
58
+ self,
59
+ worker: 'Worker',
60
+ ) -> None:
61
+ raise NotImplementedError
62
+
63
+
64
+ class WorkerCleanup(ABC):
65
+ @abstractmethod
66
+ def __call__(
67
+ self,
68
+ worker: 'Worker',
69
+ ) -> None:
70
+ raise NotImplementedError
71
+
72
+
73
+ def worker_hooks(
74
+ hooks: Optional[Union[Callable[['Worker'], None], Sequence[Callable[['Worker'], None]]]],
75
+ ) -> Tuple[Callable[['Worker'], None], ...]:
76
+ result = () if hooks is None else tuple(hooks) if isinstance(
77
+ hooks,
78
+ Sequence,
79
+ ) else (hooks,)
80
+ if not all(callable(hook) for hook in result):
81
+ raise TypeError('Worker hooks must be callable')
82
+ return result
83
+
84
+
85
+ class Worker:
86
+ def __init__(
87
+ self,
88
+ broker: WorkerConnection,
89
+ pool: Pool,
90
+ setups: Optional[Union[WorkerSetup, Sequence[WorkerSetup]]] = None,
91
+ cleanups: Optional[Union[WorkerCleanup, Sequence[WorkerCleanup]]] = None,
92
+ inspector: Optional[Inspector] = None,
93
+ ):
94
+ self.broker = broker
95
+ self.pool = pool
96
+ self.tasks: List[Type[Run]] = []
97
+ self.consumer: Optional[Consumer] = None
98
+ self.started = False
99
+ self.running = PyEvent()
100
+ self.stopping = PyEvent()
101
+ self.finished = PyEvent()
102
+ self.pending = 0
103
+ self.setups = worker_hooks(setups)
104
+ self.cleanups = worker_hooks(cleanups)
105
+ self.inspector = inspector
106
+
107
+ def add(
108
+ self,
109
+ run: Type[Run],
110
+ ) -> None:
111
+ if self.started:
112
+ raise RuntimeError('Cannot register runs after starting the worker')
113
+ task_parameters(run)
114
+ if run in self.tasks:
115
+ raise ValueError('Run is already registered')
116
+ self.tasks.append(run)
117
+
118
+ def run(self) -> None:
119
+ if self.started:
120
+ raise RuntimeError('Worker can only be started once')
121
+ if not self.tasks:
122
+ raise RuntimeError('Register at least one Run before starting')
123
+ self.started = True
124
+ try:
125
+ startup(self)
126
+ if self.inspector is not None:
127
+ self.inspector.start(self)
128
+ self.broker.connect()
129
+ for setup in self.setups:
130
+ setup(self)
131
+ self.consumer = self.broker.consumer()
132
+ for run in self.tasks:
133
+ self.consumer.subs(
134
+ run.parameters,
135
+ Handler(
136
+ self,
137
+ run,
138
+ ),
139
+ )
140
+ if not self.stopping.is_set():
141
+ self.running.set()
142
+ self.consumer.run()
143
+ finally:
144
+ self.running.clear()
145
+ self.finished.set()
146
+ if self.pending:
147
+ logger.error(
148
+ 'Consumer exited with %s unconfirmed deliveries',
149
+ self.pending,
150
+ )
151
+ try:
152
+ self.pool.stop()
153
+ finally:
154
+ try:
155
+ self.cleanup()
156
+ finally:
157
+ try:
158
+ if self.consumer is not None:
159
+ self.consumer.close()
160
+ finally:
161
+ try:
162
+ self.broker.close()
163
+ finally:
164
+ if self.inspector is not None and self.inspector.worker is self:
165
+ self.inspector.stop()
166
+
167
+ def cleanup(self) -> None:
168
+ run_cleanups(
169
+ self.cleanups,
170
+ self,
171
+ )
172
+
173
+ def stop(self) -> None:
174
+ self.stopping.set()
175
+ if self.consumer is not None and not self.finished.is_set():
176
+ self.consumer.call(self.try_stop)
177
+
178
+ def try_stop(self) -> None:
179
+ if self.stopping.is_set() and self.pending == 0 and self.consumer is not None:
180
+ self.consumer.stop()
181
+
182
+ def receive(
183
+ self,
184
+ run: Type[Run],
185
+ event: Event,
186
+ ) -> None:
187
+ if self.stopping.is_set():
188
+ event.nack()
189
+ return
190
+ if not isinstance(
191
+ event.body,
192
+ Mapping,
193
+ ):
194
+ self.failed(
195
+ run,
196
+ event,
197
+ TypeError('Run message body must be a mapping'),
198
+ )
199
+ return
200
+ self.pending += 1
201
+ consumer = self.consumer
202
+ if consumer is None:
203
+ self.pending -= 1
204
+ raise RuntimeError('Worker consumer is not initialized')
205
+ inspector = self.inspector
206
+ task = run.__module__ + '.' + run.__qualname__
207
+ contexts = inspector.begin(task) if inspector is not None else ()
208
+
209
+ def schedule(
210
+ error: Optional[BaseException] = None,
211
+ result: Any = None,
212
+ ) -> None:
213
+ if inspector is not None:
214
+ inspector.end(
215
+ task,
216
+ contexts,
217
+ error is None,
218
+ )
219
+ if self.finished.is_set():
220
+ logger.error(
221
+ 'Consumer exited before Run %s delivery could be confirmed',
222
+ run.__name__,
223
+ )
224
+ return
225
+ consumer.call(lambda: self.complete(
226
+ run,
227
+ event,
228
+ error,
229
+ result,
230
+ ))
231
+
232
+ try:
233
+ self.pool.submit(
234
+ run,
235
+ dict(event.body),
236
+ callback=lambda result: schedule(result=result),
237
+ error_callback=schedule,
238
+ )
239
+ except PoolFull:
240
+ if inspector is not None:
241
+ inspector.end(
242
+ task,
243
+ contexts,
244
+ None,
245
+ )
246
+ self.pending -= 1
247
+ event.nack()
248
+ return
249
+ except Exception:
250
+ if inspector is not None:
251
+ inspector.end(
252
+ task,
253
+ contexts,
254
+ None,
255
+ )
256
+ self.pending -= 1
257
+ raise
258
+
259
+ def complete(
260
+ self,
261
+ run: Type[Run],
262
+ event: Event,
263
+ error: Optional[BaseException] = None,
264
+ result: Any = None,
265
+ ) -> None:
266
+ try:
267
+ if error is not None:
268
+ self.failed(
269
+ run,
270
+ event,
271
+ error,
272
+ )
273
+ else:
274
+ if self.consumer is None:
275
+ raise RuntimeError('Worker consumer is not initialized')
276
+ self.consumer.complete(
277
+ event,
278
+ result,
279
+ )
280
+ finally:
281
+ self.pending -= 1
282
+ self.try_stop()
283
+
284
+ def failed(
285
+ self,
286
+ run: Type[Run],
287
+ event: Event,
288
+ error: BaseException,
289
+ ) -> None:
290
+ logger.error(
291
+ 'Run %s failed',
292
+ run.__name__,
293
+ exc_info=(
294
+ type(error),
295
+ error,
296
+ error.__traceback__,
297
+ ),
298
+ )
299
+ if self.consumer is None:
300
+ raise RuntimeError('Worker consumer is not initialized')
301
+ self.consumer.fail(
302
+ event,
303
+ run.on_error,
304
+ )
305
+
306
+
307
+ class Handler(EventHandler):
308
+ def __init__(
309
+ self,
310
+ worker: Worker,
311
+ run: Type[Run],
312
+ ):
313
+ self.worker = worker
314
+ self.run = run
315
+
316
+ def __call__(
317
+ self,
318
+ event: Event,
319
+ ) -> None:
320
+ self.worker.receive(
321
+ self.run,
322
+ event,
323
+ )
@@ -0,0 +1,49 @@
1
+ from .Worker import (
2
+ Worker,
3
+ WorkerConnection,
4
+ WorkerSetup,
5
+ WorkerCleanup,
6
+ )
7
+ from .Message import (
8
+ declare,
9
+ send,
10
+ request,
11
+ )
12
+ from .Run import (
13
+ Run,
14
+ Context,
15
+ RunSetup,
16
+ RunComplete,
17
+ RunError,
18
+ )
19
+ from .Specification import Specification
20
+ from .Pool import (
21
+ Pool,
22
+ PoolFull,
23
+ PoolSetup,
24
+ PoolCleanup,
25
+ ThreadPool,
26
+ ProcessPool,
27
+ )
28
+
29
+ __all__ = (
30
+ 'Worker',
31
+ 'declare',
32
+ 'send',
33
+ 'request',
34
+ 'WorkerConnection',
35
+ 'WorkerSetup',
36
+ 'WorkerCleanup',
37
+ 'Run',
38
+ 'Specification',
39
+ 'Pool',
40
+ 'PoolFull',
41
+ 'PoolSetup',
42
+ 'PoolCleanup',
43
+ 'ThreadPool',
44
+ 'ProcessPool',
45
+ 'Context',
46
+ 'RunSetup',
47
+ 'RunComplete',
48
+ 'RunError',
49
+ )
DefStream/__init__.py ADDED
File without changes