Python already has communicate()
method implemented (it goes to A.py
, B.py
is fine). However it's just suitable for simple communication (you know what data are you going to send up front), if you need a more complex one like:
send data to process B
read stdout
if stdout ...
do something bases on stdout
write to stdin
You have to implement your own communicate()
, original implementation here.
Step by step
I've tested and debugged this step by step and here is what happens:
# For Popen(bufsize!=0)
A: process.stdin.write(b'hello\r\n')
B: line = sys.stdin.readline() # Hangs
So after adding bufsize=0
(unbuffered)
# Popen(bufsize=0)
A: process.stdin.write(b'hello\r\n') # Without \r\n B still hangs
B: line = sys.stdin.readline()
B: print('Send back', line.strip()) # Without strip it prints empty line
A: process.stdout.readline() # Hangs
So what works?
# Popen(bufsize=0)
A: process.stdin.write(b'hello\r\n')
B: line = sys.stdin.readline()
B: print('Send back', line.strip())
B: sys.stdout.flush()
A: process.stdout.readline()
Explained
You have buffering set to io.DEFAULT_BUFFER_SIZE
(it's usually 4090B). From docs:
bufsize will be supplied as the corresponding argument to the io.open() function when creating the stdin/stdout/stderr pipe file objects: 0 means unbuffered (read and write are one system call and can return short), 1 means line buffered, any other positive value means use a buffer of approximately that size. A negative bufsize (the default) means the system default of io.DEFAULT_BUFFER_SIZE will be used.
So at first A
won't flush because it hasn't filled its buffer yet and therefore B
is waiting. It's not possible to simply process.stdin.flush()
under Windows, so you have to use bufsize=0
.
Also writing os.linesep
(\r\n
) is important because of readline()
.
Note: I believe it should have worked also with bufsize=1
(line buffering) but it didn't. I have no idea why.
Then the same happens in B
when it won't flush sys.stdout
and it surprises me, that B:sys.stdout
is not set to unbuffered because:
bufsize will be supplied as the corresponding argument to the io.open() function when creating the stdin/stdout/stderr pipe file objects
Anyway, you have to call sys.stdout.flush()
in B
.
It works with close()
because it forces flush()
.
Gimme teh codes
A.py:
import subprocess
import sys
process = subprocess.Popen([sys.executable, r'B.py'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, bufsize=0)
for _ in range(3):
process.stdin.write(b'hello\r\n')
print(process.stdout.readline())
B.py:
import sys
for line in sys.stdin:
print('Send back', line.strip())
sys.stdout.flush()