()
| 59 | pass |
| 60 | |
| 61 | async def _runner() -> None: |
| 62 | loop = asyncio.get_running_loop() |
| 63 | runner_task = asyncio.current_task() |
| 64 | if runner_task is None: |
| 65 | raise RuntimeError("Runner task not found") |
| 66 | |
| 67 | shutdown_requested = False |
| 68 | |
| 69 | def _on_signal(signum: int) -> None: |
| 70 | nonlocal shutdown_requested |
| 71 | |
| 72 | if shutdown_requested: |
| 73 | print("[Main] Received interrupt signal again, force exit.") |
| 74 | os._exit(force_exit_code) |
| 75 | |
| 76 | shutdown_requested = True |
| 77 | print(f"\n[Main] Received interrupt signal {signum}, exiting (cleanup max {cleanup_timeout_seconds}s)...") |
| 78 | |
| 79 | if on_first_interrupt is not None: |
| 80 | try: |
| 81 | on_first_interrupt() |
| 82 | except Exception: |
| 83 | pass |
| 84 | |
| 85 | runner_task.cancel() |
| 86 | |
| 87 | try: |
| 88 | loop.add_signal_handler(signal.SIGINT, _on_signal, signal.SIGINT) |
| 89 | loop.add_signal_handler(signal.SIGTERM, _on_signal, signal.SIGTERM) |
| 90 | except NotImplementedError: |
| 91 | signal.signal(signal.SIGINT, lambda signum, _frame: _on_signal(signum)) |
| 92 | signal.signal(signal.SIGTERM, lambda signum, _frame: _on_signal(signum)) |
| 93 | |
| 94 | cancelled = False |
| 95 | try: |
| 96 | await app_main() |
| 97 | except asyncio.CancelledError: |
| 98 | cancelled = True |
| 99 | finally: |
| 100 | try: |
| 101 | await _cleanup_with_timeout() |
| 102 | except Exception as e: |
| 103 | print(f"[Main] Error during cleanup: {e}") |
| 104 | await _cancel_remaining_tasks() |
| 105 | |
| 106 | if cancelled: |
| 107 | return |
| 108 | |
| 109 | asyncio.run(_runner()) |
no test coverage detected