Mark Galeck
Mark Galeck

Reputation: 6405

how to pass a pipe() descriptor to Python subprocess.Popen(), in any version of Python

I want to spawn a child process in Python, whose standard streams are used for other purposes and I can't touch them, and I want to have a dedicated pipe for my own purposes, independent of standard streams.

In Python version 2, the following works:

foobar.py:

#!/usr/bin/python

from os import pipe, environ, read
import subprocess

output, input = pipe()
new_environ = environ.copy()
new_environ["PIPE_INPUT"] = str(input)
subprocess.Popen(['./a.out'], env=new_environ)
print(read(output, 6))

and a.out is compiled from foobar.c:

#include <unistd.h>
#include <stdlib.h>

void main() {
    write(atoi(getenv("PIPE_INPUT")), "foobar", 6);
}

This works, ./foobar.py prints foobar.

But, with the shebang line

#!/usr/bin/python3

it does not work. Of course, this is because, according to the docs, in Python 3 the descriptors are not inheritable.

Well, then how do I do this in Python 3, and also so that the same code works for Python 2. I cannot have different code for different Pythons, has to be portable code for Python 2 and 3.

Upvotes: 2

Views: 292

Answers (1)

Michael Butscher
Michael Butscher

Reputation: 10969

To make the input descriptor inheritable if necessary, use

import os

try:
    os.set_inheritable(input, True)
except AttributeError:
    # This is Python 3.3 or older -> Nothing to do
    pass

The Popen call must then be modified to

subprocess.Popen(['./a.out'], env=new_environ, close_fds=False)

which works on Python 2 and Python 3.

Upvotes: 1

Related Questions