@@ -1133,6 +1133,12 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor
11331133 # Used by complete() for readline tab completion
11341134 self .completion_matches = []
11351135
1136+ # Used to keep track of whether we are redirecting or piping output
1137+ self .redirecting = False
1138+
1139+ # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing
1140+ self .broken_pipe_warning = ''
1141+
11361142 # ----- Methods related to presenting output to the user -----
11371143
11381144 @property
@@ -1171,10 +1177,10 @@ def poutput(self, msg, end='\n'):
11711177 self .stdout .write (end )
11721178 except BROKEN_PIPE_ERROR :
11731179 # This occurs if a command's output is being piped to another process and that process closes before the
1174- # command is finished. We intentionally don't print a warning message here since we know that stdout
1175- # will be restored by the _restore_output() method. If you would like your application to print a
1176- # warning message, then override this method.
1177- pass
1180+ # command is finished. If you would like your application to print a warning message, then set the
1181+ # broken_pipe_warning attribute to the message you want printed.
1182+ if self . broken_pipe_warning :
1183+ sys . stderr . write ( self . broken_pipe_warning )
11781184
11791185 def perror (self , errmsg , exception_type = None , traceback_war = True ):
11801186 """ Print error message to sys.stderr and if debug is true, print an exception Traceback if one exists.
@@ -1207,6 +1213,56 @@ def pfeedback(self, msg):
12071213 else :
12081214 sys .stderr .write ("{}\n " .format (msg ))
12091215
1216+ def ppaged (self , msg , end = '\n ' ):
1217+ """Print output using a pager if it would go off screen and stdout isn't currently being redirected.
1218+
1219+ Never uses a pager inside of a script (Python or text) or when output is being redirected or piped.
1220+
1221+ :param msg: str - message to print to current stdout - anything convertible to a str with '{}'.format() is OK
1222+ :param end: str - string appended after the end of the message if not already present, default a newline
1223+ """
1224+ if msg is not None and msg != '' :
1225+ try :
1226+ msg_str = '{}' .format (msg )
1227+ if not msg_str .endswith (end ):
1228+ msg_str += end
1229+
1230+ # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python)
1231+ if not self .redirecting and not self ._in_py and not self ._script_dir :
1232+ if sys .platform .startswith ('win' ):
1233+ pager_cmd = 'more'
1234+ else :
1235+ # Here is the meaning of the various flags we are using with the less command:
1236+ # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped
1237+ # -R causes ANSI "color" escape sequences to be output in raw form (i.e. colors are displayed)
1238+ # -X disables sending the termcap initialization and deinitialization strings to the terminal
1239+ # -F causes less to automatically exit if the entire file can be displayed on the first screen
1240+ pager_cmd = 'less -SRXF'
1241+ self .pipe_proc = subprocess .Popen (pager_cmd , shell = True , stdin = subprocess .PIPE )
1242+ try :
1243+ self .pipe_proc .stdin .write (msg_str .encode ('utf-8' , 'replace' ))
1244+ self .pipe_proc .stdin .close ()
1245+ except (IOError , KeyboardInterrupt ):
1246+ pass
1247+
1248+ # Less doesn't respect ^C, but catches it for its own UI purposes (aborting search etc. inside less)
1249+ while True :
1250+ try :
1251+ self .pipe_proc .wait ()
1252+ except KeyboardInterrupt :
1253+ pass
1254+ else :
1255+ break
1256+ self .pipe_proc = None
1257+ else :
1258+ self .stdout .write (msg_str )
1259+ except BROKEN_PIPE_ERROR :
1260+ # This occurs if a command's output is being piped to another process and that process closes before the
1261+ # command is finished. If you would like your application to print a warning message, then set the
1262+ # broken_pipe_warning attribute to the message you want printed.
1263+ if self .broken_pipe_warning :
1264+ sys .stderr .write (self .broken_pipe_warning )
1265+
12101266 def colorize (self , val , color ):
12111267 """Given a string (``val``), returns that string wrapped in UNIX-style
12121268 special characters that turn on (and then off) text color and style.
@@ -1599,6 +1655,7 @@ def _redirect_output(self, statement):
15991655 # Open each side of the pipe and set stdout accordingly
16001656 # noinspection PyTypeChecker
16011657 self .stdout = io .open (write_fd , write_mode )
1658+ self .redirecting = True
16021659 # noinspection PyTypeChecker
16031660 subproc_stdin = io .open (read_fd , read_mode )
16041661
@@ -1612,6 +1669,7 @@ def _redirect_output(self, statement):
16121669 self .pipe_proc = None
16131670 self .kept_state .restore ()
16141671 self .kept_state = None
1672+ self .redirecting = False
16151673
16161674 # Re-raise the exception
16171675 raise ex
@@ -1620,6 +1678,7 @@ def _redirect_output(self, statement):
16201678 raise EnvironmentError ('Cannot redirect to paste buffer; install ``xclip`` and re-run to enable' )
16211679 self .kept_state = Statekeeper (self , ('stdout' ,))
16221680 self .kept_sys = Statekeeper (sys , ('stdout' ,))
1681+ self .redirecting = True
16231682 if statement .parsed .outputTo :
16241683 mode = 'w'
16251684 if statement .parsed .output == 2 * self .redirector :
@@ -1662,6 +1721,8 @@ def _restore_output(self, statement):
16621721 self .kept_sys .restore ()
16631722 self .kept_sys = None
16641723
1724+ self .redirecting = False
1725+
16651726 def _func_named (self , arg ):
16661727 """Gets the method name associated with a given command.
16671728
0 commit comments