anyio icon indicating copy to clipboard operation
anyio copied to clipboard

`get_coro` is missing a `None` check in `_task_started`

Open ZeroIntensity opened this issue 1 year ago • 1 comments

Things to check first

  • [X] I have searched the existing issues and didn't find my bug already reported there

  • [X] I have checked that my bug is still present in the latest release

AnyIO version

4.4.0

Python version

3.12

What happened?

Hello!

This was discovered upstream in CPython, see this issue. In AnyIO's _task_started function (see here), get_coro() can return None as of 3.12, with asyncio's new eager_task_factory.

Unfortunately, there's not all that much you can do about this at this very moment -- a bug in C caused this code to segfault on current versions (this is fixed now, but not released).

How can we reproduce the bug?

This was the reproducer used in the CPython issue, which eventually fails at _task_started. It's not all that minimal, sorry! I haven't used AnyIO enough to come up with my own reproducer.

import asyncio

import httpx
import uvicorn
from fastapi import FastAPI
from starlette.responses import StreamingResponse


async def main():
    loop = asyncio.get_running_loop()
    loop.set_task_factory(asyncio.eager_task_factory)

    app = FastAPI()

    @app.get("/")
    async def get():
        async def _():
            yield "1"

        return StreamingResponse(
            _(),
        )

    server = uvicorn.Server(
        uvicorn.Config(
            app,
            host="0.0.0.0",
            port=8080,
        )
    )
    asyncio.create_task(server.serve())

    client = httpx.AsyncClient()
    await client.get("http://localhost:8080")


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

Again, this code segfaults on 3.12.4, you'll need to wait for the next patch release for the fix. In the meantime, it might be a good idea to throw an exception if using AnyIO with eager_task_factory.

ZeroIntensity avatar Jul 27 '24 16:07 ZeroIntensity

It's bizarre that get_coro() can return None when the eager task has a reference to the coro

graingert avatar Jul 27 '24 16:07 graingert

What is the expected outcome here? Even if I patch _task_started() to check for None and return False then, it still crashes with CancelledError at asyncio/queues.py line 158 (await getter).

agronholm avatar Nov 10 '24 10:11 agronholm

This happens even if I comment out the httpx code.

agronholm avatar Nov 10 '24 11:11 agronholm

Also same if I comment out the set_task_factory() call.

agronholm avatar Nov 10 '24 11:11 agronholm