qena-shared-lib 0.1.10__py3-none-any.whl → 0.1.12__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.
- qena_shared_lib/application.py +16 -11
- qena_shared_lib/background.py +14 -11
- qena_shared_lib/dependencies/http.py +3 -3
- qena_shared_lib/exception_handlers.py +4 -0
- qena_shared_lib/exceptions.py +59 -49
- qena_shared_lib/http.py +57 -3
- qena_shared_lib/logging.py +3 -3
- qena_shared_lib/logstash/_base.py +31 -30
- qena_shared_lib/logstash/_http_sender.py +1 -1
- qena_shared_lib/logstash/_tcp_sender.py +9 -9
- qena_shared_lib/rabbitmq/__init__.py +2 -0
- qena_shared_lib/rabbitmq/_base.py +32 -26
- qena_shared_lib/rabbitmq/_channel.py +24 -14
- qena_shared_lib/rabbitmq/_exception_handlers.py +3 -3
- qena_shared_lib/rabbitmq/_listener.py +46 -47
- qena_shared_lib/rabbitmq/_pool.py +2 -2
- qena_shared_lib/rabbitmq/_publisher.py +3 -3
- qena_shared_lib/rabbitmq/_rpc_client.py +28 -22
- qena_shared_lib/scheduler.py +20 -18
- qena_shared_lib/security.py +2 -2
- qena_shared_lib/utils.py +5 -22
- {qena_shared_lib-0.1.10.dist-info → qena_shared_lib-0.1.12.dist-info}/METADATA +2 -2
- qena_shared_lib-0.1.12.dist-info/RECORD +29 -0
- qena_shared_lib-0.1.10.dist-info/RECORD +0 -29
- {qena_shared_lib-0.1.10.dist-info → qena_shared_lib-0.1.12.dist-info}/WHEEL +0 -0
@@ -59,9 +59,9 @@ class FlowControl:
|
|
59
59
|
self._channel = channel
|
60
60
|
self._loop = loop
|
61
61
|
self._lock = Lock()
|
62
|
-
self._flow_control_future = None
|
62
|
+
self._flow_control_future: Future | None = None
|
63
63
|
|
64
|
-
async def request(self, prefetch_count: int):
|
64
|
+
async def request(self, prefetch_count: int) -> None:
|
65
65
|
async with self._lock:
|
66
66
|
self._flow_control_future = self._loop.create_future()
|
67
67
|
|
@@ -72,7 +72,7 @@ class FlowControl:
|
|
72
72
|
|
73
73
|
await self._flow_control_future
|
74
74
|
|
75
|
-
def _on_prefetch_count_set(self, method: Method):
|
75
|
+
def _on_prefetch_count_set(self, method: Method) -> None:
|
76
76
|
del method
|
77
77
|
|
78
78
|
if self._flow_control_future is None:
|
@@ -87,13 +87,13 @@ class RpcReply:
|
|
87
87
|
channel_pool: ChannelPool,
|
88
88
|
reply_to: str,
|
89
89
|
correlation_id: str | None = None,
|
90
|
-
):
|
90
|
+
) -> None:
|
91
91
|
self._channel_pool = channel_pool
|
92
92
|
self._reply_to = reply_to
|
93
93
|
self._correlation_id = correlation_id
|
94
94
|
self._replied = False
|
95
95
|
|
96
|
-
async def reply(self, message: Any):
|
96
|
+
async def reply(self, message: Any) -> None:
|
97
97
|
base_channel = await self._channel_pool.get()
|
98
98
|
|
99
99
|
reply_properties = BasicProperties(content_type="application/json")
|
@@ -128,7 +128,7 @@ class ListenerContext:
|
|
128
128
|
rpc_reply: RpcReply | None = None
|
129
129
|
context_dispose_callback: Callable | None = None
|
130
130
|
|
131
|
-
def dispose(self):
|
131
|
+
def dispose(self) -> None:
|
132
132
|
if self.context_dispose_callback is not None:
|
133
133
|
self.context_dispose_callback(self)
|
134
134
|
|
@@ -145,7 +145,7 @@ class BackoffRetryDelay(RetryDelayStrategy):
|
|
145
145
|
min: float
|
146
146
|
max: float
|
147
147
|
|
148
|
-
def __post_init__(self):
|
148
|
+
def __post_init__(self) -> None:
|
149
149
|
if self.min > self.max:
|
150
150
|
raise ValueError("`min` greater than `max`")
|
151
151
|
|
@@ -173,7 +173,7 @@ class RetryDelayJitter:
|
|
173
173
|
min: float = 0.5
|
174
174
|
max: float = 1.0
|
175
175
|
|
176
|
-
def __post_init__(self):
|
176
|
+
def __post_init__(self) -> None:
|
177
177
|
if self.min > self.max:
|
178
178
|
raise ValueError("`min` greater than `max`")
|
179
179
|
|
@@ -214,7 +214,7 @@ class ListenerChannelAdapter(BaseChannel):
|
|
214
214
|
connection: AsyncioConnection,
|
215
215
|
on_channel_open_callback: Callable[[Channel], None],
|
216
216
|
on_cancel_callback: Callable,
|
217
|
-
):
|
217
|
+
) -> None:
|
218
218
|
super().__init__(
|
219
219
|
connection=connection,
|
220
220
|
failed_reopen_threshold=None,
|
@@ -223,10 +223,10 @@ class ListenerChannelAdapter(BaseChannel):
|
|
223
223
|
self._on_channle_open_listener_callback = on_channel_open_callback
|
224
224
|
self._on_listener_cancel_callback = on_cancel_callback
|
225
225
|
|
226
|
-
def _hook_on_channel_opened(self):
|
226
|
+
def _hook_on_channel_opened(self) -> None:
|
227
227
|
self._on_channle_open_listener_callback(self.channel)
|
228
228
|
|
229
|
-
def _hook_on_cancelled(self):
|
229
|
+
def _hook_on_cancelled(self) -> None:
|
230
230
|
self._on_listener_cancel_callback()
|
231
231
|
|
232
232
|
|
@@ -265,7 +265,7 @@ class Listener(AsyncEventLoopMixin):
|
|
265
265
|
durable: bool = True,
|
266
266
|
purge_on_startup: bool = False,
|
267
267
|
retry_policy: RetryPolicy | None = None,
|
268
|
-
):
|
268
|
+
) -> None:
|
269
269
|
self._queue = queue
|
270
270
|
self._listener_name_header_key = listener_name_header_key
|
271
271
|
self._prefetch_count = prefetch_count
|
@@ -292,7 +292,7 @@ class Listener(AsyncEventLoopMixin):
|
|
292
292
|
listener_name: str | None,
|
293
293
|
listener_method: Callable,
|
294
294
|
retry_policy: RetryPolicy | None = None,
|
295
|
-
):
|
295
|
+
) -> None:
|
296
296
|
self._register_listener_method(
|
297
297
|
listener_name=listener_name,
|
298
298
|
listener_method=listener_method,
|
@@ -306,7 +306,7 @@ class Listener(AsyncEventLoopMixin):
|
|
306
306
|
listener_method: Callable,
|
307
307
|
parameters: MappingProxyType[str, Parameter],
|
308
308
|
retry_policy: RetryPolicy | None = None,
|
309
|
-
):
|
309
|
+
) -> None:
|
310
310
|
listener_name = listener_name or "__default__"
|
311
311
|
|
312
312
|
if listener_name in self._listeners:
|
@@ -345,7 +345,7 @@ class Listener(AsyncEventLoopMixin):
|
|
345
345
|
container: Container,
|
346
346
|
logstash: BaseLogstashSender,
|
347
347
|
global_retry_policy: RetryPolicy | None = None,
|
348
|
-
):
|
348
|
+
) -> None:
|
349
349
|
self._connection = connection
|
350
350
|
self._channel_pool = channel_pool
|
351
351
|
self._listener_future = self.loop.create_future()
|
@@ -362,16 +362,16 @@ class Listener(AsyncEventLoopMixin):
|
|
362
362
|
|
363
363
|
await self._listener_future
|
364
364
|
|
365
|
-
def _on_channel_opened(self, channel: Channel):
|
365
|
+
def _on_channel_opened(self, channel: Channel) -> None:
|
366
366
|
self._channel = channel
|
367
367
|
self._flow_control = FlowControl(channel=self._channel, loop=self.loop)
|
368
368
|
|
369
369
|
self._declare_queue()
|
370
370
|
|
371
|
-
def _on_cancelled(self):
|
371
|
+
def _on_cancelled(self) -> None:
|
372
372
|
self._declare_queue()
|
373
373
|
|
374
|
-
def _declare_queue(self):
|
374
|
+
def _declare_queue(self) -> None:
|
375
375
|
try:
|
376
376
|
self._channel.queue_declare(
|
377
377
|
queue=self._queue,
|
@@ -381,7 +381,7 @@ class Listener(AsyncEventLoopMixin):
|
|
381
381
|
except Exception as e:
|
382
382
|
self._fail_listener(e)
|
383
383
|
|
384
|
-
def _on_queue_declared(self, method: Method):
|
384
|
+
def _on_queue_declared(self, method: Method) -> None:
|
385
385
|
del method
|
386
386
|
|
387
387
|
if self._purge_on_startup:
|
@@ -395,7 +395,7 @@ class Listener(AsyncEventLoopMixin):
|
|
395
395
|
else:
|
396
396
|
self._set_prefetch_count()
|
397
397
|
|
398
|
-
def _set_prefetch_count(self):
|
398
|
+
def _set_prefetch_count(self) -> None:
|
399
399
|
try:
|
400
400
|
self._channel.basic_qos(
|
401
401
|
prefetch_count=self._prefetch_count,
|
@@ -404,7 +404,7 @@ class Listener(AsyncEventLoopMixin):
|
|
404
404
|
except Exception as e:
|
405
405
|
self._fail_listener(e)
|
406
406
|
|
407
|
-
def _register_listener(self):
|
407
|
+
def _register_listener(self) -> None:
|
408
408
|
try:
|
409
409
|
_ = self._channel.basic_consume(
|
410
410
|
queue=self._queue,
|
@@ -419,7 +419,7 @@ class Listener(AsyncEventLoopMixin):
|
|
419
419
|
if not self._listener_future.done():
|
420
420
|
self._listener_future.set_result(None)
|
421
421
|
|
422
|
-
def _fail_listener(self, exception: Exception):
|
422
|
+
def _fail_listener(self, exception: Exception) -> None:
|
423
423
|
if not self._listener_future.done():
|
424
424
|
self._listener_future.set_exception(exception)
|
425
425
|
|
@@ -432,7 +432,7 @@ class Listener(AsyncEventLoopMixin):
|
|
432
432
|
method: Basic.Deliver,
|
433
433
|
properties: BasicProperties,
|
434
434
|
body: bytes,
|
435
|
-
):
|
435
|
+
) -> None:
|
436
436
|
del channel
|
437
437
|
|
438
438
|
if properties.headers is not None:
|
@@ -486,7 +486,7 @@ class Listener(AsyncEventLoopMixin):
|
|
486
486
|
|
487
487
|
def _on_submitted_listener_error(
|
488
488
|
self, listener_message_meta: ListenerMessageMeta, future: Future
|
489
|
-
):
|
489
|
+
) -> None:
|
490
490
|
if future.cancelled():
|
491
491
|
return
|
492
492
|
|
@@ -499,7 +499,9 @@ class Listener(AsyncEventLoopMixin):
|
|
499
499
|
message=f"error occured while submitting listener callback on listener `{listener_message_meta.listener_name}` and queue `{self._queue}`",
|
500
500
|
)
|
501
501
|
|
502
|
-
def _parse_and_execute(
|
502
|
+
def _parse_and_execute(
|
503
|
+
self, listener_message_meta: ListenerMessageMeta
|
504
|
+
) -> None:
|
503
505
|
try:
|
504
506
|
listener_method_args, listener_method_kwargs = self._parse_args(
|
505
507
|
listener_message_meta
|
@@ -543,7 +545,7 @@ class Listener(AsyncEventLoopMixin):
|
|
543
545
|
except:
|
544
546
|
message = listener_message_meta.body.decode()
|
545
547
|
|
546
|
-
assigned_args = []
|
548
|
+
assigned_args: list[Any] = []
|
547
549
|
listener_method_args = []
|
548
550
|
listener_method_kwargs = {}
|
549
551
|
next_positional_arg = 0
|
@@ -578,8 +580,8 @@ class Listener(AsyncEventLoopMixin):
|
|
578
580
|
)
|
579
581
|
|
580
582
|
if (
|
581
|
-
parameter.kind
|
582
|
-
or parameter.kind
|
583
|
+
parameter.kind == Parameter.POSITIONAL_ONLY
|
584
|
+
or parameter.kind == Parameter.POSITIONAL_OR_KEYWORD
|
583
585
|
):
|
584
586
|
if (
|
585
587
|
listener_context is not None
|
@@ -614,7 +616,7 @@ class Listener(AsyncEventLoopMixin):
|
|
614
616
|
raise ValueError(
|
615
617
|
f"argument {parameter.name} has no default"
|
616
618
|
)
|
617
|
-
elif parameter.kind
|
619
|
+
elif parameter.kind == Parameter.VAR_POSITIONAL:
|
618
620
|
listener_method_args.extend(
|
619
621
|
[
|
620
622
|
self._validate_parameter(
|
@@ -626,10 +628,7 @@ class Listener(AsyncEventLoopMixin):
|
|
626
628
|
)
|
627
629
|
|
628
630
|
next_positional_arg += len(args[next_positional_arg:])
|
629
|
-
elif
|
630
|
-
parameter.kind is Parameter.KEYWORD_ONLY
|
631
|
-
or parameter.kind is Parameter.POSITIONAL_OR_KEYWORD
|
632
|
-
):
|
631
|
+
elif parameter.kind == Parameter.KEYWORD_ONLY:
|
633
632
|
if (
|
634
633
|
listener_context is not None
|
635
634
|
or dependency is not None
|
@@ -654,7 +653,7 @@ class Listener(AsyncEventLoopMixin):
|
|
654
653
|
raise ValueError(
|
655
654
|
f"argument {parameter.name} has no default"
|
656
655
|
)
|
657
|
-
elif parameter.kind
|
656
|
+
elif parameter.kind == Parameter.VAR_KEYWORD:
|
658
657
|
listener_method_kwargs.update(
|
659
658
|
{
|
660
659
|
k: self._validate_parameter(
|
@@ -741,7 +740,7 @@ class Listener(AsyncEventLoopMixin):
|
|
741
740
|
self,
|
742
741
|
listener_message_meta: ListenerMessageMeta,
|
743
742
|
task_or_future: Task | Future,
|
744
|
-
):
|
743
|
+
) -> None:
|
745
744
|
if task_or_future.cancelled():
|
746
745
|
return
|
747
746
|
|
@@ -829,7 +828,7 @@ class Listener(AsyncEventLoopMixin):
|
|
829
828
|
|
830
829
|
def _observe_listener_time(
|
831
830
|
self, listener_message_meta: ListenerMessageMeta
|
832
|
-
):
|
831
|
+
) -> None:
|
833
832
|
self.LISTENER_PROCESSING_LATENCY.labels(
|
834
833
|
queue=self._queue, listener_name=listener_message_meta.listener_name
|
835
834
|
).observe(listener_message_meta.listener_start_time - time())
|
@@ -876,13 +875,13 @@ class Listener(AsyncEventLoopMixin):
|
|
876
875
|
exception: BaseException,
|
877
876
|
listener_message_meta: ListenerMessageMeta,
|
878
877
|
message: str | None = None,
|
879
|
-
):
|
878
|
+
) -> None:
|
880
879
|
context_dispose_callback = None
|
881
880
|
rpc_reply = None
|
882
881
|
|
883
882
|
if listener_message_meta.properties.reply_to is not None:
|
884
883
|
|
885
|
-
def on_context_disposed(context: ListenerContext):
|
884
|
+
def on_context_disposed(context: ListenerContext) -> None:
|
886
885
|
assert listener_message_meta.properties.reply_to is not None
|
887
886
|
assert context.rpc_reply is not None
|
888
887
|
|
@@ -954,7 +953,7 @@ class Listener(AsyncEventLoopMixin):
|
|
954
953
|
listener_message_meta: ListenerMessageMeta,
|
955
954
|
retry_policy: RetryPolicy,
|
956
955
|
times_rejected: int,
|
957
|
-
):
|
956
|
+
) -> None:
|
958
957
|
message_redelivery_delay = retry_policy.next_delay(times_rejected)
|
959
958
|
|
960
959
|
self._logger.debug(
|
@@ -977,7 +976,7 @@ class Listener(AsyncEventLoopMixin):
|
|
977
976
|
self,
|
978
977
|
listener_message_meta: ListenerMessageMeta,
|
979
978
|
times_rejected: int,
|
980
|
-
):
|
979
|
+
) -> None:
|
981
980
|
self.loop.create_task(self._channel_pool.get()).add_done_callback(
|
982
981
|
partial(
|
983
982
|
self._on_redelivery_channel_found,
|
@@ -991,7 +990,7 @@ class Listener(AsyncEventLoopMixin):
|
|
991
990
|
listener_message_meta: ListenerMessageMeta,
|
992
991
|
times_rejected: int,
|
993
992
|
task: Task[BaseChannel],
|
994
|
-
):
|
993
|
+
) -> None:
|
995
994
|
if task.cancelled():
|
996
995
|
return
|
997
996
|
|
@@ -1044,7 +1043,7 @@ class Listener(AsyncEventLoopMixin):
|
|
1044
1043
|
self,
|
1045
1044
|
listener_message_meta: ListenerMessageMeta,
|
1046
1045
|
response: Any,
|
1047
|
-
):
|
1046
|
+
) -> None:
|
1048
1047
|
assert listener_message_meta.properties.reply_to is not None
|
1049
1048
|
|
1050
1049
|
reponse_properties = BasicProperties(content_type="application/json")
|
@@ -1108,7 +1107,7 @@ class Listener(AsyncEventLoopMixin):
|
|
1108
1107
|
response_body: bytes,
|
1109
1108
|
response_properties: BasicProperties,
|
1110
1109
|
task: Task[BaseChannel],
|
1111
|
-
):
|
1110
|
+
) -> None:
|
1112
1111
|
if task.cancelled():
|
1113
1112
|
return
|
1114
1113
|
|
@@ -1159,7 +1158,7 @@ class Consumer(Listener):
|
|
1159
1158
|
queue: str,
|
1160
1159
|
prefetch_count: int = 250,
|
1161
1160
|
retry_policy: RetryPolicy | None = None,
|
1162
|
-
):
|
1161
|
+
) -> None:
|
1163
1162
|
super().__init__(
|
1164
1163
|
queue=queue,
|
1165
1164
|
listener_name_header_key="target",
|
@@ -1170,7 +1169,7 @@ class Consumer(Listener):
|
|
1170
1169
|
def consume(
|
1171
1170
|
self, target: str | None = None, retry_policy: RetryPolicy | None = None
|
1172
1171
|
) -> Callable[[Callable], Callable]:
|
1173
|
-
def wrapper(consumer_method: Callable):
|
1172
|
+
def wrapper(consumer_method: Callable) -> Callable:
|
1174
1173
|
if not callable(consumer_method):
|
1175
1174
|
raise TypeError(
|
1176
1175
|
f"consumer method argument not a callable, got {type(consumer_method)}"
|
@@ -1221,7 +1220,7 @@ def consume(
|
|
1221
1220
|
|
1222
1221
|
|
1223
1222
|
class RpcWorker(Listener):
|
1224
|
-
def __init__(self, queue: str, prefetch_count: int = 250):
|
1223
|
+
def __init__(self, queue: str, prefetch_count: int = 250) -> None:
|
1225
1224
|
super().__init__(
|
1226
1225
|
queue=queue,
|
1227
1226
|
listener_name_header_key="procedure",
|
@@ -1232,7 +1231,7 @@ class RpcWorker(Listener):
|
|
1232
1231
|
def execute(
|
1233
1232
|
self, procedure: str | None = None
|
1234
1233
|
) -> Callable[[Callable], Callable]:
|
1235
|
-
def wrapper(worker_method: Callable):
|
1234
|
+
def wrapper(worker_method: Callable) -> Callable:
|
1236
1235
|
if not callable(worker_method):
|
1237
1236
|
raise TypeError(
|
1238
1237
|
f"worker method argument not a callable, got {type(worker_method)}"
|
@@ -21,14 +21,14 @@ class ChannelPool(AsyncEventLoopMixin):
|
|
21
21
|
async def fill(
|
22
22
|
self,
|
23
23
|
connection: AsyncioConnection,
|
24
|
-
):
|
24
|
+
) -> None:
|
25
25
|
self._connection = connection
|
26
26
|
|
27
27
|
async with self._pool_lock:
|
28
28
|
for _ in range(self._initial_pool_size):
|
29
29
|
await self._add_new_channel()
|
30
30
|
|
31
|
-
async def drain(self):
|
31
|
+
async def drain(self) -> None:
|
32
32
|
async with self._pool_lock:
|
33
33
|
for channel_base in self._pool.values():
|
34
34
|
with channel_base as channel:
|
@@ -40,13 +40,13 @@ class Publisher:
|
|
40
40
|
)
|
41
41
|
self._logger = LoggerProvider.default().get_logger("rabbitmq.publisher")
|
42
42
|
|
43
|
-
async def publish_as_arguments(self, *args, **kwargs):
|
43
|
+
async def publish_as_arguments(self, *args: Any, **kwargs: Any) -> None:
|
44
44
|
await self._get_channel_and_publish({"args": args, "kwargs": kwargs})
|
45
45
|
|
46
|
-
async def publish(self, message: Any | None = None):
|
46
|
+
async def publish(self, message: Any | None = None) -> None:
|
47
47
|
await self._get_channel_and_publish(message)
|
48
48
|
|
49
|
-
async def _get_channel_and_publish(self, message: Any):
|
49
|
+
async def _get_channel_and_publish(self, message: Any) -> None:
|
50
50
|
if self._blocked_connection_check_callback():
|
51
51
|
raise RabbitMQBlockedError(
|
52
52
|
"rabbitmq broker is not able to accept message right now"
|
@@ -2,7 +2,7 @@ from asyncio import Future, Lock
|
|
2
2
|
from functools import partial
|
3
3
|
from importlib import import_module
|
4
4
|
from time import time
|
5
|
-
from typing import Any, Callable
|
5
|
+
from typing import Any, Callable, Generic, TypeVar
|
6
6
|
from uuid import uuid4
|
7
7
|
|
8
8
|
from pika import BasicProperties
|
@@ -27,26 +27,26 @@ __all__ = ["RpcClient"]
|
|
27
27
|
|
28
28
|
class ExitHandler:
|
29
29
|
_exiting = False
|
30
|
-
_rpc_futures = []
|
30
|
+
_rpc_futures: list[Future] = []
|
31
31
|
_original_exit_handler: Callable
|
32
32
|
|
33
33
|
@classmethod
|
34
|
-
def is_exising(cls):
|
34
|
+
def is_exising(cls) -> bool:
|
35
35
|
return cls._exiting
|
36
36
|
|
37
37
|
@classmethod
|
38
|
-
def add_rpc_future(cls, rpc_future: Future):
|
38
|
+
def add_rpc_future(cls, rpc_future: Future) -> None:
|
39
39
|
cls._rpc_futures.append(rpc_future)
|
40
40
|
|
41
41
|
@classmethod
|
42
|
-
def remove_rpc_future(cls, rpc_future: Future):
|
42
|
+
def remove_rpc_future(cls, rpc_future: Future) -> None:
|
43
43
|
try:
|
44
44
|
cls._rpc_futures.remove(rpc_future)
|
45
45
|
except:
|
46
46
|
pass
|
47
47
|
|
48
48
|
@classmethod
|
49
|
-
def cancel_futures(cls):
|
49
|
+
def cancel_futures(cls) -> None:
|
50
50
|
cls._exiting = True
|
51
51
|
|
52
52
|
for rpc_future in cls._rpc_futures:
|
@@ -54,7 +54,7 @@ class ExitHandler:
|
|
54
54
|
rpc_future.cancel()
|
55
55
|
|
56
56
|
@staticmethod
|
57
|
-
def patch_exit_handler():
|
57
|
+
def patch_exit_handler() -> None:
|
58
58
|
try:
|
59
59
|
Server = import_module("uvicorn.server").Server
|
60
60
|
except ModuleNotFoundError:
|
@@ -64,11 +64,11 @@ class ExitHandler:
|
|
64
64
|
Server.handle_exit = ExitHandler.handle_exit
|
65
65
|
|
66
66
|
@staticmethod
|
67
|
-
def notify_clients():
|
67
|
+
def notify_clients() -> None:
|
68
68
|
ExitHandler.cancel_futures()
|
69
69
|
|
70
70
|
@staticmethod
|
71
|
-
def handle_exit(*args, **kwargs):
|
71
|
+
def handle_exit(*args: Any, **kwargs: Any) -> None:
|
72
72
|
ExitHandler.notify_clients()
|
73
73
|
ExitHandler._original_exit_handler(*args, **kwargs)
|
74
74
|
|
@@ -76,7 +76,10 @@ class ExitHandler:
|
|
76
76
|
ExitHandler.patch_exit_handler()
|
77
77
|
|
78
78
|
|
79
|
-
|
79
|
+
R = TypeVar("R")
|
80
|
+
|
81
|
+
|
82
|
+
class RpcClient(Generic[R], AsyncEventLoopMixin):
|
80
83
|
SUCCEEDED_RPC_CALLS = Counter(
|
81
84
|
name="succeeded_rpc_calls",
|
82
85
|
documentation="RPC calls made",
|
@@ -101,7 +104,7 @@ class RpcClient(AsyncEventLoopMixin):
|
|
101
104
|
exchange: str | None = None,
|
102
105
|
procedure: str | None = None,
|
103
106
|
headers: dict[str, str] | None = None,
|
104
|
-
return_type: type | None = None,
|
107
|
+
return_type: type[R] | None = None,
|
105
108
|
timeout: float = 15,
|
106
109
|
):
|
107
110
|
self._routing_key = routing_key
|
@@ -117,23 +120,23 @@ class RpcClient(AsyncEventLoopMixin):
|
|
117
120
|
self._blocked_connection_check_callback = (
|
118
121
|
blocked_connection_check_callback
|
119
122
|
)
|
120
|
-
self._rpc_future = None
|
121
|
-
self._rpc_call_start_time = None
|
123
|
+
self._rpc_future: Future[R] | None = None
|
124
|
+
self._rpc_call_start_time: float | None = None
|
122
125
|
self._rpc_call_lock = Lock()
|
123
126
|
self._rpc_call_pending = False
|
124
127
|
self._logger = LoggerProvider.default().get_logger(
|
125
128
|
"rabbitmq.rpc_client"
|
126
129
|
)
|
127
130
|
|
128
|
-
async def call_with_arguments(self, *args, **kwargs) ->
|
131
|
+
async def call_with_arguments(self, *args: Any, **kwargs: Any) -> R:
|
129
132
|
return await self._get_channel_and_call(
|
130
133
|
({"args": args, "kwargs": kwargs})
|
131
134
|
)
|
132
135
|
|
133
|
-
async def call(self, message: Any | None = None) ->
|
136
|
+
async def call(self, message: Any | None = None) -> R:
|
134
137
|
return await self._get_channel_and_call(message)
|
135
138
|
|
136
|
-
async def _get_channel_and_call(self, message: Any) ->
|
139
|
+
async def _get_channel_and_call(self, message: Any) -> R:
|
137
140
|
if self._blocked_connection_check_callback():
|
138
141
|
raise RabbitMQBlockedError(
|
139
142
|
"rabbitmq broker is not able to accept message right now"
|
@@ -162,7 +165,7 @@ class RpcClient(AsyncEventLoopMixin):
|
|
162
165
|
|
163
166
|
return await self._rpc_future
|
164
167
|
|
165
|
-
def _on_queue_declared(self, message: Any, method: Method):
|
168
|
+
def _on_queue_declared(self, message: Any, method: Method) -> None:
|
166
169
|
try:
|
167
170
|
self._rpc_reply_consumer_tag = self._channel.basic_consume(
|
168
171
|
queue=method.method.queue,
|
@@ -194,7 +197,7 @@ class RpcClient(AsyncEventLoopMixin):
|
|
194
197
|
return
|
195
198
|
|
196
199
|
if self._timeout > 0:
|
197
|
-
|
200
|
+
self._timeout_timer_handle = self.loop.call_later(
|
198
201
|
delay=self._timeout, callback=self._on_timeout
|
199
202
|
)
|
200
203
|
|
@@ -205,7 +208,7 @@ class RpcClient(AsyncEventLoopMixin):
|
|
205
208
|
self._procedure,
|
206
209
|
)
|
207
210
|
|
208
|
-
def _on_timeout(self):
|
211
|
+
def _on_timeout(self) -> None:
|
209
212
|
self._finalize_call(
|
210
213
|
exception=RabbitMQRpcRequestTimeoutError(
|
211
214
|
f"rpc worker didn't responed in a timely manner within `{self._timeout}` seconds"
|
@@ -218,7 +221,7 @@ class RpcClient(AsyncEventLoopMixin):
|
|
218
221
|
method: Basic.Deliver,
|
219
222
|
properties: BasicProperties,
|
220
223
|
body: bytes,
|
221
|
-
):
|
224
|
+
) -> None:
|
222
225
|
del channel, method
|
223
226
|
|
224
227
|
if properties.correlation_id != self._correlation_id:
|
@@ -260,9 +263,12 @@ class RpcClient(AsyncEventLoopMixin):
|
|
260
263
|
|
261
264
|
def _finalize_call(
|
262
265
|
self,
|
263
|
-
response: Any
|
266
|
+
response: Any = None,
|
264
267
|
exception: BaseException | None = None,
|
265
|
-
):
|
268
|
+
) -> None:
|
269
|
+
if not self._timeout_timer_handle.cancelled():
|
270
|
+
self._timeout_timer_handle.cancel()
|
271
|
+
|
266
272
|
self._rpc_call_pending = False
|
267
273
|
self._channel.basic_cancel(self._rpc_reply_consumer_tag)
|
268
274
|
self._channel_base.release()
|
qena_shared_lib/scheduler.py
CHANGED
@@ -36,8 +36,8 @@ class ScheduledTask:
|
|
36
36
|
cron_expression: str
|
37
37
|
zone_info: ZoneInfo | None
|
38
38
|
|
39
|
-
def __post_init__(self):
|
40
|
-
self._next_run_in = None
|
39
|
+
def __post_init__(self) -> None:
|
40
|
+
self._next_run_in: int | None = None
|
41
41
|
self._ran = False
|
42
42
|
self._paramters = {}
|
43
43
|
|
@@ -56,7 +56,7 @@ class ScheduledTask:
|
|
56
56
|
return self._next_run_in
|
57
57
|
|
58
58
|
@next_run_in.setter
|
59
|
-
def next_run_in(self, value: int):
|
59
|
+
def next_run_in(self, value: int) -> None:
|
60
60
|
self._next_run_in = value
|
61
61
|
|
62
62
|
@property
|
@@ -64,7 +64,7 @@ class ScheduledTask:
|
|
64
64
|
return self._ran
|
65
65
|
|
66
66
|
@ran.setter
|
67
|
-
def ran(self, value: bool):
|
67
|
+
def ran(self, value: bool) -> None:
|
68
68
|
self._ran = value
|
69
69
|
|
70
70
|
@property
|
@@ -73,7 +73,7 @@ class ScheduledTask:
|
|
73
73
|
|
74
74
|
|
75
75
|
class Scheduler:
|
76
|
-
def __init__(self):
|
76
|
+
def __init__(self) -> None:
|
77
77
|
self._scheduled_tasks: list[ScheduledTask] = []
|
78
78
|
|
79
79
|
def schedule(
|
@@ -90,7 +90,7 @@ class Scheduler:
|
|
90
90
|
|
91
91
|
def add_task(
|
92
92
|
self, task: Callable, cron_expression: str, timezone: str | None
|
93
|
-
):
|
93
|
+
) -> None:
|
94
94
|
self._scheduled_tasks.append(
|
95
95
|
ScheduledTask(
|
96
96
|
task=task,
|
@@ -188,15 +188,17 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
188
188
|
self,
|
189
189
|
logstash: BaseLogstashSender,
|
190
190
|
container: Container | None = None,
|
191
|
-
):
|
191
|
+
) -> None:
|
192
192
|
self._container = container or Container()
|
193
193
|
self._logstash = logstash
|
194
194
|
self._scheduled_tasks: list[ScheduledTask] = []
|
195
|
-
self._next_run_in = None
|
196
|
-
self._scheduler_task = None
|
195
|
+
self._next_run_in: int | None = None
|
196
|
+
self._scheduler_task: Task | None = None
|
197
197
|
self._logger = LoggerProvider.default().get_logger("schedule_manager")
|
198
198
|
|
199
|
-
def include_scheduler(
|
199
|
+
def include_scheduler(
|
200
|
+
self, scheduler: Scheduler | type[SchedulerBase]
|
201
|
+
) -> None:
|
200
202
|
if isinstance(scheduler, Scheduler):
|
201
203
|
self._scheduled_tasks.extend(scheduler.scheduled_tasks)
|
202
204
|
|
@@ -229,7 +231,7 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
229
231
|
def scheduled_task_count(self) -> int:
|
230
232
|
return len(self._scheduled_tasks)
|
231
233
|
|
232
|
-
def start(self):
|
234
|
+
def start(self) -> None:
|
233
235
|
if not self._aquired_lock():
|
234
236
|
return
|
235
237
|
|
@@ -251,20 +253,20 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
251
253
|
self._scheduler_task.add_done_callback(self._on_scheduler_done)
|
252
254
|
self.SCHEDULE_MANAGER_STATE.state("running")
|
253
255
|
|
254
|
-
def use_schedulers(self):
|
256
|
+
def use_schedulers(self) -> None:
|
255
257
|
for scheduler in [
|
256
258
|
scheduler.register_scheduled_tasks()
|
257
259
|
for scheduler in self._container.resolve_all(SchedulerBase)
|
258
260
|
]:
|
259
261
|
self._scheduled_tasks.extend(scheduler.scheduled_tasks)
|
260
262
|
|
261
|
-
def stop(self):
|
263
|
+
def stop(self) -> None:
|
262
264
|
if self._scheduler_task is not None and not self._scheduler_task.done():
|
263
265
|
self._scheduler_task.cancel()
|
264
266
|
|
265
267
|
self.SCHEDULE_MANAGER_STATE.state("stopped")
|
266
268
|
|
267
|
-
def _on_scheduler_done(self, task: Task):
|
269
|
+
def _on_scheduler_done(self, task: Task) -> None:
|
268
270
|
if task.cancelled():
|
269
271
|
return
|
270
272
|
|
@@ -306,7 +308,7 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
306
308
|
|
307
309
|
return True
|
308
310
|
|
309
|
-
async def _run_scheduler(self):
|
311
|
+
async def _run_scheduler(self) -> None:
|
310
312
|
while True:
|
311
313
|
self._calculate_next_schedule()
|
312
314
|
self._logger.debug(
|
@@ -344,7 +346,7 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
344
346
|
def _resolve_dependencies(
|
345
347
|
self, scheduled_task: ScheduledTask
|
346
348
|
) -> dict[str, Any]:
|
347
|
-
args = {}
|
349
|
+
args: dict[str, Any] = {}
|
348
350
|
|
349
351
|
if self._container is None:
|
350
352
|
return args
|
@@ -354,7 +356,7 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
354
356
|
|
355
357
|
return args
|
356
358
|
|
357
|
-
def _on_task_done(self, task_or_future: Task | Future):
|
359
|
+
def _on_task_done(self, task_or_future: Task | Future) -> None:
|
358
360
|
if task_or_future.cancelled():
|
359
361
|
return
|
360
362
|
|
@@ -366,7 +368,7 @@ class ScheduleManager(AsyncEventLoopMixin):
|
|
366
368
|
exception=exception,
|
367
369
|
)
|
368
370
|
|
369
|
-
def _calculate_next_schedule(self):
|
371
|
+
def _calculate_next_schedule(self) -> None:
|
370
372
|
prev_run_in = self._next_run_in or 0
|
371
373
|
self._next_run_in = None
|
372
374
|
|