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.
@@ -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,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 ..logstash import BaseLogstashSender
29
+ from ..remotelogging import BaseRemoteLogSender
37
30
  from ..utils import AsyncEventLoopMixin
38
31
  from ._exception_handlers import (
39
- handle_general_mq_exception,
40
- handle_rabbit_mq_service_exception,
41
- handle_validation_error,
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
- E = TypeVar("E")
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
- @dataclass
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
- logstash: BaseLogstashSender,
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], ExceptionHandlerContainer
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._logstash = logstash
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 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()
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
- 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
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._exception_handlers[exception] = ExceptionHandlerContainer(
185
- handler=handler, dependencies=dependencies
131
+ self._container.register(
132
+ service=AbstractRabbitMqExceptionHandler,
133
+ factory=exception_handler,
134
+ scope=Scope.singleton,
186
135
  )
187
136
 
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
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._logstash.error(
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._logstash.warning(
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._logstash.info("broker resumed accepting published messages")
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._logstash.error(
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
- logstash=self._logstash,
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._logstash.error(
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._logstash.error(
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
- dependencies = {}
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 iscoroutinefunction(exception_handler.handler):
572
+ if self._is_async_exception_handler(exception_handler):
611
573
  self.loop.create_task(
612
- exception_handler.handler(context, exception, **dependencies)
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._logstash.error(
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._logstash.error(
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._logstash.exception(
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._logstash.error(
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 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