DefStream 0.1.17__64-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/RabbitMQ/Configuration.py +48 -0
- DefStream/Broker/Implements/RabbitMQ/Connection.py +542 -0
- DefStream/Broker/Implements/RabbitMQ/Consumer.py +359 -0
- DefStream/Broker/Implements/RabbitMQ/Event.py +127 -0
- DefStream/Broker/Implements/RabbitMQ/Exchange.py +278 -0
- DefStream/Broker/Implements/RabbitMQ/Policies.py +9 -0
- DefStream/Broker/Implements/RabbitMQ/Queue.py +319 -0
- DefStream/Broker/Implements/RabbitMQ/Serializer.py +61 -0
- DefStream/Broker/Implements/RabbitMQ/Stream.py +179 -0
- DefStream/Broker/Implements/RabbitMQ/Subscription.py +74 -0
- DefStream/Broker/Implements/RabbitMQ/__init__.py +47 -0
- DefStream/Broker/Implements/Redis/Configuration.py +59 -0
- DefStream/Broker/Implements/Redis/Connection.py +265 -0
- DefStream/Broker/Implements/Redis/Consumer.py +364 -0
- DefStream/Broker/Implements/Redis/Event.py +191 -0
- DefStream/Broker/Implements/Redis/Queue.py +284 -0
- DefStream/Broker/Implements/Redis/Serializer.py +48 -0
- DefStream/Broker/Implements/Redis/Subscription.py +46 -0
- DefStream/Broker/Implements/Redis/__init__.py +29 -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/Metrics.py +316 -0
- DefStream/Inspector/Extensions/Ping.py +107 -0
- DefStream/Inspector/Extensions/Specifications.py +176 -0
- DefStream/Inspector/Extensions/Status.html +206 -0
- DefStream/Inspector/Extensions/Status.py +124 -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 +92 -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.17.dist-info/METADATA +1174 -0
- defstream-0.1.17.dist-info/RECORD +75 -0
- defstream-0.1.17.dist-info/WHEEL +6 -0
- defstream-0.1.17.dist-info/licenses/LICENSE +674 -0
- defstream-0.1.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from inspect import isabstract
|
|
3
|
+
from typing import (
|
|
4
|
+
Any,
|
|
5
|
+
Optional,
|
|
6
|
+
Type,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from ..Broker.Connection import (
|
|
10
|
+
Connection,
|
|
11
|
+
Declarable,
|
|
12
|
+
Emittable,
|
|
13
|
+
Requestable,
|
|
14
|
+
PrioritySupport,
|
|
15
|
+
)
|
|
16
|
+
from .Run import (
|
|
17
|
+
Run,
|
|
18
|
+
priority_setting,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
'declare',
|
|
23
|
+
'emit',
|
|
24
|
+
'request',
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def task_parameters(
|
|
29
|
+
task: Type[Run],
|
|
30
|
+
) -> Any:
|
|
31
|
+
if not isinstance(
|
|
32
|
+
task,
|
|
33
|
+
type,
|
|
34
|
+
) or not issubclass(
|
|
35
|
+
task,
|
|
36
|
+
Run,
|
|
37
|
+
) or isabstract(task):
|
|
38
|
+
raise TypeError('A concrete Run class is required')
|
|
39
|
+
if task.parameters is None:
|
|
40
|
+
raise ValueError('Run subscription parameters are required')
|
|
41
|
+
return task.parameters
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def message_body(
|
|
45
|
+
body: Mapping,
|
|
46
|
+
) -> dict:
|
|
47
|
+
if not isinstance(
|
|
48
|
+
body,
|
|
49
|
+
Mapping,
|
|
50
|
+
) or not all(isinstance(
|
|
51
|
+
key,
|
|
52
|
+
str,
|
|
53
|
+
) for key in body):
|
|
54
|
+
raise TypeError('Task body must be a mapping with string keys')
|
|
55
|
+
return dict(body)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def priority_parameters(
|
|
59
|
+
broker: Connection,
|
|
60
|
+
parameters: Any,
|
|
61
|
+
priority: Optional[int],
|
|
62
|
+
) -> Any:
|
|
63
|
+
if priority_setting(
|
|
64
|
+
parameters,
|
|
65
|
+
'priority',
|
|
66
|
+
) is None and priority_setting(
|
|
67
|
+
parameters,
|
|
68
|
+
'max_priority',
|
|
69
|
+
) is None and priority is None:
|
|
70
|
+
return parameters
|
|
71
|
+
if not isinstance(
|
|
72
|
+
broker,
|
|
73
|
+
PrioritySupport,
|
|
74
|
+
):
|
|
75
|
+
raise NotImplementedError('This connection does not support task priorities')
|
|
76
|
+
return broker.priority_parameters(
|
|
77
|
+
parameters,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def declare(
|
|
82
|
+
*,
|
|
83
|
+
task: Type[Run],
|
|
84
|
+
broker: Connection,
|
|
85
|
+
) -> None:
|
|
86
|
+
parameters = task_parameters(task)
|
|
87
|
+
if not isinstance(
|
|
88
|
+
broker,
|
|
89
|
+
Declarable,
|
|
90
|
+
):
|
|
91
|
+
raise NotImplementedError('This connection does not support task declarations')
|
|
92
|
+
broker.declare(priority_parameters(
|
|
93
|
+
broker,
|
|
94
|
+
parameters,
|
|
95
|
+
None,
|
|
96
|
+
))
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def emit(
|
|
101
|
+
body: Mapping,
|
|
102
|
+
*,
|
|
103
|
+
task: Type[Run],
|
|
104
|
+
broker: Connection,
|
|
105
|
+
priority: Optional[int] = None,
|
|
106
|
+
) -> Any:
|
|
107
|
+
parameters = task_parameters(task)
|
|
108
|
+
payload = message_body(body)
|
|
109
|
+
if not isinstance(
|
|
110
|
+
broker,
|
|
111
|
+
Emittable,
|
|
112
|
+
):
|
|
113
|
+
raise NotImplementedError('This connection does not support task emission')
|
|
114
|
+
parameters = priority_parameters(
|
|
115
|
+
broker,
|
|
116
|
+
parameters,
|
|
117
|
+
priority,
|
|
118
|
+
)
|
|
119
|
+
effective = priority_setting(
|
|
120
|
+
parameters,
|
|
121
|
+
'priority',
|
|
122
|
+
) if priority is None else priority
|
|
123
|
+
if effective is not None:
|
|
124
|
+
if not isinstance(
|
|
125
|
+
broker,
|
|
126
|
+
PrioritySupport,
|
|
127
|
+
):
|
|
128
|
+
raise NotImplementedError('This connection does not support task priorities')
|
|
129
|
+
return broker.emit(
|
|
130
|
+
payload,
|
|
131
|
+
parameters,
|
|
132
|
+
priority=effective,
|
|
133
|
+
)
|
|
134
|
+
return broker.emit(
|
|
135
|
+
payload,
|
|
136
|
+
parameters,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def request(
|
|
141
|
+
body: Mapping,
|
|
142
|
+
*,
|
|
143
|
+
task: Type[Run],
|
|
144
|
+
broker: Connection,
|
|
145
|
+
timeout: float = 5,
|
|
146
|
+
priority: Optional[int] = None,
|
|
147
|
+
) -> Any:
|
|
148
|
+
parameters = task_parameters(task)
|
|
149
|
+
payload = message_body(body)
|
|
150
|
+
if not isinstance(
|
|
151
|
+
broker,
|
|
152
|
+
Requestable,
|
|
153
|
+
):
|
|
154
|
+
raise NotImplementedError('This connection does not support task RPC requests')
|
|
155
|
+
parameters = priority_parameters(
|
|
156
|
+
broker,
|
|
157
|
+
parameters,
|
|
158
|
+
priority,
|
|
159
|
+
)
|
|
160
|
+
effective = priority_setting(
|
|
161
|
+
parameters,
|
|
162
|
+
'priority',
|
|
163
|
+
) if priority is None else priority
|
|
164
|
+
if effective is not None:
|
|
165
|
+
if not isinstance(
|
|
166
|
+
broker,
|
|
167
|
+
PrioritySupport,
|
|
168
|
+
):
|
|
169
|
+
raise NotImplementedError('This connection does not support task priorities')
|
|
170
|
+
return broker.request(
|
|
171
|
+
payload,
|
|
172
|
+
parameters,
|
|
173
|
+
timeout=timeout,
|
|
174
|
+
priority=effective,
|
|
175
|
+
)
|
|
176
|
+
return broker.request(
|
|
177
|
+
payload,
|
|
178
|
+
parameters,
|
|
179
|
+
timeout=timeout,
|
|
180
|
+
)
|
DefStream/Worker/Pool.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
from abc import (
|
|
2
|
+
ABC,
|
|
3
|
+
abstractmethod,
|
|
4
|
+
)
|
|
5
|
+
from concurrent.futures import (
|
|
6
|
+
Executor as PyExecutor,
|
|
7
|
+
Future,
|
|
8
|
+
wait,
|
|
9
|
+
)
|
|
10
|
+
from os import cpu_count
|
|
11
|
+
from logging import getLogger
|
|
12
|
+
from threading import (
|
|
13
|
+
BoundedSemaphore,
|
|
14
|
+
Lock,
|
|
15
|
+
)
|
|
16
|
+
from typing import (
|
|
17
|
+
Any,
|
|
18
|
+
Callable,
|
|
19
|
+
Dict,
|
|
20
|
+
Optional,
|
|
21
|
+
Sequence,
|
|
22
|
+
Set,
|
|
23
|
+
Tuple,
|
|
24
|
+
Type,
|
|
25
|
+
Union,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from .Run import Run
|
|
29
|
+
from .Executor import Executor
|
|
30
|
+
|
|
31
|
+
__all__ = (
|
|
32
|
+
'Pool',
|
|
33
|
+
'PoolFull',
|
|
34
|
+
'PoolSetup',
|
|
35
|
+
'PoolCleanup',
|
|
36
|
+
'ThreadPool',
|
|
37
|
+
'ProcessPool',
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PoolFull(RuntimeError):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Pool:
|
|
46
|
+
logger = getLogger('DefStream.Pool')
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
executor: PyExecutor,
|
|
51
|
+
max: int,
|
|
52
|
+
):
|
|
53
|
+
self.pool = executor
|
|
54
|
+
self.max = max
|
|
55
|
+
self.slots = BoundedSemaphore(max)
|
|
56
|
+
self.lock = Lock()
|
|
57
|
+
self.futures: Set[Future] = set()
|
|
58
|
+
self.closed = False
|
|
59
|
+
self.logger.info(
|
|
60
|
+
'Pool configured: concurrency=%s',
|
|
61
|
+
max,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def submit(
|
|
65
|
+
self,
|
|
66
|
+
run: Type[Run],
|
|
67
|
+
body: Dict[str, Any],
|
|
68
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
69
|
+
error_callback: Optional[Callable[[BaseException], None]] = None,
|
|
70
|
+
) -> Future:
|
|
71
|
+
with self.lock:
|
|
72
|
+
if self.closed:
|
|
73
|
+
raise RuntimeError('Pool is closed')
|
|
74
|
+
if not self.slots.acquire(blocking=False):
|
|
75
|
+
self.logger.debug(
|
|
76
|
+
'Pool capacity exhausted: active=%s capacity=%s',
|
|
77
|
+
len(self.futures),
|
|
78
|
+
self.max,
|
|
79
|
+
)
|
|
80
|
+
raise PoolFull('Pool capacity is exhausted')
|
|
81
|
+
try:
|
|
82
|
+
future = self.pool.submit(
|
|
83
|
+
run(),
|
|
84
|
+
**body,
|
|
85
|
+
)
|
|
86
|
+
except BaseException:
|
|
87
|
+
self.slots.release()
|
|
88
|
+
raise
|
|
89
|
+
self.futures.add(future)
|
|
90
|
+
self.logger.debug(
|
|
91
|
+
'Task submission accepted: task=%s.%s active=%s',
|
|
92
|
+
run.__module__,
|
|
93
|
+
run.__qualname__,
|
|
94
|
+
len(self.futures),
|
|
95
|
+
)
|
|
96
|
+
future.add_done_callback(lambda completed: self.complete(
|
|
97
|
+
completed,
|
|
98
|
+
callback,
|
|
99
|
+
error_callback,
|
|
100
|
+
))
|
|
101
|
+
return future
|
|
102
|
+
|
|
103
|
+
def complete(
|
|
104
|
+
self,
|
|
105
|
+
future: Future,
|
|
106
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
107
|
+
error_callback: Optional[Callable[[BaseException], None]] = None,
|
|
108
|
+
) -> None:
|
|
109
|
+
with self.lock:
|
|
110
|
+
self.futures.remove(future)
|
|
111
|
+
self.slots.release()
|
|
112
|
+
self.logger.debug(
|
|
113
|
+
'Task slot released: active=%s',
|
|
114
|
+
len(self.futures),
|
|
115
|
+
)
|
|
116
|
+
try:
|
|
117
|
+
result = future.result()
|
|
118
|
+
except BaseException as error:
|
|
119
|
+
self.logger.debug(
|
|
120
|
+
'Delivering failed result: error_type=%s',
|
|
121
|
+
type(error).__qualname__,
|
|
122
|
+
)
|
|
123
|
+
if error_callback is not None:
|
|
124
|
+
error_callback(error)
|
|
125
|
+
else:
|
|
126
|
+
self.logger.debug('Delivering successful result')
|
|
127
|
+
if callback is not None:
|
|
128
|
+
callback(result)
|
|
129
|
+
|
|
130
|
+
def waits(
|
|
131
|
+
self,
|
|
132
|
+
timeout: Optional[float] = None,
|
|
133
|
+
) -> None:
|
|
134
|
+
with self.lock:
|
|
135
|
+
tasks = tuple(self.futures)
|
|
136
|
+
self.logger.debug(
|
|
137
|
+
'Waiting for Pool tasks: active=%s timeout=%s',
|
|
138
|
+
len(tasks),
|
|
139
|
+
timeout,
|
|
140
|
+
)
|
|
141
|
+
if tasks and wait(
|
|
142
|
+
tasks,
|
|
143
|
+
timeout=timeout,
|
|
144
|
+
).not_done:
|
|
145
|
+
self.logger.warning(
|
|
146
|
+
'Pool task wait timed out: timeout=%s',
|
|
147
|
+
timeout,
|
|
148
|
+
)
|
|
149
|
+
raise TimeoutError('Pool tasks did not complete in time')
|
|
150
|
+
|
|
151
|
+
def stop(self) -> None:
|
|
152
|
+
self.logger.info('Stopping Pool')
|
|
153
|
+
with self.lock:
|
|
154
|
+
self.closed = True
|
|
155
|
+
self.pool.shutdown(wait=True)
|
|
156
|
+
self.logger.info('Pool shutdown completed')
|
|
157
|
+
|
|
158
|
+
def shutdown(self) -> None:
|
|
159
|
+
self.stop()
|
|
160
|
+
|
|
161
|
+
def size(self) -> int:
|
|
162
|
+
return self.max
|
|
163
|
+
|
|
164
|
+
def count(self) -> int:
|
|
165
|
+
with self.lock:
|
|
166
|
+
return len(self.futures)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class PoolSetup(ABC):
|
|
170
|
+
@abstractmethod
|
|
171
|
+
def __call__(self) -> None:
|
|
172
|
+
raise NotImplementedError
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class PoolCleanup(ABC):
|
|
176
|
+
@abstractmethod
|
|
177
|
+
def __call__(self) -> None:
|
|
178
|
+
raise NotImplementedError
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def pool_hooks(
|
|
182
|
+
hooks: Optional[Union[Callable[[], None], Sequence[Callable[[], None]]]],
|
|
183
|
+
) -> Tuple[Callable[[], None], ...]:
|
|
184
|
+
result = () if hooks is None else tuple(hooks) if isinstance(
|
|
185
|
+
hooks,
|
|
186
|
+
Sequence,
|
|
187
|
+
) else (hooks,)
|
|
188
|
+
if not all(callable(hook) for hook in result):
|
|
189
|
+
raise TypeError('Pool hooks must be callable')
|
|
190
|
+
return result
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def pool_size(max: Optional[int]) -> int:
|
|
194
|
+
if max is None:
|
|
195
|
+
return cpu_count() or 1
|
|
196
|
+
if isinstance(
|
|
197
|
+
max,
|
|
198
|
+
bool,
|
|
199
|
+
) or not isinstance(
|
|
200
|
+
max,
|
|
201
|
+
int,
|
|
202
|
+
) or max < 1:
|
|
203
|
+
raise ValueError('Pool size must be a positive integer')
|
|
204
|
+
return max
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class ThreadPool(Pool):
|
|
208
|
+
logger = getLogger('DefStream.ThreadPool')
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
max: Optional[int] = None,
|
|
213
|
+
setups: Optional[Union[PoolSetup, Sequence[PoolSetup]]] = None,
|
|
214
|
+
cleanups: Optional[Union[PoolCleanup, Sequence[PoolCleanup]]] = None,
|
|
215
|
+
):
|
|
216
|
+
size = pool_size(max)
|
|
217
|
+
super().__init__(
|
|
218
|
+
Executor(
|
|
219
|
+
size,
|
|
220
|
+
pool_hooks(setups),
|
|
221
|
+
pool_hooks(cleanups),
|
|
222
|
+
),
|
|
223
|
+
size,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class ProcessPool(Pool):
|
|
228
|
+
logger = getLogger('DefStream.ProcessPool')
|
|
229
|
+
|
|
230
|
+
def __init__(
|
|
231
|
+
self,
|
|
232
|
+
max: Optional[int] = None,
|
|
233
|
+
setups: Optional[Union[PoolSetup, Sequence[PoolSetup]]] = None,
|
|
234
|
+
cleanups: Optional[Union[PoolCleanup, Sequence[PoolCleanup]]] = None,
|
|
235
|
+
):
|
|
236
|
+
size = pool_size(max)
|
|
237
|
+
super().__init__(
|
|
238
|
+
Executor(
|
|
239
|
+
size,
|
|
240
|
+
pool_hooks(setups),
|
|
241
|
+
pool_hooks(cleanups),
|
|
242
|
+
process=True,
|
|
243
|
+
),
|
|
244
|
+
size,
|
|
245
|
+
)
|