The following is pure speculation: Iterating over an input stream is likely implemented as reading characters into a buffer until either a newline or an end-of-file condition is encountered. If the child dies, some implementations (platform dependent) loose the remaining characters from the buffer.
Maybe using some more low-level I/O can avoid this issue. When I run the original script on my GNU/Linux system, I don't get the “foobar” but an IOError
instead. However, when I change it to
with os.fdopen(out) as istr:
sys.stdout.write(istr.read())
it prints “foobar” without throwing any exception.
Update: In order to read the stream one piece at a time, we'll need to resort to even more low-level I/O. I found that the following works for me:
import pty
import os
import sys
pid, out = pty.fork()
if pid:
while True:
buffsz = 10 # Use a larger number in production code.
buff = b''
died = False
try:
buff = os.read(out, buffsz)
except IOError:
died = True
sys.stdout.write(buff.decode())
if len(buff) == 0 or died:
break
else:
with sys.stdout:
# Also try writing a longer string with newlines.
sys.stdout.write("foobar")
Unfortunately, this means we'll need to reassemble the buffer chunks manually and scan for newlines. This is inconvenient but certainly can be done.