From 673eec374db3123d8ac8ffd1d5d6d0b8113a3f5d Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 12 Sep 2024 02:47:49 -0400 Subject: [PATCH] Fixed issue where persistent history file was not saved upon SIGTERM and SIGHUP signals. --- cmd2/cmd2.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index c5c0db78..f9b8658c 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -780,7 +780,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: methods = inspect.getmembers( cmdset, - predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] + predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) @@ -811,7 +811,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: def _check_uninstallable(self, cmdset: CommandSet) -> None: methods = inspect.getmembers( cmdset, - predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] + predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) @@ -2418,7 +2418,7 @@ def get_help_topics(self) -> List[str]: def sigint_handler(self, signum: int, _: FrameType) -> None: """Signal handler for SIGINTs which typically come from Ctrl-C events. - If you need custom SIGINT behavior, then override this function. + If you need custom SIGINT behavior, then override this method. :param signum: signal number :param _: required param for signal handlers @@ -2437,6 +2437,30 @@ def sigint_handler(self, signum: int, _: FrameType) -> None: if raise_interrupt: self._raise_keyboard_interrupt() + def sigterm_handler(self, signum: int, _: FrameType) -> None: + """ + Signal handler for SIGTERMs which are sent to politely ask this app to terminate. + + If you need custom SIGTERM behavior, then override this method. + + :param signum: signal number + :param _: required param for signal handlers + """ + # Gracefully exit so the persistent history file will be written. + sys.exit(self.exit_code) + + def sighup_handler(self, signum: int, _: FrameType) -> None: + """ + Signal handler for SIGHUPs which are sent when the terminal is closed. + + If you need custom SIGHUP behavior, then override this method. + + :param signum: signal number + :param _: required param for signal handlers + """ + # Gracefully exit so the persistent history file will be written. + sys.exit(self.exit_code) + def _raise_keyboard_interrupt(self) -> None: """Helper function to raise a KeyboardInterrupt""" raise KeyboardInterrupt("Got a keyboard interrupt") @@ -5447,6 +5471,12 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override] original_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, self.sigint_handler) # type: ignore + original_sigterm_handler = signal.getsignal(signal.SIGTERM) + signal.signal(signal.SIGTERM, self.sigterm_handler) # type: ignore + + original_sighup_handler = signal.getsignal(signal.SIGHUP) + signal.signal(signal.SIGHUP, self.sighup_handler) # type: ignore + # Grab terminal lock before the command line prompt has been drawn by readline self.terminal_lock.acquire() @@ -5479,8 +5509,10 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override] # This will also zero the lock count in case cmdloop() is called again self.terminal_lock.release() - # Restore the original signal handler + # Restore the original signal handlers signal.signal(signal.SIGINT, original_sigint_handler) + signal.signal(signal.SIGTERM, original_sigterm_handler) + signal.signal(signal.SIGHUP, original_sighup_handler) return self.exit_code