@@ -187,7 +187,7 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
187187
188188 # Attributes which should NOT be dynamically settable via the set command at runtime
189189 self .default_to_shell = False # Attempt to run unrecognized commands as shell commands
190- self .quit_on_sigint = False # Quit the loop on interrupt instead of just resetting prompt
190+ self .quit_on_sigint = False # Ctrl-C at the prompt will quit the program instead of just resetting prompt
191191 self .allow_redirection = allow_redirection # Security setting to prevent redirection of stdout
192192
193193 # Attributes which ARE dynamically settable via the set command at runtime
@@ -1584,11 +1584,15 @@ def parseline(self, line: str) -> Tuple[str, str, str]:
15841584 statement = self .statement_parser .parse_command_only (line )
15851585 return statement .command , statement .args , statement .command_and_args
15861586
1587- def onecmd_plus_hooks (self , line : str , * , add_to_history : bool = True , py_bridge_call : bool = False ) -> bool :
1587+ def onecmd_plus_hooks (self , line : str , * , add_to_history : bool = True ,
1588+ raise_keyboard_interrupt : bool = False , py_bridge_call : bool = False ) -> bool :
15881589 """Top-level function called by cmdloop() to handle parsing a line and running the command and all of its hooks.
15891590
15901591 :param line: command line to run
15911592 :param add_to_history: If True, then add this command to history. Defaults to True.
1593+ :param raise_keyboard_interrupt: if True, then KeyboardInterrupt exceptions will be raised. This is used when
1594+ running commands in a loop to be able to stop the whole loop and not just
1595+ the current command. Defaults to False.
15921596 :param py_bridge_call: This should only ever be set to True by PyBridge to signify the beginning
15931597 of an app() call from Python. It is used to enable/disable the storage of the
15941598 command's stdout.
@@ -1681,14 +1685,18 @@ def onecmd_plus_hooks(self, line: str, *, add_to_history: bool = True, py_bridge
16811685 if py_bridge_call :
16821686 # Stop saving command's stdout before command finalization hooks run
16831687 self .stdout .pause_storage = True
1684-
1688+ except KeyboardInterrupt as e :
1689+ if raise_keyboard_interrupt :
1690+ raise e
16851691 except (Cmd2ArgparseError , EmptyStatement ):
16861692 # Don't do anything, but do allow command finalization hooks to run
16871693 pass
16881694 except Exception as ex :
16891695 self .pexcept (ex )
16901696 finally :
1691- return self ._run_cmdfinalization_hooks (stop , statement )
1697+ stop = self ._run_cmdfinalization_hooks (stop , statement )
1698+
1699+ return stop
16921700
16931701 def _run_cmdfinalization_hooks (self , stop : bool , statement : Optional [Statement ]) -> bool :
16941702 """Run the command finalization hooks"""
@@ -1711,13 +1719,16 @@ def _run_cmdfinalization_hooks(self, stop: bool, statement: Optional[Statement])
17111719 except Exception as ex :
17121720 self .pexcept (ex )
17131721
1714- def runcmds_plus_hooks (self , cmds : List [Union [HistoryItem , str ]], * , add_to_history : bool = True ) -> bool :
1722+ def runcmds_plus_hooks (self , cmds : List [Union [HistoryItem , str ]], * , add_to_history : bool = True ,
1723+ stop_on_keyboard_interrupt : bool = True ) -> bool :
17151724 """
17161725 Used when commands are being run in an automated fashion like text scripts or history replays.
17171726 The prompt and command line for each command will be printed if echo is True.
17181727
17191728 :param cmds: commands to run
17201729 :param add_to_history: If True, then add these commands to history. Defaults to True.
1730+ :param stop_on_keyboard_interrupt: stop command loop if Ctrl-C is pressed instead of just
1731+ moving to the next command. Defaults to True.
17211732 :return: True if running of commands should stop
17221733 """
17231734 for line in cmds :
@@ -1727,8 +1738,14 @@ def runcmds_plus_hooks(self, cmds: List[Union[HistoryItem, str]], *, add_to_hist
17271738 if self .echo :
17281739 self .poutput ('{}{}' .format (self .prompt , line ))
17291740
1730- if self .onecmd_plus_hooks (line , add_to_history = add_to_history ):
1731- return True
1741+ try :
1742+ if self .onecmd_plus_hooks (line , add_to_history = add_to_history ,
1743+ raise_keyboard_interrupt = stop_on_keyboard_interrupt ):
1744+ return True
1745+ except KeyboardInterrupt as e :
1746+ if stop_on_keyboard_interrupt :
1747+ self .perror (e )
1748+ break
17321749
17331750 return False
17341751
@@ -3238,8 +3255,10 @@ def py_quit():
32383255 # noinspection PyBroadException
32393256 try :
32403257 interp .runcode (py_code_to_run )
3258+ except KeyboardInterrupt as e :
3259+ raise e
32413260 except BaseException :
3242- # We don't care about any exception that happened in the Python code
3261+ # We don't care about any other exceptions that happened in the Python code
32433262 pass
32443263
32453264 # Otherwise we will open an interactive Python shell
@@ -3269,9 +3288,6 @@ def py_quit():
32693288 if saved_cmd2_env is not None :
32703289 self ._restore_cmd2_env (saved_cmd2_env )
32713290
3272- except KeyboardInterrupt :
3273- pass
3274-
32753291 finally :
32763292 with self .sigint_protection :
32773293 if saved_sys_path is not None :
@@ -3302,8 +3318,6 @@ def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
33023318 if selection != 'Yes' :
33033319 return
33043320
3305- py_return = False
3306-
33073321 # Save current command line arguments
33083322 orig_args = sys .argv
33093323
@@ -3314,9 +3328,6 @@ def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
33143328 # noinspection PyTypeChecker
33153329 py_return = self .do_py ('--pyscript {}' .format (utils .quote_string (args .script_path )))
33163330
3317- except KeyboardInterrupt :
3318- pass
3319-
33203331 finally :
33213332 # Restore command line arguments to original state
33223333 sys .argv = orig_args
@@ -3629,7 +3640,12 @@ def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcrip
36293640 self .stdout = utils .StdSim (self .stdout )
36303641
36313642 # then run the command and let the output go into our buffer
3632- stop = self .onecmd_plus_hooks (history_item )
3643+ try :
3644+ stop = self .onecmd_plus_hooks (history_item , raise_keyboard_interrupt = True )
3645+ except KeyboardInterrupt as e :
3646+ self .perror (e )
3647+ stop = True
3648+
36333649 commands_run += 1
36343650
36353651 # add the regex-escaped output to the transcript
0 commit comments