Paging output from python

Viewed 15370

I'm trying to implement something similar to git log, which will only page the output if the log is of a certain length. If you're not familiar with git, I'm essentially trying to achieve this:

python some_script.py | less

With some help from the paging implementation in python2.6/pydoc.py, I was able to come up with this:

import os
text = '...some text...'
pipe = os.popen('less', 'w')
pipe.write(text)
pipe.close()

which works great, but os.popen() is deprecated. I've considered writing to a temp file and calling less with its path, but that doesn't seem ideal. Is this possible with subprocess? Any other ideas?

EDIT:

So I've gotten subprocess working. I was able to give it the text variable with Popen.communicate(text), but since I really want to redirect print statements, I've settled on this:

  import os, sys, subprocess, tempfile

  page = True
  if page:
      path = tempfile.mkstemp()[1]
      tmp_file = open(path, 'a')
      sys.stdout = tmp_file
  print '...some text...'
  if page:
      tmp_file.flush()
      tmp_file.close()
      p = subprocess.Popen(['less', path], stdin=subprocess.PIPE)
      p.communicate()
      sys.stdout = sys.__stdout__     

Of course, I'd end up wrapping it into functions. Does anyone see a problem with that?

5 Answers

With one caveat, this works for me as a way to redirect process output to a pager on Linux (I don't have Windows handy to test there) when, for whatever reason, it's not feasible to write it all to a file or StringIO and then feed it to the pager all at once:

import os, sys

less = None
if os.isatty(sys.stdout.fileno()):
    less = subprocess.Popen(
        ['less', '-R', '--quit-if-one-screen'],
        stdin=subprocess.PIPE)
    os.dup2(less.stdin.fileno(), sys.stdout.fileno())

Now for the caveat:

less behaves as if -E has been passed and terminates when I reach the bottom of the output. I assume that's because, as the child of the script, it's dying when the script does, but adding the following to the end of the script just causes less to hang when it would otherwise have exited and I haven't had time to figure out why:

if less:
    less.stdin.close()  # no different with or without
    less.communicate()  # no different with less.wait()

(It wasn't an issue when I first came up with this hack because I originated it for making a PyGTK 2.x program pipe its output to grep to work around PyGTK not exposing the function from the GTK+ C API needed to silence some spurious log messages.)

Related