Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persistent python subprocess

Is there a way to make a subprocess call in python "persistent"? I'm calling a program that takes a while to load multiple times. So it would be great if I could just leave that program open and communicate with it without killing it.

The cartoon version of my python script looks like this:

for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myoutputtext, err = myprocess.communicate(input=text)

I need to process each text separately, so joining it all into one large text file and processing it once is not an option.

Preferably, if there's an option like this

myprocess = subprocess.Popen(["myexecutable"],
            stdin = subprocess.PIPE, stdout = subprocess.PIPE,
            stderr = None)    for text in textcollection:
for text in textcollection:
    myoutputtext, err = myprocess.communicate(input=text)

where I can leave the process open, I'd really appreciate it.

like image 620
JasonMond Avatar asked Jan 23 '12 23:01

JasonMond


People also ask

How do you keep subprocess alive in Python?

Use the standard subprocess module. You use subprocess. Popen() to start the process, and it will run in the background (i.e. at the same time as your Python program). When you call Popen(), you probably want to set the stdin, stdout and stderr parameters to subprocess.

Is Popen asynchronous?

Popen. Run subprocesses asynchronously using the subprocess module.

Is subprocess thread safe Python?

subprocess. Process class is not thread safe. The Concurrency and multithreading in asyncio section.

What is pipe in Python subprocess?

These arguments are used to set the PIPE, which the child process uses as its stdin and stdout. The subprocess. PIPE is passed as a constant so that either of the subprocess. Popen() or subprocess. PIPE the user specifies that they want the resultant.


1 Answers

You can use myprocess.stdin.write() and myprocess.stdout.read() to communicate with your subprocess, you just need to be careful to make sure you handle buffering correctly to prevent your calls from blocking.

If the output from your subprocess is well-defined, you should be able to reliably communicate with it using line-buffering and myprocess.stdout.readline().

Here is an example:

>>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> p.stdin.write('hello world\n')
>>> p.stdout.readline()
'hello world\n'
>>> p.stdout.readline()        # THIS CALL WILL BLOCK

An alternative to this method for Unix is to put the file handle in non-blocking mode, which will allow you to call functions like myprocess.stdout.read() and have it return data if any is available, or raise an IOError if there isn't any data:

>>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> import fcntl, os
>>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
0
>>> p.stdout.read()         # raises an exception instead of blocking
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 11] Resource temporarily unavailable

This would allow you to do something like this:

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
for text in textcollection:
    myprocess.stdin.write(text + '\n')
    while True:
        myoutputtext = ''
        try:
            myoutputtext += myprocess.stdout.read()
        except IOError:
            pass
        if validate_output(myoutputtext):
            break
        time.sleep(.1)    # short sleep before attempting another read

In this example, validate_output() is a function you would need to write that returns True if the data you have received so far is all of output that you expect to get.

like image 146
Andrew Clark Avatar answered Sep 18 '22 12:09

Andrew Clark