-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
Description
Bug report
Bug description:
Documentation on asyncio.subprocess.Process
(link) says:
the communicate() and wait() methods don’t have a timeout parameter: use the wait_for() function;
but in the meantime, cancelling this Future may result in stdout/stderr loss, which might be unclear from the documentation and not very user-friendly if the user ever retries communicating with the process again to collect the whole output.
So in general, using the synchronous API I would do it like this:
import sys
import subprocess
process = subprocess.Popen(
args=[
sys.executable,
'-c',
'import time; print("first", flush=True); time.sleep(3); print("second")',
],
stdout=subprocess.PIPE)
try:
[stdout, _] = process.communicate(timeout=1)
except subprocess.TimeoutExpired:
process.kill()
[stdout, _] = process.communicate()
print(f"Timed out, {stdout=}")
output:
Timed out, stdout=b'first\r\n'
which gives me the full process output. I think this is a common approach to using the subprocess API.
Doing the same in async will mostly lead to output loss:
import asyncio
import sys
async def main():
process = await asyncio.create_subprocess_exec(
sys.executable,
'-c',
'import time; print("first", flush=True); time.sleep(3); print("second")',
stdout=asyncio.subprocess.PIPE,
)
try:
[stdout, _] = await asyncio.wait_for(process.communicate(), timeout=1)
except asyncio.TimeoutError:
process.kill()
[stdout, _] = await process.communicate()
print(f"Timed out, {stdout=}")
if __name__ == '__main__':
asyncio.run(main())
output:
Timed out, stdout=b''
I researched the source code a bit and don't see a clear solution for this other than implementing a timeout
parameter for Process.communicate()
. Cancellation may occur right between those two calls:
cpython/Lib/asyncio/subprocess.py
Lines 201 to 202 in 93ac352
stdin, stdout, stderr = await tasks.gather(stdin, stdout, stderr) | |
await self.wait() |
so the transport buffer will already be drained, but the process is not yet .wait()
ed for.
PS I don't know if I chose the issue type correctly, since it might be a bug, a documentation issue, or an enhancement proposal.
CPython versions tested on:
3.11, 3.13
Operating systems tested on:
No response
Metadata
Metadata
Assignees
Labels
Projects
Status