qena-shared-lib 0.1.12__py3-none-any.whl → 0.1.13__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.
@@ -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
- logstash_logging: bool | None = None,
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 logstash_logging is not None:
92
- self._logstash_logging = logstash_logging
91
+ if remote_logging is not None:
92
+ self._remote_logging = remote_logging
93
93
  else:
94
- self._logstash_logging = True
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 logstash_logging(self) -> bool | None:
121
- return self._logstash_logging
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
- logstash_logging: bool = True,
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
- logstash_logging=logstash_logging,
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
- logstash_logging: bool | None = None,
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
- logstash_logging=logstash_logging,
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
- handle_general_mq_exception,
5
- handle_rabbit_mq_service_exception,
6
- handle_validation_error,
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
- "handle_general_mq_exception",
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,17 @@ 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
13
  Callable,
16
14
  Concatenate,
15
+ Generic,
17
16
  ParamSpec,
18
17
  TypeVar,
18
+ cast,
19
19
  )
20
20
 
21
21
  from pika.adapters.asyncio_connection import AsyncioConnection
@@ -25,20 +25,18 @@ from pika.frame import Method
25
25
  from prometheus_client import Counter
26
26
  from prometheus_client import Enum as PrometheusEnum
27
27
  from punq import Container, Scope
28
- from pydantic import ValidationError
29
28
 
30
- from ..dependencies.miscellaneous import validate_annotation
31
29
  from ..exceptions import (
32
30
  RabbitMQConnectionUnhealthyError,
33
- ServiceException,
34
31
  )
35
32
  from ..logging import LoggerProvider
36
- from ..logstash import BaseLogstashSender
33
+ from ..remotelogging import BaseRemoteLogSender
37
34
  from ..utils import AsyncEventLoopMixin
38
35
  from ._exception_handlers import (
39
- handle_general_mq_exception,
40
- handle_rabbit_mq_service_exception,
41
- handle_validation_error,
36
+ AbstractRabbitMqExceptionHandler,
37
+ GeneralMqExceptionHandler,
38
+ RabbitMqServiceExceptionHandler,
39
+ ValidationErrorHandler,
42
40
  )
43
41
  from ._listener import (
44
42
  LISTENER_ATTRIBUTE,
@@ -56,29 +54,28 @@ __all__ = [
56
54
  "RabbitMqManager",
57
55
  ]
58
56
 
59
- E = TypeVar("E")
57
+
58
+ E = TypeVar("E", bound=BaseException)
60
59
  P = ParamSpec("P")
61
- ExceptionHandler = (
62
- Callable[Concatenate[ListenerContext, E, P], None]
63
- | Callable[Concatenate[ListenerContext, E, P], Awaitable]
64
- )
60
+ SyncExceptionHandler = Callable[Concatenate[ListenerContext, E, P], None]
61
+ AsyncExcpetionHandler = Callable[
62
+ Concatenate[ListenerContext, E, P], Awaitable[None]
63
+ ]
64
+ ExceptionHandler = SyncExceptionHandler[E, P] | AsyncExcpetionHandler[E, P]
65
65
  R = TypeVar("R")
66
66
 
67
67
 
68
68
  class AbstractRabbitMQService(ABC):
69
69
  def initialize(
70
70
  self, connection: AsyncioConnection, channel_pool: ChannelPool
71
- ) -> Future:
71
+ ) -> Future[None]:
72
72
  raise NotImplementedError()
73
73
 
74
-
75
- @dataclass
76
- class ExceptionHandlerContainer:
77
- handler: ExceptionHandler
78
- dependencies: dict[str, type]
74
+ def close(self) -> Future[None]:
75
+ raise NotImplementedError()
79
76
 
80
77
 
81
- class RabbitMqManager(AsyncEventLoopMixin):
78
+ class RabbitMqManager(Generic[E, P], AsyncEventLoopMixin):
82
79
  RABBITMQ_CONNECTION_STATE = PrometheusEnum(
83
80
  name="rabbitmq_connection_state",
84
81
  documentation="Babbitmq connection state",
@@ -97,7 +94,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
97
94
 
98
95
  def __init__(
99
96
  self,
100
- logstash: BaseLogstashSender,
97
+ remote_logger: BaseRemoteLogSender,
101
98
  parameters: Parameters | str | None = None,
102
99
  reconnect_delay: float = 5.0,
103
100
  reconnect_delay_jitter: tuple[float, float] = (1.0, 5.0),
@@ -121,87 +118,39 @@ class RabbitMqManager(AsyncEventLoopMixin):
121
118
  self._connection_blocked = False
122
119
  self._services: list[AbstractRabbitMQService] = []
123
120
  self._exception_handlers: dict[
124
- type[Exception], ExceptionHandlerContainer
121
+ type[Exception], AbstractRabbitMqExceptionHandler
125
122
  ] = {}
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
123
  self._channel_pool = ChannelPool()
134
- self._logstash = logstash
124
+ self._remote_logger = remote_logger
135
125
  self._logger = LoggerProvider.default().get_logger("rabbitmq")
136
126
 
137
127
  @property
138
128
  def container(self) -> Container:
139
129
  return self._container
140
130
 
141
- def exception_handler(
142
- self, exception: type[Exception]
143
- ) -> Callable[[ExceptionHandler], ExceptionHandler]:
144
- if not issubclass(exception, Exception):
145
- raise TypeError(
146
- f"exception should be of type `Exception`, got {exception}"
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()
131
+ def set_exception_handlers(
132
+ self, *exception_handlers: type[AbstractRabbitMqExceptionHandler]
133
+ ) -> None:
134
+ for index, exception_handler in enumerate(exception_handlers):
135
+ if not isinstance(exception_handler, type) or not issubclass(
136
+ exception_handler, AbstractRabbitMqExceptionHandler
159
137
  ):
160
- if 0 <= parameter_position < 2:
161
- is_valid, expected_type = (
162
- self._is_valid_exception_handler_parameter(
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
138
+ raise TypeError(
139
+ f"exception handler {index} is {type(exception_handler)}, expected instance of type or subclass of `AbstractRabbitMqExceptionHandler`"
140
+ )
183
141
 
184
- self._exception_handlers[exception] = ExceptionHandlerContainer(
185
- handler=handler, dependencies=dependencies
142
+ self._container.register(
143
+ service=AbstractRabbitMqExceptionHandler,
144
+ factory=exception_handler,
145
+ scope=Scope.singleton,
186
146
  )
187
147
 
188
- return handler
189
-
190
- return wrapper
191
-
192
- def _is_valid_exception_handler_parameter(
193
- self, position: int, annotation: type
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
148
+ def init_default_exception_handlers(self) -> None:
149
+ self.set_exception_handlers(
150
+ RabbitMqServiceExceptionHandler,
151
+ ValidationErrorHandler,
152
+ GeneralMqExceptionHandler,
153
+ )
205
154
 
206
155
  def include_listener(self, listener: Listener | type[ListenerBase]) -> None:
207
156
  if isinstance(listener, Listener):
@@ -259,10 +208,11 @@ class RabbitMqManager(AsyncEventLoopMixin):
259
208
  scope=Scope.singleton,
260
209
  )
261
210
 
262
- def connect(self) -> Future:
211
+ def connect(self) -> Future[None]:
263
212
  if not self._connected:
264
213
  self._resolve_listener_classes()
265
214
  self._resolve_service_classes()
215
+ self._resolve_exception_handlers()
266
216
 
267
217
  if self._is_connection_healthy():
268
218
  raise RuntimeError("rabbitmq already connected and healthy")
@@ -289,6 +239,23 @@ class RabbitMqManager(AsyncEventLoopMixin):
289
239
  self._container.resolve_all(AbstractRabbitMQService)
290
240
  )
291
241
 
242
+ def _resolve_exception_handlers(self) -> None:
243
+ for exception_handler in self._container.resolve_all(
244
+ AbstractRabbitMqExceptionHandler
245
+ ):
246
+ exception_handler = cast(
247
+ AbstractRabbitMqExceptionHandler, exception_handler
248
+ )
249
+
250
+ if not callable(exception_handler):
251
+ raise ValueError(
252
+ f"exception handler {exception_handler.__class__.__name__} is not callable"
253
+ )
254
+
255
+ self._exception_handlers[exception_handler.exception] = (
256
+ exception_handler
257
+ )
258
+
292
259
  @property
293
260
  def connection(self) -> AsyncioConnection:
294
261
  if not self._is_connection_healthy():
@@ -357,7 +324,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
357
324
  and not self._connection.is_closed
358
325
  )
359
326
 
360
- def disconnect(self) -> None:
327
+ async def disconnect(self) -> None:
361
328
  if self._disconnected:
362
329
  raise RuntimeError("already disconnected from rabbitmq")
363
330
 
@@ -366,6 +333,8 @@ class RabbitMqManager(AsyncEventLoopMixin):
366
333
  if self._connection is None:
367
334
  raise RabbitMQConnectionUnhealthyError("connection not ready yet")
368
335
 
336
+ await self._wait_for_listeners_and_services()
337
+
369
338
  if self._connection.is_closing or self._connection.is_closed:
370
339
  self._logger.info("already disconnected from rabbitmq")
371
340
  else:
@@ -374,6 +343,16 @@ class RabbitMqManager(AsyncEventLoopMixin):
374
343
 
375
344
  self.RABBITMQ_CONNECTION_STATE.state("disconnected")
376
345
 
346
+ async def _wait_for_listeners_and_services(self) -> None:
347
+ _ = await gather(
348
+ *(listener.cancel() for listener in self._listeners),
349
+ return_exceptions=True,
350
+ )
351
+ _ = await gather(
352
+ *(service.close() for service in self._services),
353
+ return_exceptions=True,
354
+ )
355
+
377
356
  def _on_connection_opened(self, connection: AsyncioConnection) -> None:
378
357
  self._connection = connection
379
358
  self._connection_blocked = False
@@ -396,7 +375,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
396
375
  self._channel_pool.fill(self._connection)
397
376
  ).add_done_callback(self._channel_pool_filled)
398
377
 
399
- def _channel_pool_drained(self, task: Task) -> None:
378
+ def _channel_pool_drained(self, task: Task[None]) -> None:
400
379
  if task.cancelled():
401
380
  if not self._connected and not self._connected_future.done():
402
381
  _ = self._connected_future.cancel(None)
@@ -407,7 +386,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
407
386
 
408
387
  if exception is not None:
409
388
  if self._can_reconnect(exception):
410
- self._logstash.error(
389
+ self._remote_logger.error(
411
390
  message="couldn't drain the channel pool",
412
391
  exception=exception,
413
392
  )
@@ -429,7 +408,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
429
408
 
430
409
  self._connection_blocked = True
431
410
 
432
- self._logstash.warning(
411
+ self._remote_logger.warning(
433
412
  "connection is blocked by broker, will not accept published messages"
434
413
  )
435
414
  self.RABBITMQ_PUBLISHER_BLOCKED_STATE.state("blocked")
@@ -441,13 +420,13 @@ class RabbitMqManager(AsyncEventLoopMixin):
441
420
 
442
421
  self._connection_blocked = False
443
422
 
444
- self._logstash.info("broker resumed accepting published messages")
423
+ self._remote_logger.info("broker resumed accepting published messages")
445
424
  self.RABBITMQ_PUBLISHER_BLOCKED_STATE.state("unblocked")
446
425
 
447
426
  def _is_connection_blocked(self) -> bool:
448
427
  return self._connection_blocked
449
428
 
450
- def _channel_pool_filled(self, task: Task) -> None:
429
+ def _channel_pool_filled(self, task: Task[None]) -> None:
451
430
  if task.cancelled():
452
431
  if not self._connected and not self._connected_future.done():
453
432
  _ = self._connected_future.cancel(None)
@@ -460,7 +439,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
460
439
  if not self._connected and not self._connected_future.done():
461
440
  self._connected_future.set_exception(exception)
462
441
  elif self._can_reconnect(exception):
463
- self._logstash.error(
442
+ self._remote_logger.error(
464
443
  message="couldn't fill the channel pool",
465
444
  exception=exception,
466
445
  )
@@ -482,7 +461,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
482
461
  *self._configure_listeners(), *self._initialize_services()
483
462
  ).add_done_callback(self._listener_and_service_config_and_init_done)
484
463
 
485
- def _configure_listeners(self) -> list[Awaitable]:
464
+ def _configure_listeners(self) -> list[Awaitable[Any]]:
486
465
  try:
487
466
  assert self._connection is not None
488
467
 
@@ -492,7 +471,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
492
471
  channel_pool=self._channel_pool,
493
472
  on_exception_callback=self._invoke_exception_handler,
494
473
  container=self._container,
495
- logstash=self._logstash,
474
+ remote_logger=self._remote_logger,
496
475
  global_retry_policy=self._listener_global_retry_policy,
497
476
  )
498
477
  for listener in self._listeners
@@ -504,7 +483,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
504
483
 
505
484
  return [listener_configuration_error_future]
506
485
 
507
- def _initialize_services(self) -> list[Future]:
486
+ def _initialize_services(self) -> list[Future[Any]]:
508
487
  assert self._connection is not None
509
488
 
510
489
  try:
@@ -520,7 +499,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
520
499
  return [service_initialization_error_future]
521
500
 
522
501
  def _listener_and_service_config_and_init_done(
523
- self, future: Future
502
+ self, future: Future[list[Any]]
524
503
  ) -> None:
525
504
  if future.cancelled():
526
505
  if not self._connected and not self._connected_future.done():
@@ -534,7 +513,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
534
513
  if not self._connected and not self._connected_future.done():
535
514
  self._connected_future.set_exception(exception)
536
515
  elif self._can_reconnect(exception):
537
- self._logstash.error(
516
+ self._remote_logger.error(
538
517
  message="couldn't configure and initialize all listeners and services",
539
518
  exception=exception,
540
519
  )
@@ -579,7 +558,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
579
558
  return
580
559
 
581
560
  if self._connected and not self._disconnected:
582
- self._logstash.error(
561
+ self._remote_logger.error(
583
562
  message="error while opening connection to rabbitmq",
584
563
  exception=exception,
585
564
  )
@@ -599,29 +578,18 @@ class RabbitMqManager(AsyncEventLoopMixin):
599
578
  if exception_handler is None:
600
579
  return False
601
580
 
602
- dependencies = {}
603
-
604
- for (
605
- parameter_name,
606
- dependency,
607
- ) in exception_handler.dependencies.items():
608
- dependencies[parameter_name] = self._container.resolve(dependency)
581
+ assert callable(exception_handler)
609
582
 
610
- if iscoroutinefunction(exception_handler.handler):
583
+ if self._is_async_exception_handler(exception_handler):
611
584
  self.loop.create_task(
612
- exception_handler.handler(context, exception, **dependencies)
585
+ exception_handler(context, exception)
613
586
  ).add_done_callback(
614
587
  partial(self._on_exception_handler_done, context)
615
588
  )
616
589
  else:
617
590
  self.loop.run_in_executor(
618
591
  executor=None,
619
- func=partial(
620
- exception_handler.handler,
621
- context,
622
- exception,
623
- **dependencies,
624
- ),
592
+ func=partial(exception_handler, context, exception),
625
593
  ).add_done_callback(
626
594
  partial(self._on_exception_handler_done, context)
627
595
  )
@@ -634,6 +602,18 @@ class RabbitMqManager(AsyncEventLoopMixin):
634
602
 
635
603
  return True
636
604
 
605
+ def _is_async_exception_handler(
606
+ self, exception_handler: AbstractRabbitMqExceptionHandler
607
+ ) -> bool:
608
+ exception_handler_callable = getattr(
609
+ exception_handler, "__call__", None
610
+ )
611
+
612
+ if exception_handler_callable is None:
613
+ raise RuntimeError("exception handler has not `__call__` method")
614
+
615
+ return iscoroutinefunction(exception_handler_callable)
616
+
637
617
  def _on_exception_handler_done(
638
618
  self, context: ListenerContext, task_or_future: Task[Any] | Future[Any]
639
619
  ) -> None:
@@ -643,7 +623,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
643
623
  exception = task_or_future.exception()
644
624
 
645
625
  if exception is not None:
646
- self._logstash.error(
626
+ self._remote_logger.error(
647
627
  message="error occured in listener exception handler",
648
628
  exception=exception,
649
629
  )
@@ -661,7 +641,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
661
641
  return
662
642
 
663
643
  if self._can_reconnect(exception):
664
- self._logstash.error(
644
+ self._remote_logger.error(
665
645
  message="connection to rabbitmq closed unexpectedly, attempting to reconnect",
666
646
  exception=exception,
667
647
  )
@@ -683,7 +663,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
683
663
  if not self._connected_future.done():
684
664
  self._connected_future.set_result(None)
685
665
 
686
- self._logstash.exception(
666
+ self._remote_logger.exception(
687
667
  "couldn't reconnect to rabbitmq, attempting to reconnect"
688
668
  )
689
669
  self._reconnect()
@@ -705,7 +685,7 @@ class RabbitMqManager(AsyncEventLoopMixin):
705
685
  return
706
686
 
707
687
  if self._can_reconnect(exception):
708
- self._logstash.error(
688
+ self._remote_logger.error(
709
689
  message="couldn't reconnect to rabbitmq, attempting to reconnect",
710
690
  exception=exception,
711
691
  )
@@ -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 self._channel.is_closed
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