@@ -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 ex :
1689+ if raise_keyboard_interrupt :
1690+ raise ex
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
@@ -3269,9 +3286,6 @@ def py_quit():
32693286 if saved_cmd2_env is not None :
32703287 self ._restore_cmd2_env (saved_cmd2_env )
32713288
3272- except KeyboardInterrupt :
3273- pass
3274-
32753289 finally :
32763290 with self .sigint_protection :
32773291 if saved_sys_path is not None :
@@ -3302,8 +3316,6 @@ def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
33023316 if selection != 'Yes' :
33033317 return
33043318
3305- py_return = False
3306-
33073319 # Save current command line arguments
33083320 orig_args = sys .argv
33093321
@@ -3314,9 +3326,6 @@ def do_run_pyscript(self, args: argparse.Namespace) -> Optional[bool]:
33143326 # noinspection PyTypeChecker
33153327 py_return = self .do_py ('--pyscript {}' .format (utils .quote_string (args .script_path )))
33163328
3317- except KeyboardInterrupt :
3318- pass
3319-
33203329 finally :
33213330 # Restore command line arguments to original state
33223331 sys .argv = orig_args
@@ -3629,7 +3638,12 @@ def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcrip
36293638 self .stdout = utils .StdSim (self .stdout )
36303639
36313640 # then run the command and let the output go into our buffer
3632- stop = self .onecmd_plus_hooks (history_item )
3641+ try :
3642+ stop = self .onecmd_plus_hooks (history_item , raise_keyboard_interrupt = True )
3643+ except KeyboardInterrupt as e :
3644+ self .perror (e )
3645+ stop = True
3646+
36333647 commands_run += 1
36343648
36353649 # add the regex-escaped output to the transcript
0 commit comments