@@ -302,11 +302,20 @@ class EmptyStatement(Exception):
302302# Contains data about a disabled command which is used to restore its original functions when the command is enabled
303303DisabledCommand = namedtuple ('DisabledCommand' , ['command_function' , 'help_function' ])
304304
305- # Used to restore state after redirection ends
306- # redirecting and piping are used to know what needs to be restored
307- RedirectionSavedState = utils .namedtuple_with_defaults ('RedirectionSavedState' ,
308- ['redirecting' , 'self_stdout' , 'sys_stdout' ,
309- 'piping' , 'pipe_proc_reader' ])
305+
306+ class RedirectionSavedState (object ):
307+ # Created by each command to store information about their redirection
308+ def __init__ (self ):
309+ # Tells if the command is redirecting
310+ self .redirecting = False
311+
312+ # If the command created a process to pipe to, then then is its reader
313+ self .pipe_proc_reader = None
314+
315+ # Used to restore values after the command ends
316+ self .saved_self_stdout = None
317+ self .saved_sys_stdout = None
318+ self .saved_pipe_proc_reader = None
310319
311320
312321class Cmd (cmd .Cmd ):
@@ -424,8 +433,11 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, persistent
424433 # Used load command to store the current script dir as a LIFO queue to support _relative_load command
425434 self ._script_dir = []
426435
427- # Used when piping command output to a shell command
428- self .pipe_proc_reader = None
436+ # A flag used to protect the setting up of redirection from a KeyboardInterrupt
437+ self .setting_up_redirection = False
438+
439+ # When this is not None, then it holds a ProcReader for the pipe process created by the current command
440+ self .cur_pipe_proc_reader = None
429441
430442 # Used by complete() for readline tab completion
431443 self .completion_matches = []
@@ -1654,12 +1666,13 @@ def sigint_handler(self, signum: int, frame) -> None:
16541666 :param signum: signal number
16551667 :param frame
16561668 """
1657- try :
1669+ # Don't do anything if we are setting up redirection
1670+ if self .setting_up_redirection :
1671+ return
1672+
1673+ if self .cur_pipe_proc_reader is not None :
16581674 # Terminate the current pipe process
1659- self .pipe_proc_reader .terminate ()
1660- except AttributeError :
1661- # Ignore since self.pipe_proc_reader was None
1662- pass
1675+ self .cur_pipe_proc_reader .terminate ()
16631676
16641677 # Re-raise a KeyboardInterrupt so other parts of the code can catch it
16651678 raise KeyboardInterrupt ("Got a keyboard interrupt" )
@@ -1722,13 +1735,18 @@ def onecmd_plus_hooks(self, line: str) -> bool:
17221735 # Keep track of whether or not we were already redirecting before this command
17231736 already_redirecting = self .redirecting
17241737
1725- # When this isn't None, we know that _redirect_output completed. This prevents getting into
1726- # a weird state if _redirect_output returns early because of a Ctrl-C event.
1738+ # This will be a RedirectionSavedState object for the command
17271739 saved_state = None
17281740
17291741 try :
1730- # Handle any redirection for this command
1742+ # Prevent a Ctrl-C from messing up our state while we set up redirection
1743+ self .setting_up_redirection = True
1744+
17311745 redir_error , saved_state = self ._redirect_output (statement )
1746+ self .cur_pipe_proc_reader = saved_state .pipe_proc_reader
1747+
1748+ # End Ctrl-C protection
1749+ self .setting_up_redirection = False
17321750
17331751 # Do not continue if an error occurred while trying to redirect
17341752 if not redir_error :
@@ -1761,7 +1779,7 @@ def onecmd_plus_hooks(self, line: str) -> bool:
17611779 stop = self .postcmd (stop , statement )
17621780
17631781 if self .timing :
1764- self .pfeedback ('Elapsed: %s' % str (datetime .datetime .now () - timestart ))
1782+ self .pfeedback ('Elapsed: {}' . format (datetime .datetime .now () - timestart ))
17651783 finally :
17661784 # Make sure _redirect_output completed
17671785 if saved_state is not None :
@@ -1904,7 +1922,12 @@ def _redirect_output(self, statement: Statement) -> Tuple[bool, RedirectionSaved
19041922 import subprocess
19051923
19061924 redir_error = False
1907- saved_state = RedirectionSavedState (redirecting = False , piping = False )
1925+
1926+ # Initialize the saved state
1927+ saved_state = RedirectionSavedState ()
1928+ saved_state .saved_self_stdout = self .stdout
1929+ saved_state .saved_sys_stdout = sys .stdout
1930+ saved_state .saved_pipe_proc_reader = self .cur_pipe_proc_reader
19081931
19091932 if not self .allow_redirection :
19101933 return redir_error , saved_state
@@ -1938,10 +1961,8 @@ def _redirect_output(self, statement: Statement) -> Tuple[bool, RedirectionSaved
19381961 creationflags = creationflags ,
19391962 start_new_session = start_new_session )
19401963
1941- saved_state = RedirectionSavedState (redirecting = True , self_stdout = self .stdout , sys_stdout = sys .stdout ,
1942- piping = True , pipe_proc_reader = self .pipe_proc_reader )
1943-
1944- self .pipe_proc_reader = utils .ProcReader (proc , self .stdout , sys .stderr )
1964+ saved_state .redirecting = True
1965+ saved_state .pipe_proc_reader = utils .ProcReader (proc , self .stdout , sys .stderr )
19451966 sys .stdout = self .stdout = pipe_write
19461967 except Exception as ex :
19471968 self .perror ('Failed to open pipe because - {}' .format (ex ), traceback_war = False )
@@ -1965,17 +1986,17 @@ def _redirect_output(self, statement: Statement) -> Tuple[bool, RedirectionSaved
19651986 mode = 'a'
19661987 try :
19671988 new_stdout = open (statement .output_to , mode )
1968- saved_state = RedirectionSavedState (redirecting = True , self_stdout = self .stdout ,
1969- sys_stdout = sys .stdout )
1989+ saved_state .redirecting = True
19701990 sys .stdout = self .stdout = new_stdout
19711991 except OSError as ex :
19721992 self .perror ('Failed to redirect because - {}' .format (ex ), traceback_war = False )
19731993 redir_error = True
19741994 else :
19751995 # going to a paste buffer
19761996 new_stdout = tempfile .TemporaryFile (mode = "w+" )
1977- saved_state = RedirectionSavedState ( redirecting = True , self_stdout = self . stdout , sys_stdout = sys . stdout )
1997+ saved_state . redirecting = True
19781998 sys .stdout = self .stdout = new_stdout
1999+
19792000 if statement .output == constants .REDIRECTION_APPEND :
19802001 self .poutput (get_paste_buffer ())
19812002
@@ -1988,7 +2009,6 @@ def _restore_output(self, statement: Statement, saved_state: RedirectionSavedSta
19882009 :param statement: Statement object which contains the parsed input from the user
19892010 :param saved_state: contains information needed to restore state data
19902011 """
1991- # Check if self.stdout was redirected
19922012 if saved_state .redirecting :
19932013 # If we redirected output to the clipboard
19942014 if statement .output and not statement .output_to :
@@ -2000,17 +2020,17 @@ def _restore_output(self, statement: Statement, saved_state: RedirectionSavedSta
20002020 self .stdout .close ()
20012021 except BrokenPipeError :
20022022 pass
2003- finally :
2004- self .stdout = saved_state .self_stdout
20052023
2006- # Check if sys.stdout was redirected
2007- if saved_state .sys_stdout is not None :
2008- sys .stdout = saved_state .sys_stdout
2024+ # Restore the stdout values
2025+ self .stdout = saved_state .saved_self_stdout
2026+ sys .stdout = saved_state .saved_sys_stdout
2027+
2028+ # Check if we need to wait for the process being piped to
2029+ if self .cur_pipe_proc_reader is not None :
2030+ self .cur_pipe_proc_reader .wait ()
20092031
2010- # Check if output was being piped to a process
2011- if saved_state .piping :
2012- self .pipe_proc_reader .wait ()
2013- self .pipe_proc_reader = saved_state .pipe_proc_reader
2032+ # Restore cur_pipe_proc_reader
2033+ self .cur_pipe_proc_reader = saved_state .saved_pipe_proc_reader
20142034
20152035 def cmd_func (self , command : str ) -> Optional [Callable ]:
20162036 """
0 commit comments