diff --git a/src/atm/main.py b/src/atm/main.py index 43001d0..cba4489 100644 --- a/src/atm/main.py +++ b/src/atm/main.py @@ -1124,69 +1124,7 @@ async def _drain_cmd_queue(ctx: RunContext) -> None: def run_live(cfg, duration_s=None, capture_stub: bool = False) -> None: """Sync entry point — delegates to asyncio event loop.""" - if sys.platform == "win32": - _run_live_win32(cfg, duration_s=duration_s, capture_stub=capture_stub) - else: - asyncio.run(run_live_async(cfg, duration_s=duration_s, capture_stub=capture_stub)) - - -def _run_live_win32(cfg, duration_s=None, capture_stub: bool = False) -> None: - """Windows variant: catches CTRL_CLOSE_EVENT so the finally block (Telegram alert) runs.""" - try: - import win32api # type: ignore[import-untyped] - except ImportError: - asyncio.run(run_live_async(cfg, duration_s=duration_s, capture_stub=capture_stub)) - return - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - _task: list[asyncio.Task] = [] - - def _close_handler(event: int) -> bool: - # CTRL_CLOSE_EVENT=2, CTRL_LOGOFF_EVENT=5, CTRL_SHUTDOWN_EVENT=6 - if event in (2, 5, 6) and _task: - loop.call_soon_threadsafe(_task[0].cancel) - return True # tell Windows we handled it; gives ~5 s before force-kill - return False # CTRL+C / CTRL+BREAK: let Python's default handler fire - - win32api.SetConsoleCtrlHandler(_close_handler, True) - - async def _run() -> None: - t = asyncio.create_task( - run_live_async(cfg, duration_s=duration_s, capture_stub=capture_stub), - name="atm_live", - ) - _task.append(t) - try: - await t - except asyncio.CancelledError: - pass # finally in run_live_async already sent the Telegram alert - - try: - loop.run_until_complete(_run()) - except KeyboardInterrupt: - # CTRL+C: cancel the live task and let its finally block run - if _task and not _task[0].done(): - _task[0].cancel() - try: - loop.run_until_complete(_task[0]) - except (asyncio.CancelledError, Exception): - pass - finally: - pending = asyncio.all_tasks(loop) - for t in pending: - t.cancel() - if pending: - try: - loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True)) - except Exception: - pass - try: - loop.run_until_complete(loop.shutdown_asyncgens()) - except Exception: - pass - asyncio.set_event_loop(None) - loop.close() + asyncio.run(run_live_async(cfg, duration_s=duration_s, capture_stub=capture_stub)) async def run_live_async(cfg, duration_s=None, capture_stub: bool = False) -> None: