user2300369
user2300369

Reputation: 298

Closing stdin in subprocess.Popen

Let's say I wrote following function. Now the problem is that another function uses the voronoi.out file as input. What I do not know is how to close this stdin after sp.Popen has finished. When I don't do it, qvoronoi program is "hanging" till the end of my script and next function cannot read this voronoi.out file.

def voronoi(self):

    ''' This function calls qvoronoi program to create 
file with vertices.'''

    self.inp=open(self.out_name, 'r') #Redirect input and output for qvoronoi script
    self.out=open('voronoi.out', 'w') 

    sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=self.inp, stdout=self.out) #Execute qvoronoi

    self.out.close() #Safely close output file

Upvotes: 3

Views: 5916

Answers (2)

bukzor
bukzor

Reputation: 38462

It's possible to close the child process' stdin before it starts:

from sys import stdin
import subprocess
subprocess.run(cmd, preexec_fn=stdin.close)

This won't close stdin, but it will ensure stdin is an empty file, plus it will work on Windows(R):

subprocess.run(cmd, stdin=subprocess.DEVNULL)

Both of these solutions lack the race condition present in other answers. Many commands check stdin at startup and alter their behavior, so to close stdin just after start can produce unpredictable results.

Upvotes: 2

tdelaney
tdelaney

Reputation: 77337

The primary problem is that you don't wait for the program to exit. qvoronio is still using the file when your function exits so others may be blocked. And since you don't wait, qvoronio remains in a "zombie" state when done executing, which may look like its hung. Also, you put the temporary inp/outp file handles on your self object, when really, you want to close them right away.

More zealous pythonistas like to use context managers to make sure files are closed properly, and may do something like this:

def voronoi(self):
    with open(self.out_name, 'r') as inp:
        with open('voronoi.out', 'w') as outp: 
            qvoronio = sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=inp, stdout=oup)
    qvoronio.wait()

Notice that the wait is done after the context managers exit. You've closed the files in the parent after handing them to the child.

I typically do:

def voronoi(self):
    qvoronio = sp.Popen(['./qvoronoi', 'p', 'FN'], stdin=open(self.out_name, 'r'),
        stdout=open('voronoi.out', 'w'))
    qvoronio.wait()

and suffer the wrath of the purists. My way works fine for cpython, but may jam you up in other variants.

Upvotes: 3

Related Questions