From f74a706adb28f453c6daa59cad1fe2b3e25a672e Mon Sep 17 00:00:00 2001 From: Luca Toniolo <10792599+grandixximo@users.noreply.github.com> Date: Sat, 30 May 2026 09:38:20 +0800 Subject: [PATCH] gmoccapy: exit cleanly on termination signal instead of error dialog gmoccapy did not exit on SIGTERM; callers had to escalate to SIGKILL. Root cause: PyGObject's signal bridge surfaces a termination signal into the GTK main loop as a Python KeyboardInterrupt. That propagated to the installed sys.excepthook, which unconditionally popped a modal 'Found an error ... KeyboardInterrupt' dialog and blocked in the dialog's nested loop, so the process never quit. Handle KeyboardInterrupt at the top of excepthook: log it and quit the main loop instead of showing the modal error dialog. Verified that gmoccapy now exits cleanly on SIGTERM (no SIGKILL escalation, no spurious error-dialog traceback). The previous broken 'except KeyboardInterrupt' on the window lookup (which could never fire there) is removed. Surfaced by the ui-smoke tests (PR #4054). Fixes #4069 --- src/emc/usr_intf/gmoccapy/gmoccapy.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/emc/usr_intf/gmoccapy/gmoccapy.py b/src/emc/usr_intf/gmoccapy/gmoccapy.py index f419ea39b46..86c2ed28a5d 100644 --- a/src/emc/usr_intf/gmoccapy/gmoccapy.py +++ b/src/emc/usr_intf/gmoccapy/gmoccapy.py @@ -51,10 +51,20 @@ # Throws up a dialog with debug info when an error is encountered def excepthook(exc_type, exc_obj, exc_tb): + # A KeyboardInterrupt reaching the excepthook is a termination request: + # either SIGINT, or SIGTERM surfaced into the main loop by PyGObject's + # signal bridge. Quit cleanly instead of popping a modal error dialog, + # which would block in a nested loop and leave gmoccapy running until the + # caller escalates to SIGKILL. + if issubclass(exc_type, KeyboardInterrupt): + LOG.info("gmoccapy interrupted (signal), shutting down") + try: + Gtk.main_quit() + except Exception: + pass + return try: w = app.widgets.window1 - except KeyboardInterrupt: - sys.exit() except NameError: w = None lines = traceback.format_exception(exc_type, exc_obj, exc_tb)