writing consecutive data to subprocess command 'more'

S

SanPy

I have this method that prints a given text via a subprocess command
'more' . It is like this:

def print_by_page(text):
if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
viewer = 'more -EMR'
proc = subprocess.Popen([viewer], shell=True,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
stdout, stderr = proc.communicate(text)
except OSError:
pass
else:
if stderr: # probably no 'more' available on this system
sys.stdout.write(text)
return
sys.stdout.write(text)

It works fine, but what I really want to do is first print a header
through the 'more' command, then fetch some data from the web, and
show it through the same 'more' command/process. And even more data,
another header, other data from the web, all through the same 'more'
command/process.
Can somebody help me out on this?

Regards,

Sander Smits
 
G

Gabriel Genellina

I have this method that prints a given text via a subprocess command
'more' . It is like this:

def print_by_page(text):
if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
viewer = 'more -EMR'
proc = subprocess.Popen([viewer], shell=True,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
try:
stdout, stderr = proc.communicate(text)
except OSError:
pass
else:
if stderr: # probably no 'more' available on this system
sys.stdout.write(text)
return
sys.stdout.write(text)

It works fine, but what I really want to do is first print a header
through the 'more' command, then fetch some data from the web, and
show it through the same 'more' command/process. And even more data,
another header, other data from the web, all through the same 'more'
command/process.
Can somebody help me out on this?

communicate writes to the child's stdin and waits for it to finish. If you
want to keep writing, don't use communicate. And you'll need to keep state
from one call to another, so use a class. Based on the code above, create
a class Pager with __init__, write and close methods:

class Pager:
def __init__(self):
# copy the logic above
self.proc = subprocess.Popen(...)
self.file = self.proc.stdin
# if something goes wrong, set self.proc=None and self.file=sys.stdout

def write(self, text):
self.file.write(text)

def close(self):
if self.proc:
self.file.close()
self.proc.wait()

Also, take a look at the pager function in the pydoc module (see the
source) - it handles several cases.
 
P

Piet van Oostrum

SanPy said:
S> I have this method that prints a given text via a subprocess command
S> 'more' . It is like this:
S> def print_by_page(text):
S> if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
S> viewer = 'more -EMR'
S> proc = subprocess.Popen([viewer], shell=True,
S> stdin=subprocess.PIPE,
S> stderr=subprocess.PIPE)
S> try:
S> stdout, stderr = proc.communicate(text)
S> except OSError:
S> pass
S> else:
S> if stderr: # probably no 'more' available on this system
S> sys.stdout.write(text)
S> return
S> sys.stdout.write(text)
S> It works fine, but what I really want to do is first print a header
S> through the 'more' command, then fetch some data from the web, and
S> show it through the same 'more' command/process. And even more data,
S> another header, other data from the web, all through the same 'more'
S> command/process.
S> Can somebody help me out on this?

You have to split this. Do the startup (creating the subprocess) once,
and then for each piece of text that you want to pass through 'more' do
stdout, stderr = proc.communicate(text)

By the way, in this last line, stdout should come out empty because the
output of more doesn't come back to the parent process, but goes to the
original stdout of the calling script.

Also I would use the following:
viewer = ['more', '-EMR']
proc = subprocess.Popen(viewer, stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
No reason to use a shell.

And if there is no 'more' available you get an exception on the Popen
already. You don't have to wait until the communicate call.
 
S

SanPy

Thanks, that works beautifully!

Regards,
Sander

En Sat, 02 May 2009 15:53:17 -0300, SanPy <[email protected]> escribió:




I have this method that prints a given text via a subprocess command
'more' . It is like this:
def print_by_page(text):
    if hasattr(sys.stdout, 'isatty') and sys.stdout.isatty():
        viewer = 'more -EMR'
        proc = subprocess.Popen([viewer], shell=True,
stdin=subprocess.PIPE,
            stderr=subprocess.PIPE)
        try:
            stdout, stderr = proc.communicate(text)
        except OSError:
            pass
        else:
            if stderr: # probably no 'more' available on this system
                sys.stdout.write(text)
            return
    sys.stdout.write(text)
It works fine, but what I really want to do is first print a header
through the 'more' command, then fetch some data from the web, and
show it through the same 'more' command/process. And even more data,
another header, other data from the web, all through the same 'more'
command/process.
Can somebody help me out on this?

communicate writes to the child's stdin and waits for it to finish. If you  
want to keep writing, don't use communicate. And you'll need to keep state  
 from one call to another, so use a class. Based on the code above, create  
a class Pager with __init__, write and close methods:

class Pager:
  def __init__(self):
   # copy the logic above
   self.proc = subprocess.Popen(...)
   self.file = self.proc.stdin
   # if something goes wrong, set self.proc=None and self.file=sys.stdout

  def write(self, text):
   self.file.write(text)

  def close(self):
   if self.proc:
    self.file.close()
    self.proc.wait()

Also, take a look at the pager function in the pydoc module (see the  
source) - it handles several cases.
 
S

SanPy

Hmm, it works as long as the pager command is available on the system.
I created a test file to explain what I mean. You can find it here:
http://gist.github.com/105880

The 'pager' command is on purpose a command that is not available on
the system.
It should fall back to sys.stdout in the write method. However, it
does not show any output at all.
Strangely enough, when a put a time.sleep(1) between the first and
second printer.write statements, the second and third statement do
appear.
Btw, the normal pager command works fine on my system.
How can I solve this, so that the fallback sys.stdout does show
output?
---
Sander

Thanks, that works beautifully!

Regards,
Sander

communicate writes to the child's stdin and waits for it to finish. If you  
want to keep writing, don't use communicate. And you'll need to keep state  
 from one call to another, so use a class. Based on the code above, create  
a class Pager with __init__, write and close methods:
class Pager:
  def __init__(self):
   # copy the logic above
   self.proc = subprocess.Popen(...)
   self.file = self.proc.stdin
   # if something goes wrong, set self.proc=None and self.file=sys.stdout
  def write(self, text):
   self.file.write(text)
  def close(self):
   if self.proc:
    self.file.close()
    self.proc.wait()
Also, take a look at the pager function in the pydoc module (see the  
source) - it handles several cases.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top