diff --git a/CHANGELOG.md b/CHANGELOG.md index 378fffcd..dac58426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ ## 2.5.0 (TBD) * Breaking Change * `cmd2` 2.5 supports Python 3.8+ (removed support for Python 3.6 and 3.7) -* Bug Fixes - * Fixed issue where persistent history file was not saved upon SIGHUP and SIGTERM signals. * Enhancements * Removed dependency on `attrs` and replaced with [dataclasses](https://docs.python.org/3/library/dataclasses.html) * add `allow_clipboard` initialization parameter and attribute to disable ability to diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 1381f0d5..5f95b2ab 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -2405,13 +2405,13 @@ def get_help_topics(self) -> List[str]: return [topic for topic in all_topics if topic not in self.hidden_commands and topic not in self.disabled_commands] # noinspection PyUnusedLocal - def sigint_handler(self, signum: int, _: Optional[FrameType]) -> None: + 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 method. + If you need custom SIGINT behavior, then override this function. :param signum: signal number - :param _: the current stack frame or None + :param _: required param for signal handlers """ if self._cur_pipe_proc_reader is not None: # Pass the SIGINT to the current pipe process @@ -2427,23 +2427,6 @@ def sigint_handler(self, signum: int, _: Optional[FrameType]) -> None: if raise_interrupt: self._raise_keyboard_interrupt() - def termination_signal_handler(self, signum: int, _: Optional[FrameType]) -> None: - """ - Signal handler for SIGHUP and SIGTERM. Only runs on Linux and Mac. - - SIGHUP - received when terminal window is closed - SIGTERM - received when this app has been requested to terminate - - The basic purpose of this method is to call sys.exit() so our exit handler will run - and save the persistent history file. If you need more complex behavior like killing - threads and performing cleanup, then override this method. - - :param signum: signal number - :param _: the current stack frame or None - """ - # POSIX systems add 128 to signal numbers for the exit code - sys.exit(128 + signum) - def _raise_keyboard_interrupt(self) -> None: """Helper function to raise a KeyboardInterrupt""" raise KeyboardInterrupt("Got a keyboard interrupt") @@ -5443,18 +5426,11 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override] if not threading.current_thread() is threading.main_thread(): raise RuntimeError("cmdloop must be run in the main thread") - # Register signal handlers + # Register a SIGINT signal handler for Ctrl+C import signal original_sigint_handler = signal.getsignal(signal.SIGINT) - signal.signal(signal.SIGINT, self.sigint_handler) - - if not sys.platform.startswith('win'): - original_sighup_handler = signal.getsignal(signal.SIGHUP) - signal.signal(signal.SIGHUP, self.termination_signal_handler) - - original_sigterm_handler = signal.getsignal(signal.SIGTERM) - signal.signal(signal.SIGTERM, self.termination_signal_handler) + signal.signal(signal.SIGINT, self.sigint_handler) # type: ignore # Grab terminal lock before the command line prompt has been drawn by readline self.terminal_lock.acquire() @@ -5488,13 +5464,9 @@ 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 original signal handlers + # Restore the original signal handler signal.signal(signal.SIGINT, original_sigint_handler) - if not sys.platform.startswith('win'): - signal.signal(signal.SIGHUP, original_sighup_handler) - signal.signal(signal.SIGTERM, original_sigterm_handler) - return self.exit_code ### diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 2130cbf1..5686e9f9 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -1038,17 +1038,6 @@ def test_raise_keyboard_interrupt(base_app): assert 'Got a keyboard interrupt' in str(excinfo.value) -@pytest.mark.skipif(sys.platform.startswith('win'), reason="SIGTERM only handeled on Linux/Mac") -def test_termination_signal_handler(base_app): - with pytest.raises(SystemExit) as excinfo: - base_app.termination_signal_handler(signal.SIGHUP, 1) - assert excinfo.value.code == signal.SIGHUP + 128 - - with pytest.raises(SystemExit) as excinfo: - base_app.termination_signal_handler(signal.SIGTERM, 1) - assert excinfo.value.code == signal.SIGTERM + 128 - - class HookFailureApp(cmd2.Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)