@@ -2405,13 +2405,13 @@ def get_help_topics(self) -> List[str]:
24052405 return [topic for topic in all_topics if topic not in self .hidden_commands and topic not in self .disabled_commands ]
24062406
24072407 # noinspection PyUnusedLocal
2408- def sigint_handler (self , signum : int , _ : FrameType ) -> None :
2408+ def sigint_handler (self , signum : int , _ : Optional [ FrameType ] ) -> None :
24092409 """Signal handler for SIGINTs which typically come from Ctrl-C events.
24102410
2411- If you need custom SIGINT behavior, then override this function .
2411+ If you need custom SIGINT behavior, then override this method .
24122412
24132413 :param signum: signal number
2414- :param _: required param for signal handlers
2414+ :param _: the current stack frame or None
24152415 """
24162416 if self ._cur_pipe_proc_reader is not None :
24172417 # Pass the SIGINT to the current pipe process
@@ -2427,6 +2427,23 @@ def sigint_handler(self, signum: int, _: FrameType) -> None:
24272427 if raise_interrupt :
24282428 self ._raise_keyboard_interrupt ()
24292429
2430+ def termination_signal_handler (self , signum : int , _ : Optional [FrameType ]) -> None :
2431+ """
2432+ Signal handler for SIGHUP and SIGTERM. Only runs on Linux and Mac.
2433+
2434+ SIGHUP - received when terminal window is closed
2435+ SIGTERM - received when this app has been requested to terminate
2436+
2437+ The basic purpose of this method is to call sys.exit() so our exit handler will run
2438+ and save the persistent history file. If you need more complex behavior like killing
2439+ threads and performing cleanup, then override this method.
2440+
2441+ :param signum: signal number
2442+ :param _: the current stack frame or None
2443+ """
2444+ # POSIX systems add 128 to signal numbers for the exit code
2445+ sys .exit (128 + signum )
2446+
24302447 def _raise_keyboard_interrupt (self ) -> None :
24312448 """Helper function to raise a KeyboardInterrupt"""
24322449 raise KeyboardInterrupt ("Got a keyboard interrupt" )
@@ -5426,11 +5443,18 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override]
54265443 if not threading .current_thread () is threading .main_thread ():
54275444 raise RuntimeError ("cmdloop must be run in the main thread" )
54285445
5429- # Register a SIGINT signal handler for Ctrl+C
5446+ # Register signal handlers
54305447 import signal
54315448
54325449 original_sigint_handler = signal .getsignal (signal .SIGINT )
5433- signal .signal (signal .SIGINT , self .sigint_handler ) # type: ignore
5450+ signal .signal (signal .SIGINT , self .sigint_handler )
5451+
5452+ if not sys .platform .startswith ('win' ):
5453+ original_sighup_handler = signal .getsignal (signal .SIGHUP )
5454+ signal .signal (signal .SIGHUP , self .termination_signal_handler )
5455+
5456+ original_sigterm_handler = signal .getsignal (signal .SIGTERM )
5457+ signal .signal (signal .SIGTERM , self .termination_signal_handler )
54345458
54355459 # Grab terminal lock before the command line prompt has been drawn by readline
54365460 self .terminal_lock .acquire ()
@@ -5464,9 +5488,13 @@ def cmdloop(self, intro: Optional[str] = None) -> int: # type: ignore[override]
54645488 # This will also zero the lock count in case cmdloop() is called again
54655489 self .terminal_lock .release ()
54665490
5467- # Restore the original signal handler
5491+ # Restore original signal handlers
54685492 signal .signal (signal .SIGINT , original_sigint_handler )
54695493
5494+ if not sys .platform .startswith ('win' ):
5495+ signal .signal (signal .SIGHUP , original_sighup_handler )
5496+ signal .signal (signal .SIGTERM , original_sigterm_handler )
5497+
54705498 return self .exit_code
54715499
54725500 ###
0 commit comments