Need feedback on subprocess-using function

G

gb345

I'm relatively new to Python, and I'm trying to get the hang of
using Python's subprocess module. As an exercise, I wrote the Tac
class below, which can prints output to a file "in reverse order",
by piping it through the Unix tac utility. (The idea is to delegate
the problem of managing the memory for an arbitrarily large task
to tac.)

class Tac(object):
def __init__(self, path):
out = open(path, 'w')
self.pipe = subprocess.Popen(['tac'], stdout=out,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
def prn(self, string):
try:
self.pipe.stdin.write('%s\n' % string)
except:
self.close()
raise

def close(self):
p = self.pipe
p.stdin.close()
err = p.stderr.read()
if err:
raise OSError(err)

This works OK, as far as I can tell, but I'm not sure that I've
dotted all the i's and crossed all the t's... E.g., I had to add
the line "p.stdin.close()" to the close method when I when I ran
into sporadic deadlock at the p.stderr.read() statement. Are there
other similar problems lurking in this code? Also, there's no
robust mechanism for invoking this close method in case of an
exception (unless the exception happens during the execution of
prn).

Any comments and suggestions would be greatly appreciated.

G
 
N

Nobody

I'm relatively new to Python, and I'm trying to get the hang of
using Python's subprocess module. As an exercise, I wrote the Tac
class below, which can prints output to a file "in reverse order",
by piping it through the Unix tac utility. (The idea is to delegate
the problem of managing the memory for an arbitrarily large task
to tac.)
self.pipe = subprocess.Popen(['tac'], stdout=out,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
This works OK, as far as I can tell, but I'm not sure that I've
dotted all the i's and crossed all the t's... E.g., I had to add
the line "p.stdin.close()" to the close method when I when I ran
into sporadic deadlock at the p.stderr.read() statement. Are there
other similar problems lurking in this code?

Yep. If the process writes more than a buffer-full of data to stderr, it
will deadlock. tac will block trying to write to stderr, and won't be
reading its stdin, so your program will block trying to write to tac.

This is why the POSIX popen() call only lets you attach a pipe to stdin or
stdout but not both.

If you want a "double-ended" slave process, you need to use polling or
non-blocking I/O or asynchronous I/O or multiple threads. I'm not aware of
any single solution which works on all platforms.

The easiest way around this problem is to redirect stderr to a temporary
file and read in the file's contents in the close() method.
 
R

ryles

I'm relatively new to Python, and I'm trying to get the hang of
using Python's subprocess module.  As an exercise, I wrote the Tac
class below, which can prints output to a file "in reverse order",
by piping it through the Unix tac utility.  (The idea is to delegate
the problem of managing the memory for an arbitrarily large task
to tac.)
        self.pipe = subprocess.Popen(['tac'], stdout=out,
                                     stdin=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
This works OK, as far as I can tell, but I'm not sure that I've
dotted all the i's and crossed all the t's...  E.g., I had to add
the line "p.stdin.close()" to the close method when I when I ran
into sporadic deadlock at the p.stderr.read() statement.  Are there
other similar problems lurking in this code?

Yep. If the process writes more than a buffer-full of data to stderr, it
will deadlock. tac will block trying to write to stderr, and won't be
reading its stdin, so your program will block trying to write to tac.

This is why the POSIX popen() call only lets you attach a pipe to stdin or
stdout but not both.

If you want a "double-ended" slave process, you need to use polling or
non-blocking I/O or asynchronous I/O or multiple threads. I'm not aware of
any single solution which works on all platforms.

The easiest way around this problem is to redirect stderr to a temporary
file and read in the file's contents in the close() method.

There is also Popen.communicate():

http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate
 
N

Nobody

There is also Popen.communicate():

That doesn't help if you want to write data incrementally.

You could always lift the code from Popen._communicate(), which uses
threads for Windows and select() for POSIX.
 
G

gb345

In said:
You could always lift the code from Popen._communicate(), which uses
threads for Windows and select() for POSIX.

Thanks. A lot of useful advice in your replies.

G.
 

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

Forum statistics

Threads
473,754
Messages
2,569,526
Members
44,997
Latest member
mileyka

Latest Threads

Top