qena-shared-lib 0.1.12__py3-none-any.whl → 0.1.14__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/__init__.py +2 -2
- qena_shared_lib/application.py +71 -29
- qena_shared_lib/background.py +6 -5
- qena_shared_lib/exception_handlers.py +203 -175
- qena_shared_lib/exceptions.py +10 -10
- qena_shared_lib/http.py +16 -16
- qena_shared_lib/rabbitmq/__init__.py +8 -6
- qena_shared_lib/rabbitmq/_base.py +96 -127
- qena_shared_lib/rabbitmq/_channel.py +4 -1
- qena_shared_lib/rabbitmq/_exception_handlers.py +154 -119
- qena_shared_lib/rabbitmq/_listener.py +94 -38
- qena_shared_lib/rabbitmq/_rpc_client.py +4 -4
- qena_shared_lib/remotelogging/__init__.py +15 -0
- qena_shared_lib/{logstash → remotelogging}/_base.py +47 -67
- qena_shared_lib/remotelogging/logstash/__init__.py +9 -0
- qena_shared_lib/remotelogging/logstash/_base.py +32 -0
- qena_shared_lib/{logstash → remotelogging/logstash}/_http_sender.py +5 -4
- qena_shared_lib/{logstash → remotelogging/logstash}/_tcp_sender.py +7 -5
- qena_shared_lib/scheduler.py +49 -24
- qena_shared_lib/security.py +2 -2
- qena_shared_lib/utils.py +9 -3
- {qena_shared_lib-0.1.12.dist-info → qena_shared_lib-0.1.14.dist-info}/METADATA +23 -20
- qena_shared_lib-0.1.14.dist-info/RECORD +31 -0
- qena_shared_lib/logstash/__init__.py +0 -17
- qena_shared_lib-0.1.12.dist-info/RECORD +0 -29
- {qena_shared_lib-0.1.12.dist-info → qena_shared_lib-0.1.14.dist-info}/WHEEL +0 -0
qena_shared_lib/exceptions.py
CHANGED
@@ -73,7 +73,7 @@ class ServiceException(Exception):
|
|
73
73
|
severity: Severity | None = None,
|
74
74
|
tags: list[str] | None = None,
|
75
75
|
extra: dict[str, str] | None = None,
|
76
|
-
|
76
|
+
remote_logging: bool | None = None,
|
77
77
|
extract_exc_info: bool | None = None,
|
78
78
|
):
|
79
79
|
self._message = message
|
@@ -88,10 +88,10 @@ class ServiceException(Exception):
|
|
88
88
|
self._tags = tags
|
89
89
|
self._extra = extra
|
90
90
|
|
91
|
-
if
|
92
|
-
self.
|
91
|
+
if remote_logging is not None:
|
92
|
+
self._remote_logging = remote_logging
|
93
93
|
else:
|
94
|
-
self.
|
94
|
+
self._remote_logging = True
|
95
95
|
|
96
96
|
if extract_exc_info is not None:
|
97
97
|
self._extract_exc_info = extract_exc_info
|
@@ -117,8 +117,8 @@ class ServiceException(Exception):
|
|
117
117
|
return self._extra
|
118
118
|
|
119
119
|
@property
|
120
|
-
def
|
121
|
-
return self.
|
120
|
+
def remote_logging(self) -> bool | None:
|
121
|
+
return self._remote_logging
|
122
122
|
|
123
123
|
@property
|
124
124
|
def extract_exc_info(self) -> bool | None:
|
@@ -145,7 +145,7 @@ class HTTPServiceError(ServiceException):
|
|
145
145
|
severity: Severity | None = None,
|
146
146
|
tags: list[str] | None = None,
|
147
147
|
extra: dict[str, str] | None = None,
|
148
|
-
|
148
|
+
remote_logging: bool = True,
|
149
149
|
extract_exc_info: bool = True,
|
150
150
|
):
|
151
151
|
super().__init__(
|
@@ -153,7 +153,7 @@ class HTTPServiceError(ServiceException):
|
|
153
153
|
severity=severity,
|
154
154
|
tags=tags,
|
155
155
|
extra=extra,
|
156
|
-
|
156
|
+
remote_logging=remote_logging,
|
157
157
|
extract_exc_info=extract_exc_info,
|
158
158
|
)
|
159
159
|
|
@@ -379,7 +379,7 @@ class RabbitMQServiceException(ServiceException):
|
|
379
379
|
severity: Severity | None = None,
|
380
380
|
tags: list[str] | None = None,
|
381
381
|
extra: dict[str, str] | None = None,
|
382
|
-
|
382
|
+
remote_logging: bool | None = None,
|
383
383
|
extract_exc_info: bool | None = None,
|
384
384
|
):
|
385
385
|
super().__init__(
|
@@ -387,7 +387,7 @@ class RabbitMQServiceException(ServiceException):
|
|
387
387
|
severity=severity,
|
388
388
|
tags=tags,
|
389
389
|
extra=extra,
|
390
|
-
|
390
|
+
remote_logging=remote_logging,
|
391
391
|
extract_exc_info=extract_exc_info,
|
392
392
|
)
|
393
393
|
|
qena_shared_lib/http.py
CHANGED
@@ -168,8 +168,8 @@ def get(
|
|
168
168
|
response_class: type[Response] = Default(JSONResponse),
|
169
169
|
name: str | None = None,
|
170
170
|
openapi_extra: dict[str, Any] | None = None,
|
171
|
-
) -> Callable[[Callable], Callable]:
|
172
|
-
def wrapper(route_handler: Callable) -> Callable:
|
171
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
172
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
173
173
|
setattr(
|
174
174
|
route_handler,
|
175
175
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -225,8 +225,8 @@ def put(
|
|
225
225
|
response_class: type[Response] = Default(JSONResponse),
|
226
226
|
name: str | None = None,
|
227
227
|
openapi_extra: dict[str, Any] | None = None,
|
228
|
-
) -> Callable[[Callable], Callable]:
|
229
|
-
def wrapper(route_handler: Callable) -> Callable:
|
228
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
229
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
230
230
|
setattr(
|
231
231
|
route_handler,
|
232
232
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -282,8 +282,8 @@ def post(
|
|
282
282
|
response_class: type[Response] = Default(JSONResponse),
|
283
283
|
name: str | None = None,
|
284
284
|
openapi_extra: dict[str, Any] | None = None,
|
285
|
-
) -> Callable[[Callable], Callable]:
|
286
|
-
def wrapper(route_handler: Callable) -> Callable:
|
285
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
286
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
287
287
|
setattr(
|
288
288
|
route_handler,
|
289
289
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -339,8 +339,8 @@ def delete(
|
|
339
339
|
response_class: type[Response] = Default(JSONResponse),
|
340
340
|
name: str | None = None,
|
341
341
|
openapi_extra: dict[str, Any] | None = None,
|
342
|
-
) -> Callable[[Callable], Callable]:
|
343
|
-
def wrapper(route_handler: Callable) -> Callable:
|
342
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
343
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
344
344
|
setattr(
|
345
345
|
route_handler,
|
346
346
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -396,8 +396,8 @@ def options(
|
|
396
396
|
response_class: type[Response] = Default(JSONResponse),
|
397
397
|
name: str | None = None,
|
398
398
|
openapi_extra: dict[str, Any] | None = None,
|
399
|
-
) -> Callable[[Callable], Callable]:
|
400
|
-
def wrapper(route_handler: Callable) -> Callable:
|
399
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
400
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
401
401
|
setattr(
|
402
402
|
route_handler,
|
403
403
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -453,8 +453,8 @@ def head(
|
|
453
453
|
response_class: type[Response] = Default(JSONResponse),
|
454
454
|
name: str | None = None,
|
455
455
|
openapi_extra: dict[str, Any] | None = None,
|
456
|
-
) -> Callable[[Callable], Callable]:
|
457
|
-
def wrapper(route_handler: Callable) -> Callable:
|
456
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
457
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
458
458
|
setattr(
|
459
459
|
route_handler,
|
460
460
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -510,8 +510,8 @@ def patch(
|
|
510
510
|
response_class: type[Response] = Default(JSONResponse),
|
511
511
|
name: str | None = None,
|
512
512
|
openapi_extra: dict[str, Any] | None = None,
|
513
|
-
) -> Callable[[Callable], Callable]:
|
514
|
-
def wrapper(route_handler: Callable) -> Callable:
|
513
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
514
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
515
515
|
setattr(
|
516
516
|
route_handler,
|
517
517
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -567,8 +567,8 @@ def trace(
|
|
567
567
|
response_class: type[Response] = Default(JSONResponse),
|
568
568
|
name: str | None = None,
|
569
569
|
openapi_extra: dict[str, Any] | None = None,
|
570
|
-
) -> Callable[[Callable], Callable]:
|
571
|
-
def wrapper(route_handler: Callable) -> Callable:
|
570
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
|
571
|
+
def wrapper(route_handler: Callable[..., Any]) -> Callable[..., Any]:
|
572
572
|
setattr(
|
573
573
|
route_handler,
|
574
574
|
ROUTE_HANDLER_ATTRIBUTE,
|
@@ -1,9 +1,10 @@
|
|
1
1
|
from ._base import AbstractRabbitMQService, RabbitMqManager
|
2
2
|
from ._channel import BaseChannel
|
3
3
|
from ._exception_handlers import (
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
AbstractRabbitMqExceptionHandler,
|
5
|
+
GeneralMqExceptionHandler,
|
6
|
+
RabbitMqServiceExceptionHandler,
|
7
|
+
ValidationErrorHandler,
|
7
8
|
)
|
8
9
|
from ._listener import (
|
9
10
|
CONSUMER_ATTRIBUTE,
|
@@ -27,6 +28,7 @@ from ._publisher import Publisher
|
|
27
28
|
from ._rpc_client import RpcClient
|
28
29
|
|
29
30
|
__all__ = [
|
31
|
+
"AbstractRabbitMqExceptionHandler",
|
30
32
|
"AbstractRabbitMQService",
|
31
33
|
"BackoffRetryDelay",
|
32
34
|
"BaseChannel",
|
@@ -37,18 +39,18 @@ __all__ = [
|
|
37
39
|
"Consumer",
|
38
40
|
"execute",
|
39
41
|
"FixedRetryDelay",
|
40
|
-
"
|
41
|
-
"handle_rabbit_mq_service_exception",
|
42
|
-
"handle_validation_error",
|
42
|
+
"GeneralMqExceptionHandler",
|
43
43
|
"LISTENER_ATTRIBUTE",
|
44
44
|
"ListenerBase",
|
45
45
|
"ListenerContext",
|
46
46
|
"Publisher",
|
47
47
|
"RabbitMqManager",
|
48
|
+
"RabbitMqServiceExceptionHandler",
|
48
49
|
"RetryDelayJitter",
|
49
50
|
"RetryPolicy",
|
50
51
|
"RPC_WORKER_ATTRIBUTE",
|
51
52
|
"rpc_worker",
|
52
53
|
"RpcClient",
|
53
54
|
"RpcWorker",
|
55
|
+
"ValidationErrorHandler",
|
54
56
|
]
|
@@ -5,17 +5,13 @@ from asyncio import (
|
|
5
5
|
gather,
|
6
6
|
iscoroutinefunction,
|
7
7
|
)
|
8
|
-
from dataclasses import dataclass
|
9
8
|
from functools import partial
|
10
|
-
from inspect import Parameter, signature
|
11
9
|
from random import uniform
|
12
10
|
from typing import (
|
13
11
|
Any,
|
14
12
|
Awaitable,
|
15
|
-
Callable,
|
16
|
-
Concatenate,
|
17
|
-
ParamSpec,
|
18
13
|
TypeVar,
|
14
|
+
cast,
|
19
15
|
)
|
20
16
|
|
21
17
|
from pika.adapters.asyncio_connection import AsyncioConnection
|
@@ -25,20 +21,18 @@ from pika.frame import Method
|
|
25
21
|
from prometheus_client import Counter
|
26
22
|
from prometheus_client import Enum as PrometheusEnum
|
27
23
|
from punq import Container, Scope
|
28
|
-
from pydantic import ValidationError
|
29
24
|
|
30
|
-
from ..dependencies.miscellaneous import validate_annotation
|
31
25
|
from ..exceptions import (
|
32
26
|
RabbitMQConnectionUnhealthyError,
|
33
|
-
ServiceException,
|
34
27
|
)
|
35
28
|
from ..logging import LoggerProvider
|
36
|
-
from ..
|
29
|
+
from ..remotelogging import BaseRemoteLogSender
|
37
30
|
from ..utils import AsyncEventLoopMixin
|
38
31
|
from ._exception_handlers import (
|
39
|
-
|
40
|
-
|
41
|
-
|
32
|
+
AbstractRabbitMqExceptionHandler,
|
33
|
+
GeneralMqExceptionHandler,
|
34
|
+
RabbitMqServiceExceptionHandler,
|
35
|
+
ValidationErrorHandler,
|
42
36
|
)
|
43
37
|
from ._listener import (
|
44
38
|
LISTENER_ATTRIBUTE,
|
@@ -56,26 +50,18 @@ __all__ = [
|
|
56
50
|
"RabbitMqManager",
|
57
51
|
]
|
58
52
|
|
59
|
-
|
60
|
-
P = ParamSpec("P")
|
61
|
-
ExceptionHandler = (
|
62
|
-
Callable[Concatenate[ListenerContext, E, P], None]
|
63
|
-
| Callable[Concatenate[ListenerContext, E, P], Awaitable]
|
64
|
-
)
|
53
|
+
|
65
54
|
R = TypeVar("R")
|
66
55
|
|
67
56
|
|
68
57
|
class AbstractRabbitMQService(ABC):
|
69
58
|
def initialize(
|
70
59
|
self, connection: AsyncioConnection, channel_pool: ChannelPool
|
71
|
-
) -> Future:
|
60
|
+
) -> Future[None]:
|
72
61
|
raise NotImplementedError()
|
73
62
|
|
74
|
-
|
75
|
-
|
76
|
-
class ExceptionHandlerContainer:
|
77
|
-
handler: ExceptionHandler
|
78
|
-
dependencies: dict[str, type]
|
63
|
+
def close(self) -> Future[None]:
|
64
|
+
raise NotImplementedError()
|
79
65
|
|
80
66
|
|
81
67
|
class RabbitMqManager(AsyncEventLoopMixin):
|
@@ -97,7 +83,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
97
83
|
|
98
84
|
def __init__(
|
99
85
|
self,
|
100
|
-
|
86
|
+
remote_logger: BaseRemoteLogSender,
|
101
87
|
parameters: Parameters | str | None = None,
|
102
88
|
reconnect_delay: float = 5.0,
|
103
89
|
reconnect_delay_jitter: tuple[float, float] = (1.0, 5.0),
|
@@ -121,87 +107,39 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
121
107
|
self._connection_blocked = False
|
122
108
|
self._services: list[AbstractRabbitMQService] = []
|
123
109
|
self._exception_handlers: dict[
|
124
|
-
type[Exception],
|
110
|
+
type[Exception], AbstractRabbitMqExceptionHandler
|
125
111
|
] = {}
|
126
|
-
|
127
|
-
self.exception_handler(ServiceException)(
|
128
|
-
handle_rabbit_mq_service_exception
|
129
|
-
)
|
130
|
-
self.exception_handler(ValidationError)(handle_validation_error)
|
131
|
-
self.exception_handler(Exception)(handle_general_mq_exception)
|
132
|
-
|
133
112
|
self._channel_pool = ChannelPool()
|
134
|
-
self.
|
113
|
+
self._remote_logger = remote_logger
|
135
114
|
self._logger = LoggerProvider.default().get_logger("rabbitmq")
|
136
115
|
|
137
116
|
@property
|
138
117
|
def container(self) -> Container:
|
139
118
|
return self._container
|
140
119
|
|
141
|
-
def
|
142
|
-
self,
|
143
|
-
) ->
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
)
|
148
|
-
|
149
|
-
def wrapper(
|
150
|
-
handler: ExceptionHandler,
|
151
|
-
) -> ExceptionHandler:
|
152
|
-
if not callable(handler):
|
153
|
-
raise TypeError(f"handler not a callable, got {type(handler)}")
|
154
|
-
|
155
|
-
dependencies = {}
|
156
|
-
|
157
|
-
for parameter_position, (parameter_name, parameter) in enumerate(
|
158
|
-
signature(handler).parameters.items()
|
120
|
+
def set_exception_handlers(
|
121
|
+
self, *exception_handlers: type[AbstractRabbitMqExceptionHandler]
|
122
|
+
) -> None:
|
123
|
+
for index, exception_handler in enumerate(exception_handlers):
|
124
|
+
if not isinstance(exception_handler, type) or not issubclass(
|
125
|
+
exception_handler, AbstractRabbitMqExceptionHandler
|
159
126
|
):
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
position=parameter_position,
|
164
|
-
annotation=parameter.annotation,
|
165
|
-
)
|
166
|
-
)
|
167
|
-
|
168
|
-
if not is_valid:
|
169
|
-
raise TypeError(
|
170
|
-
f"parameter `{parameter_name}` at `{parameter_position + 1}` is not annotated with type or subclass of `{expected_type}`, got {parameter.annotation}"
|
171
|
-
)
|
172
|
-
|
173
|
-
continue
|
174
|
-
|
175
|
-
dependency = validate_annotation(parameter)
|
176
|
-
|
177
|
-
if dependency is None:
|
178
|
-
raise ValueError(
|
179
|
-
f"handlers cannot contain parameters other than `Annotated[type, DependsOn(type)]`, got `{parameter_name}: {dependency}`"
|
180
|
-
)
|
181
|
-
|
182
|
-
dependencies[parameter_name] = dependency
|
127
|
+
raise TypeError(
|
128
|
+
f"exception handler {index} is {type(exception_handler)}, expected instance of type or subclass of `AbstractRabbitMqExceptionHandler`"
|
129
|
+
)
|
183
130
|
|
184
|
-
self.
|
185
|
-
|
131
|
+
self._container.register(
|
132
|
+
service=AbstractRabbitMqExceptionHandler,
|
133
|
+
factory=exception_handler,
|
134
|
+
scope=Scope.singleton,
|
186
135
|
)
|
187
136
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
) -> tuple[bool, type | None]:
|
195
|
-
if annotation is Parameter.empty:
|
196
|
-
return True, None
|
197
|
-
|
198
|
-
if position == 0 and annotation is not ListenerContext:
|
199
|
-
return False, ListenerContext
|
200
|
-
|
201
|
-
if position == 1 and not issubclass(annotation, Exception):
|
202
|
-
return False, Exception
|
203
|
-
|
204
|
-
return True, None
|
137
|
+
def init_default_exception_handlers(self) -> None:
|
138
|
+
self.set_exception_handlers(
|
139
|
+
RabbitMqServiceExceptionHandler,
|
140
|
+
ValidationErrorHandler,
|
141
|
+
GeneralMqExceptionHandler,
|
142
|
+
)
|
205
143
|
|
206
144
|
def include_listener(self, listener: Listener | type[ListenerBase]) -> None:
|
207
145
|
if isinstance(listener, Listener):
|
@@ -259,10 +197,11 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
259
197
|
scope=Scope.singleton,
|
260
198
|
)
|
261
199
|
|
262
|
-
def connect(self) -> Future:
|
200
|
+
def connect(self) -> Future[None]:
|
263
201
|
if not self._connected:
|
264
202
|
self._resolve_listener_classes()
|
265
203
|
self._resolve_service_classes()
|
204
|
+
self._resolve_exception_handlers()
|
266
205
|
|
267
206
|
if self._is_connection_healthy():
|
268
207
|
raise RuntimeError("rabbitmq already connected and healthy")
|
@@ -289,6 +228,23 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
289
228
|
self._container.resolve_all(AbstractRabbitMQService)
|
290
229
|
)
|
291
230
|
|
231
|
+
def _resolve_exception_handlers(self) -> None:
|
232
|
+
for exception_handler in self._container.resolve_all(
|
233
|
+
AbstractRabbitMqExceptionHandler
|
234
|
+
):
|
235
|
+
exception_handler = cast(
|
236
|
+
AbstractRabbitMqExceptionHandler, exception_handler
|
237
|
+
)
|
238
|
+
|
239
|
+
if not callable(exception_handler):
|
240
|
+
raise ValueError(
|
241
|
+
f"exception handler {exception_handler.__class__.__name__} is not callable"
|
242
|
+
)
|
243
|
+
|
244
|
+
self._exception_handlers[exception_handler.exception] = (
|
245
|
+
exception_handler
|
246
|
+
)
|
247
|
+
|
292
248
|
@property
|
293
249
|
def connection(self) -> AsyncioConnection:
|
294
250
|
if not self._is_connection_healthy():
|
@@ -357,7 +313,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
357
313
|
and not self._connection.is_closed
|
358
314
|
)
|
359
315
|
|
360
|
-
def disconnect(self) -> None:
|
316
|
+
async def disconnect(self) -> None:
|
361
317
|
if self._disconnected:
|
362
318
|
raise RuntimeError("already disconnected from rabbitmq")
|
363
319
|
|
@@ -366,6 +322,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
366
322
|
if self._connection is None:
|
367
323
|
raise RabbitMQConnectionUnhealthyError("connection not ready yet")
|
368
324
|
|
325
|
+
await self._wait_for_listeners_and_services()
|
326
|
+
|
369
327
|
if self._connection.is_closing or self._connection.is_closed:
|
370
328
|
self._logger.info("already disconnected from rabbitmq")
|
371
329
|
else:
|
@@ -374,6 +332,16 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
374
332
|
|
375
333
|
self.RABBITMQ_CONNECTION_STATE.state("disconnected")
|
376
334
|
|
335
|
+
async def _wait_for_listeners_and_services(self) -> None:
|
336
|
+
_ = await gather(
|
337
|
+
*(listener.cancel() for listener in self._listeners),
|
338
|
+
return_exceptions=True,
|
339
|
+
)
|
340
|
+
_ = await gather(
|
341
|
+
*(service.close() for service in self._services),
|
342
|
+
return_exceptions=True,
|
343
|
+
)
|
344
|
+
|
377
345
|
def _on_connection_opened(self, connection: AsyncioConnection) -> None:
|
378
346
|
self._connection = connection
|
379
347
|
self._connection_blocked = False
|
@@ -396,7 +364,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
396
364
|
self._channel_pool.fill(self._connection)
|
397
365
|
).add_done_callback(self._channel_pool_filled)
|
398
366
|
|
399
|
-
def _channel_pool_drained(self, task: Task) -> None:
|
367
|
+
def _channel_pool_drained(self, task: Task[None]) -> None:
|
400
368
|
if task.cancelled():
|
401
369
|
if not self._connected and not self._connected_future.done():
|
402
370
|
_ = self._connected_future.cancel(None)
|
@@ -407,7 +375,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
407
375
|
|
408
376
|
if exception is not None:
|
409
377
|
if self._can_reconnect(exception):
|
410
|
-
self.
|
378
|
+
self._remote_logger.error(
|
411
379
|
message="couldn't drain the channel pool",
|
412
380
|
exception=exception,
|
413
381
|
)
|
@@ -429,7 +397,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
429
397
|
|
430
398
|
self._connection_blocked = True
|
431
399
|
|
432
|
-
self.
|
400
|
+
self._remote_logger.warning(
|
433
401
|
"connection is blocked by broker, will not accept published messages"
|
434
402
|
)
|
435
403
|
self.RABBITMQ_PUBLISHER_BLOCKED_STATE.state("blocked")
|
@@ -441,13 +409,13 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
441
409
|
|
442
410
|
self._connection_blocked = False
|
443
411
|
|
444
|
-
self.
|
412
|
+
self._remote_logger.info("broker resumed accepting published messages")
|
445
413
|
self.RABBITMQ_PUBLISHER_BLOCKED_STATE.state("unblocked")
|
446
414
|
|
447
415
|
def _is_connection_blocked(self) -> bool:
|
448
416
|
return self._connection_blocked
|
449
417
|
|
450
|
-
def _channel_pool_filled(self, task: Task) -> None:
|
418
|
+
def _channel_pool_filled(self, task: Task[None]) -> None:
|
451
419
|
if task.cancelled():
|
452
420
|
if not self._connected and not self._connected_future.done():
|
453
421
|
_ = self._connected_future.cancel(None)
|
@@ -460,7 +428,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
460
428
|
if not self._connected and not self._connected_future.done():
|
461
429
|
self._connected_future.set_exception(exception)
|
462
430
|
elif self._can_reconnect(exception):
|
463
|
-
self.
|
431
|
+
self._remote_logger.error(
|
464
432
|
message="couldn't fill the channel pool",
|
465
433
|
exception=exception,
|
466
434
|
)
|
@@ -482,7 +450,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
482
450
|
*self._configure_listeners(), *self._initialize_services()
|
483
451
|
).add_done_callback(self._listener_and_service_config_and_init_done)
|
484
452
|
|
485
|
-
def _configure_listeners(self) -> list[Awaitable]:
|
453
|
+
def _configure_listeners(self) -> list[Awaitable[Any]]:
|
486
454
|
try:
|
487
455
|
assert self._connection is not None
|
488
456
|
|
@@ -492,7 +460,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
492
460
|
channel_pool=self._channel_pool,
|
493
461
|
on_exception_callback=self._invoke_exception_handler,
|
494
462
|
container=self._container,
|
495
|
-
|
463
|
+
remote_logger=self._remote_logger,
|
496
464
|
global_retry_policy=self._listener_global_retry_policy,
|
497
465
|
)
|
498
466
|
for listener in self._listeners
|
@@ -504,7 +472,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
504
472
|
|
505
473
|
return [listener_configuration_error_future]
|
506
474
|
|
507
|
-
def _initialize_services(self) -> list[Future]:
|
475
|
+
def _initialize_services(self) -> list[Future[Any]]:
|
508
476
|
assert self._connection is not None
|
509
477
|
|
510
478
|
try:
|
@@ -520,7 +488,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
520
488
|
return [service_initialization_error_future]
|
521
489
|
|
522
490
|
def _listener_and_service_config_and_init_done(
|
523
|
-
self, future: Future
|
491
|
+
self, future: Future[list[Any]]
|
524
492
|
) -> None:
|
525
493
|
if future.cancelled():
|
526
494
|
if not self._connected and not self._connected_future.done():
|
@@ -534,7 +502,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
534
502
|
if not self._connected and not self._connected_future.done():
|
535
503
|
self._connected_future.set_exception(exception)
|
536
504
|
elif self._can_reconnect(exception):
|
537
|
-
self.
|
505
|
+
self._remote_logger.error(
|
538
506
|
message="couldn't configure and initialize all listeners and services",
|
539
507
|
exception=exception,
|
540
508
|
)
|
@@ -579,7 +547,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
579
547
|
return
|
580
548
|
|
581
549
|
if self._connected and not self._disconnected:
|
582
|
-
self.
|
550
|
+
self._remote_logger.error(
|
583
551
|
message="error while opening connection to rabbitmq",
|
584
552
|
exception=exception,
|
585
553
|
)
|
@@ -599,29 +567,18 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
599
567
|
if exception_handler is None:
|
600
568
|
return False
|
601
569
|
|
602
|
-
|
603
|
-
|
604
|
-
for (
|
605
|
-
parameter_name,
|
606
|
-
dependency,
|
607
|
-
) in exception_handler.dependencies.items():
|
608
|
-
dependencies[parameter_name] = self._container.resolve(dependency)
|
570
|
+
assert callable(exception_handler)
|
609
571
|
|
610
|
-
if
|
572
|
+
if self._is_async_exception_handler(exception_handler):
|
611
573
|
self.loop.create_task(
|
612
|
-
exception_handler
|
574
|
+
exception_handler(context, exception)
|
613
575
|
).add_done_callback(
|
614
576
|
partial(self._on_exception_handler_done, context)
|
615
577
|
)
|
616
578
|
else:
|
617
579
|
self.loop.run_in_executor(
|
618
580
|
executor=None,
|
619
|
-
func=partial(
|
620
|
-
exception_handler.handler,
|
621
|
-
context,
|
622
|
-
exception,
|
623
|
-
**dependencies,
|
624
|
-
),
|
581
|
+
func=partial(exception_handler, context, exception),
|
625
582
|
).add_done_callback(
|
626
583
|
partial(self._on_exception_handler_done, context)
|
627
584
|
)
|
@@ -634,6 +591,18 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
634
591
|
|
635
592
|
return True
|
636
593
|
|
594
|
+
def _is_async_exception_handler(
|
595
|
+
self, exception_handler: AbstractRabbitMqExceptionHandler
|
596
|
+
) -> bool:
|
597
|
+
exception_handler_callable = getattr(
|
598
|
+
exception_handler, "__call__", None
|
599
|
+
)
|
600
|
+
|
601
|
+
if exception_handler_callable is None:
|
602
|
+
raise RuntimeError("exception handler has not `__call__` method")
|
603
|
+
|
604
|
+
return iscoroutinefunction(exception_handler_callable)
|
605
|
+
|
637
606
|
def _on_exception_handler_done(
|
638
607
|
self, context: ListenerContext, task_or_future: Task[Any] | Future[Any]
|
639
608
|
) -> None:
|
@@ -643,7 +612,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
643
612
|
exception = task_or_future.exception()
|
644
613
|
|
645
614
|
if exception is not None:
|
646
|
-
self.
|
615
|
+
self._remote_logger.error(
|
647
616
|
message="error occured in listener exception handler",
|
648
617
|
exception=exception,
|
649
618
|
)
|
@@ -661,7 +630,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
661
630
|
return
|
662
631
|
|
663
632
|
if self._can_reconnect(exception):
|
664
|
-
self.
|
633
|
+
self._remote_logger.error(
|
665
634
|
message="connection to rabbitmq closed unexpectedly, attempting to reconnect",
|
666
635
|
exception=exception,
|
667
636
|
)
|
@@ -683,7 +652,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
683
652
|
if not self._connected_future.done():
|
684
653
|
self._connected_future.set_result(None)
|
685
654
|
|
686
|
-
self.
|
655
|
+
self._remote_logger.exception(
|
687
656
|
"couldn't reconnect to rabbitmq, attempting to reconnect"
|
688
657
|
)
|
689
658
|
self._reconnect()
|
@@ -705,7 +674,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
|
|
705
674
|
return
|
706
675
|
|
707
676
|
if self._can_reconnect(exception):
|
708
|
-
self.
|
677
|
+
self._remote_logger.error(
|
709
678
|
message="couldn't reconnect to rabbitmq, attempting to reconnect",
|
710
679
|
exception=exception,
|
711
680
|
)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from asyncio import Future
|
2
2
|
from inspect import Traceback
|
3
3
|
from random import uniform
|
4
|
+
from typing import cast
|
4
5
|
from uuid import UUID, uuid4
|
5
6
|
|
6
7
|
from pika.adapters.asyncio_connection import AsyncioConnection
|
@@ -86,7 +87,9 @@ class BaseChannel(AsyncEventLoopMixin):
|
|
86
87
|
if self._channel is None:
|
87
88
|
raise RuntimeError("underlying channel not set")
|
88
89
|
|
89
|
-
return self._channel.is_closing or
|
90
|
+
return cast(bool, self._channel.is_closing) or cast(
|
91
|
+
bool, self._channel.is_closed
|
92
|
+
)
|
90
93
|
|
91
94
|
def _on_cancelled(self, method: Basic.Cancel) -> None:
|
92
95
|
del method
|