From 972c26b22f343bbd434c507b3a5bbd27f9b4fce8 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 14 Feb 2026 16:58:21 -0700 Subject: [PATCH] Guard finally blocks against app teardown during Ctrl+C The sweep/scan finally blocks call self.app.call_from_thread() to reset button state, but self.app raises NoActiveAppError if the Textual context is already torn down during Ctrl+C shutdown. Wrap with contextlib.suppress so the flag reset still happens. --- tui/src/birdcage_tui/screens/signal.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tui/src/birdcage_tui/screens/signal.py b/tui/src/birdcage_tui/screens/signal.py index 1a5ab17..591db54 100644 --- a/tui/src/birdcage_tui/screens/signal.py +++ b/tui/src/birdcage_tui/screens/signal.py @@ -447,15 +447,17 @@ class SignalScreen(Container): "Firmware sweep failed, falling back to software", exc_info=True, ) - self.app.call_from_thread( - self._set_sweep_status, - "Firmware sweep failed -- falling back...", - ) + with contextlib.suppress(Exception): + self.app.call_from_thread( + self._set_sweep_status, + "Firmware sweep failed -- falling back...", + ) self._do_sweep_software(device) finally: self._sweeping = False - self.app.call_from_thread(self._reset_sweep_buttons) + with contextlib.suppress(Exception): + self.app.call_from_thread(self._reset_sweep_buttons) def _do_sweep_firmware(self, device: DeviceLike) -> None: """Firmware-accelerated sweep via azscanwxp (runs in worker thread).""" @@ -674,7 +676,8 @@ class SignalScreen(Container): self._do_scan_inner(device) finally: self._scanning = False - self.app.call_from_thread(self._reset_scan_buttons) + with contextlib.suppress(Exception): + self.app.call_from_thread(self._reset_scan_buttons) def _do_scan_inner(self, device: DeviceLike) -> None: """Inner scan logic (called from _do_scan worker thread)."""