New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lost writes when writing to a FileObject that is a pipe #1711
Comments
(EDIT TO ADD: I saw "3.7" in the reported Python version string and interpreted all of this as if on Python 3. But instead it was Python 2, installed from some repository version 3.7.) I argue that this example has a bug: while True:
c = inp.read(16*1024)
if not c:
break
cat.stdin.write(c) Much like #1706, this code is ignoring the fact that io.write(b) may not write all the bytes of
So when you use low-level IO like that, you may need to wrap it in a loop that makes write calls until the data is fully written: while True:
c = inp.read(16 * 1024)
if not c:
break
written = 0
to_write = len(c)
while to_write:
x = cat.stdin.write(c[written:])
to_write -= x
written += x Doing so fixes the problem. The documentation continues with weasel words:
This explains the difference between Python 2 and Python 3. In Python 2, subprocess streams are unbuffered by default, while in Python 3 they are buffered by default. The cat = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=out, bufsize=-1) EDITED to add: Why does this start showing up in gevent 1.5? There was a bug fix to the file object that made it actually start respecting the buffering arguments. Previously, it always buffered even when asked not to. (I suspect another bug is the scope of the |
Ah, I see what's happening. I still think gevent is at fault here, but for a different reason - it means that the The issue is that in python 2, calling
I should note that my original bug happened when I was using Just to confirm, I have this test program: import os
if os.environ.get('GEVENT'):
import gevent.monkey
gevent.monkey.patch_all()
import subprocess
cat = subprocess.Popen(['cat'], stdin=subprocess.PIPE)
ret = cat.stdin.write(b"foo\n")
print(repr(ret)) Here are the results with and without gevent, in python2 and python3:
So in python 2, using gevent changes the code from being required to not check the number of bytes written, to being required to check. I'm not sure what the best behaviour would be to do here from gevent. It could:
In any case, thanks for looking into this. Also about the |
User error on my end. Ignore please. |
…p to write all the data. More like what Python 2 standard library does. Beware of relying on that though during an upgrade. Fixes #1711
1.5a3
onwardsDescription:
I have an application that concatenates a number of input files and writes them to stdin of a subprocess.
I've been experiencing issues where the input is claimed to be corrupted.
By replacing the subprocess with
cat
, I was able to inspect the exact content being written, and found that data, sometimes a large amount of data, was missing.After further investigation, I've narrowed this down to an issue with the gevent FileObject api affecting all versions from
1.5a3
onwards, including the current version (20.9.0
) and latest master (b7f8787 at time of writing). Usinggit bisect
, I identified the commit where the behaviour begins to happen as 0dc161e (which makes sense, as this commit involves signifigant changes to the FileObject code).I have reduced the reproduction case to this:
Having multiple files to copy seems to be important for the bug to occur - I'm not sure why. Note that this code is entirely sequential, and indeed under normal python it runs correctly. It only breaks if I monkey patch subprocess.
When the bug occurs, it seems to take the form of having blocks of missing data in the output - in effect, some calls to
write()
are ignored. I have confirmed that the bug is when writing - if I count the amount of data that I read from the files, I get the correct size.The bug does not trigger every time, but seems to be more common under certain circumstances:
Steps to reproduce
I suggest doing the following in a new directory, as it will write a large number of files.
The generated files will contain lines of form
FFooooo
whereFF
is the file name andooooo
is the line number in hex. I found this useful for seeing exactly what data was missing since it was all regularly spaced and in order.1.5a2
:On my machine it generally takes up to 10 tries to trigger the bug. I have managed to trigger the bug even with only two files being read, but this required over 500 tries.
You can compare the expected output to actual output by looking at the
correct
andout
files respectively.The text was updated successfully, but these errors were encountered: