The goal is to pass some data (a few bytes) from a child process to a parent process in Python (stdout and stderr can’t be used). The following is a simplified version of it (the actual code reads all data from the pipe before blocking on the subprocess).
import os
import subprocess
import sys
CHILD_SCRIPT = r'''
import os
os.write({wfd}, b'Hello, World!')
'''
rfd, wfd = os.pipe()
if hasattr(os, 'set_inheritable'):
os.set_inheritable(wfd, True)
subprocess.check_call([sys.executable, '-c', CHILD_SCRIPT.format(wfd=wfd)])
print(os.read(rfd, 1024))
On Unix, it works on Python 2, but not on Python 3, where it fails with:
Traceback (most recent call last):
File "<string>", line 3, in <module>
OSError: [Errno 9] Bad file descriptor
Traceback (most recent call last):
File "test_inherit_fd.py", line 13, in <module>
subprocess.check_call([sys.executable, '-c', CHILD_SCRIPT.format(wfd=wfd)])
File "/usr/lib/python3.8/subprocess.py", line 364, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/bin/python3', '-c', "\nimport os\nos.write(5, b'Hello, World!')\n"]' returned non-zero exit status 1.
Apparently, the failure is unrelated to whether or not the FD is inheritable. On Python 3.2 (where FDs were still inheritable by default), it fails the same way. What else causes the difference? (EDIT: The reason is that, starting with Python 3.2, FDs above 2 are closed by default. The problem could be solved by passing close_fds=False
).
On Windows, it fails on Python 2 and Python 3.
What’s a robust and clean way to pass data (a few bytes) from the child to the parent, that works on Python 2 and Python 3 on all platforms?