Issue 33319: subprocess.run documentation doesn't tell is using stdout=PIPE safe (original) (raw)

I'm porting old scripts from Python 2.7 to 3.6 and plan to change subprocess.call() to subprocess.run() at the same time. When using call() I've used tempfile.TemporaryFile as stdout because it's documentation has this warning:

Note: Do not use stdout=PIPE or stderr=PIPE with this function. The child process will block if it generates enough output to a pipe to fill up the OS pipe buffer as the pipes are not being read from.

Interestingly there is no such note in the docs of run(), and based on my (possibly inadequate) testing I couldn't get it to hang either. I'm still somewhat worried about using stdout=PIPE with it because the docs don't explicitly say it would be safe. I'm especially worried because the docs of call() nowadays say that it's equivalent to run(...).returncode. If that's the case, then I would expect the warning in call() to apply also to run(). Or is the warning nowadays outdated altogether?

If the goal is just to suppress stdout, that's what passing subprocess.DEVNULL is for (doesn't exist in Py2, but opening os.devnull and passing that is a slightly higher overhead equivalent).

subprocess.run includes a call to communicate as part of its default behavior, and stores its results, so call() isn't quite equivalent to run().returncode when PIPE was passed for standard handles, because call only includes an implicit call to wait, not communicate, and therefore pipes are not explicitly read and can block.

Basically, subprocess.run is deadlock-safe (because it uses communicate, not just wait), but if you don't care about the results, and the results might be huge, don't pass it PIPE for stdout/stderr (because it will store the complete outputs in memory, just like any use of communicate with PIPE).

The docs effectively tell you PIPE is safe; it returns a CompletedProcess object, and explicitly tells you that it has attributes that are (completely) populated based on whether capture was requested. If it had such attributes and still allowed deadlocks, it would definitely merit a warning.

My goal is to read stdout. It's good to hear subprocess.run() is deadlock-safe and I can use it safely. Making the docs explicit about it so that others know it's safe would in my opinion be a good idea as well.

Casual users don't know run() it uses communicate(), not wait(), internally, or even that this would mean it cannot deadlock. The current situation when the docs say that call() shouldn't be used with stdout=PIPE and that call(...) is equivalent to run(...).returncode indicates stdout=PIPE is unsafe with run() as well.

A separate questions is that if call(...) is equivalent to run(...).returncode, should it also be implemented that way. Based on this discussion it would avoid the problem with stdout=PIPE also in that case.