Skip to content

Asyncio subprocess .returncode never gets set when busy #127661

Closed as not planned
Closed as not planned
@WillyJL

Description

@WillyJL

Bug report

Bug description:

When checking asyncio subprocess .returncode for example in a busy loop, it will never be set.

Minimal example:

import asyncio

async def main():
    code = """
import time, sys
time.sleep(1)
print('proc exits')
    """
    proc = await asyncio.create_subprocess_exec("python", "-c", code)
    while proc.returncode is None:
        pass
    print("proc.returncode:", proc.returncode)

if __name__ == "__main__":
    asyncio.run(main())

One would expect this code to wait 1 second, print proc exits, followed by proc.returncode: 0. However only proc exits is printed, meaning that the parent process's asyncio never detected the process exiting.

This is problematic for what I would imagine is a relatively common and useful usecase (as was for me finding this bug): using a loop to get output from a child process.

import asyncio
import subprocess

async def main():
    code = """
import time, sys
time.sleep(1)
print('proc exits')
    """
    proc = await asyncio.create_subprocess_exec("python", "-c", code, stdout=subprocess.PIPE)
    while proc.returncode is None:
        print("proc said:", await proc.stdout.readline())
    print("proc.returncode:", proc.returncode)

if __name__ == "__main__":
    asyncio.run(main())

This will print:

proc said: b'proc exits\r\n'
proc said: b''
proc said: b''
proc said: b''
proc said: b''
proc said: b''
proc said: b''
proc said: b''
proc said: b''
...

I noticed that adding a small await asyncio.sleep(0.1) before looping again will allow the returncode to be set, but this is not ideal. Especially considering the bug happens even when using await proc.stdout.readline() in the loop, which hints to the user that this is done efficiently and asynchronously. Having to instead think about leaving enough CPU time to the event loop to detect the process exiting is not ideal and doesn't seem pythonic to me.

For the time being I worked around this issue for my usecase by checking proc.stdout.at_eof() which seems to work fine.

Maybe when accessing .returncode which is a property, it could use some CPU time to see if an exit event is pending? Not sure how it is implemented under the hood so that might not even make sense.

CPython versions tested on:

3.11

Operating systems tested on:

Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibPython modules in the Lib dirtopic-asynciotype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions