There is a program (which I cannot modify) that creates two output files. I am trying to write a Python wrapper that invokes this program, reads both output streams simultaneously, combines the output, and prints to stdout (to facilitate streaming). How can I do this without deadlocking? The following proof of concept below works fine, but when I apply this approach to the actual program it deadlocks.
Proof of concept: this is a dummy program, bogus.py
, that creates two output files like the program I'm trying to wrap.
#!/usr/bin/env python
from __future__ import print_function
import sys
with open(sys.argv[1], 'w') as f1, open(sys.argv[2], 'w') as f2:
for i in range(1000):
if i % 2 == 0:
print(i, file=f1)
else:
print(i, file=f2)
And here is the Python wrapper that invokes the program and combines its two outputs (interleaving 4 lines from each at a time).
#!/usr/bin/env python
from __future__ import print_function
from contextlib import contextmanager
import os
import shutil
import subprocess
import tempfile
@contextmanager
def named_pipe():
"""
Create a temporary named pipe.
Stolen shamelessly from StackOverflow:
http://stackoverflow.com/a/28840955/459780
"""
dirname = tempfile.mkdtemp()
try:
path = os.path.join(dirname, 'named_pipe')
os.mkfifo(path)
yield path
finally:
shutil.rmtree(dirname)
with named_pipe() as f1, named_pipe() as f2:
cmd = ['./bogus.py', f1, f2]
child = subprocess.Popen(cmd)
with open(f1, 'r') as in1, open(f2, 'r') as in2:
buff = list()
for i, lines in enumerate(zip(in1, in2)):
line1 = lines[0].strip()
line2 = lines[1].strip()
print(line1)
buff.append(line2)
if len(buff) == 4:
for line in buff:
print(line)