|
1 | 1 | import asyncio |
| 2 | +import uuid |
2 | 3 | import collections |
3 | 4 | import logging |
4 | 5 | import types |
@@ -141,6 +142,7 @@ def __init__(self, ignore_unknown_events: bool = True): |
141 | 142 | self._processing_task: Optional[asyncio.Task[Any]] = None |
142 | 143 | self._shutdown = False |
143 | 144 | self._silent_events: set[type] = set() |
| 145 | + self._handler_tasks: Dict[uuid.UUID, asyncio.Task[Any]] = {} |
144 | 146 |
|
145 | 147 | self.register(ExceptionEvent) |
146 | 148 | self.register(HealthCheckEvent) |
@@ -182,6 +184,8 @@ def register(self, event_class, ignore_not_compatible=False): |
182 | 184 | # raise KeyError(f"{event_class.type} is already registered.") |
183 | 185 | self._events[event_class.type] = event_class |
184 | 186 | logger.info(f"Registered new event {event_class} - {event_class.type}") |
| 187 | + elif event_class.__name__.endswith('BaseEvent'): |
| 188 | + return |
185 | 189 | elif not ignore_not_compatible: |
186 | 190 | raise ValueError(f"Provide valid class that ends on '*Event' and 'type' attribute: {event_class}") |
187 | 191 | else: |
@@ -458,6 +462,9 @@ async def wait(self, timeout: float = 10.0): |
458 | 462 | start_time = asyncio.get_event_loop().time() |
459 | 463 | while self._queue and (asyncio.get_event_loop().time() - start_time) < timeout: |
460 | 464 | await asyncio.sleep(0.01) |
| 465 | + |
| 466 | + if self._handler_tasks: |
| 467 | + await asyncio.wait(list(self._handler_tasks.values())) |
461 | 468 |
|
462 | 469 | def _start_processing_task(self): |
463 | 470 | """Start the background event processing task.""" |
@@ -488,18 +495,27 @@ async def _process_events_loop(self): |
488 | 495 | elif cancelled_exc: |
489 | 496 | raise cancelled_exc |
490 | 497 | else: |
| 498 | + cleanup_ids = set(task_id for task_id, task in self._handler_tasks.items() if task.done()) |
| 499 | + for task_id in cleanup_ids: |
| 500 | + self._handler_tasks.pop(task_id) |
491 | 501 | await asyncio.sleep(0.0001) |
492 | 502 |
|
| 503 | + async def _run_handler(self, handler, event): |
| 504 | + try: |
| 505 | + return await handler(event) |
| 506 | + except Exception as exc: |
| 507 | + self._queue.appendleft(ExceptionEvent(exc, handler)) # type: ignore[arg-type] |
| 508 | + module_name = getattr(handler, '__module__', 'unknown') |
| 509 | + logger.exception(f"Error calling handler {handler.__name__} from {module_name} for event {event.type}") |
| 510 | + |
493 | 511 | async def _process_single_event(self, event): |
494 | 512 | """Process a single event.""" |
495 | 513 | for handler in self._handlers.get(event.type, []): |
496 | | - try: |
497 | | - module_name = getattr(handler, '__module__', 'unknown') |
498 | | - if event.type not in self._silent_events: |
499 | | - logger.info(f"Called handler {handler.__name__} from {module_name} for event {event.type}") |
500 | | - await handler(event) |
501 | | - except Exception as exc: |
502 | | - self._queue.appendleft(ExceptionEvent(exc, handler)) # type: ignore[arg-type] |
503 | | - module_name = getattr(handler, '__module__', 'unknown') |
504 | | - logger.exception(f"Error calling handler {handler.__name__} from {module_name} for event {event.type}") |
| 514 | + module_name = getattr(handler, '__module__', 'unknown') |
| 515 | + if event.type not in self._silent_events: |
| 516 | + logger.info(f"Called handler {handler.__name__} from {module_name} for event {event.type}") |
| 517 | + |
| 518 | + loop = asyncio.get_running_loop() |
| 519 | + handler_task = loop.create_task(self._run_handler(handler, event)) |
| 520 | + self._handler_tasks[uuid.uuid4()] = handler_task |
505 | 521 |
|
0 commit comments