diff options
| author | Todd Leonhardt <todd.leonhardt@gmail.com> | 2018-03-07 20:38:24 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-03-07 20:38:24 -0500 |
| commit | 2af44735fd85cdbcfb2b43adbb3de1271ba37157 (patch) | |
| tree | 5cf1a191d33f396f7081c915cbb1ec038795e458 /cmd2.py | |
| parent | a6f0e06a350f2ab65d3bf635fa7aae0b655ed44a (diff) | |
| parent | 0979d63854bc71dab4c1596c49260539e522216b (diff) | |
| download | cmd2-git-2af44735fd85cdbcfb2b43adbb3de1271ba37157.tar.gz | |
Merge pull request #297 from python-cmd2/paged_output
Added support for paged output
Diffstat (limited to 'cmd2.py')
| -rwxr-xr-x | cmd2.py | 69 |
1 files changed, 65 insertions, 4 deletions
@@ -1133,6 +1133,12 @@ class Cmd(cmd.Cmd): # Used by complete() for readline tab completion self.completion_matches = [] + # Used to keep track of whether we are redirecting or piping output + self.redirecting = False + + # If this string is non-empty, then this warning message will print if a broken pipe error occurs while printing + self.broken_pipe_warning = '' + # ----- Methods related to presenting output to the user ----- @property @@ -1171,10 +1177,10 @@ class Cmd(cmd.Cmd): self.stdout.write(end) except BROKEN_PIPE_ERROR: # This occurs if a command's output is being piped to another process and that process closes before the - # command is finished. We intentionally don't print a warning message here since we know that stdout - # will be restored by the _restore_output() method. If you would like your application to print a - # warning message, then override this method. - pass + # command is finished. If you would like your application to print a warning message, then set the + # broken_pipe_warning attribute to the message you want printed. + if self.broken_pipe_warning: + sys.stderr.write(self.broken_pipe_warning) def perror(self, errmsg, exception_type=None, traceback_war=True): """ Print error message to sys.stderr and if debug is true, print an exception Traceback if one exists. @@ -1207,6 +1213,56 @@ class Cmd(cmd.Cmd): else: sys.stderr.write("{}\n".format(msg)) + def ppaged(self, msg, end='\n'): + """Print output using a pager if it would go off screen and stdout isn't currently being redirected. + + Never uses a pager inside of a script (Python or text) or when output is being redirected or piped. + + :param msg: str - message to print to current stdout - anything convertible to a str with '{}'.format() is OK + :param end: str - string appended after the end of the message if not already present, default a newline + """ + if msg is not None and msg != '': + try: + msg_str = '{}'.format(msg) + if not msg_str.endswith(end): + msg_str += end + + # Don't attempt to use a pager that can block if redirecting or running a script (either text or Python) + if not self.redirecting and not self._in_py and not self._script_dir: + if sys.platform.startswith('win'): + pager_cmd = 'more' + else: + # Here is the meaning of the various flags we are using with the less command: + # -S causes lines longer than the screen width to be chopped (truncated) rather than wrapped + # -R causes ANSI "color" escape sequences to be output in raw form (i.e. colors are displayed) + # -X disables sending the termcap initialization and deinitialization strings to the terminal + # -F causes less to automatically exit if the entire file can be displayed on the first screen + pager_cmd = 'less -SRXF' + self.pipe_proc = subprocess.Popen(pager_cmd, shell=True, stdin=subprocess.PIPE) + try: + self.pipe_proc.stdin.write(msg_str.encode('utf-8', 'replace')) + self.pipe_proc.stdin.close() + except (IOError, KeyboardInterrupt): + pass + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting search etc. inside less) + while True: + try: + self.pipe_proc.wait() + except KeyboardInterrupt: + pass + else: + break + self.pipe_proc = None + else: + self.stdout.write(msg_str) + except BROKEN_PIPE_ERROR: + # This occurs if a command's output is being piped to another process and that process closes before the + # command is finished. If you would like your application to print a warning message, then set the + # broken_pipe_warning attribute to the message you want printed. + if self.broken_pipe_warning: + sys.stderr.write(self.broken_pipe_warning) + def colorize(self, val, color): """Given a string (``val``), returns that string wrapped in UNIX-style special characters that turn on (and then off) text color and style. @@ -1599,6 +1655,7 @@ class Cmd(cmd.Cmd): # Open each side of the pipe and set stdout accordingly # noinspection PyTypeChecker self.stdout = io.open(write_fd, write_mode) + self.redirecting = True # noinspection PyTypeChecker subproc_stdin = io.open(read_fd, read_mode) @@ -1612,6 +1669,7 @@ class Cmd(cmd.Cmd): self.pipe_proc = None self.kept_state.restore() self.kept_state = None + self.redirecting = False # Re-raise the exception raise ex @@ -1620,6 +1678,7 @@ class Cmd(cmd.Cmd): raise EnvironmentError('Cannot redirect to paste buffer; install ``xclip`` and re-run to enable') self.kept_state = Statekeeper(self, ('stdout',)) self.kept_sys = Statekeeper(sys, ('stdout',)) + self.redirecting = True if statement.parsed.outputTo: mode = 'w' if statement.parsed.output == 2 * self.redirector: @@ -1662,6 +1721,8 @@ class Cmd(cmd.Cmd): self.kept_sys.restore() self.kept_sys = None + self.redirecting = False + def _func_named(self, arg): """Gets the method name associated with a given command. |
