Segmentation fault with asyncio.eager_task_factory
and TestClient
(probably in AnyIO or asyncio)
#2890
-
DescriptionUsing Extra ContextOriginally reported in FastAPI here: fastapi/fastapi#11868 Minimal Reproducible Exampleimport asyncio
from contextlib import asynccontextmanager
from starlette.applications import Starlette
from starlette.testclient import TestClient
@asynccontextmanager
async def lifespan(_: Starlette):
loop = asyncio.get_event_loop()
loop.set_task_factory(asyncio.eager_task_factory)
yield
app = Starlette(lifespan=lifespan, debug=True)
def test():
with TestClient(app=app) as client:
print("this runs")
print("this doesn't run")
if __name__ == "__main__":
test() Extra ExamplesThe example above shows that it breaks on the Calling an EndpointCalling an endpoint seems to work, and then breaks again only in the exit code: import asyncio
from contextlib import asynccontextmanager
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from starlette.testclient import TestClient
@asynccontextmanager
async def lifespan(_: Starlette):
loop = asyncio.get_event_loop()
loop.set_task_factory(asyncio.eager_task_factory)
yield
async def endpoint(request: Request):
return JSONResponse({"message": "Hello World"})
app = Starlette(lifespan=lifespan, debug=True, routes=[Route("/tst", endpoint)])
def test():
with TestClient(app=app) as client:
print("this runs")
response = client.get("/tst")
print(response)
print("this doesn't run")
if __name__ == "__main__":
test() Calling an Endpoint with an AsyncClientThe problem is only with the regular This example works: import asyncio
from contextlib import asynccontextmanager
from httpx import AsyncClient
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
@asynccontextmanager
async def lifespan(_: Starlette):
loop = asyncio.get_event_loop()
loop.set_task_factory(asyncio.eager_task_factory)
yield
async def endpoint(request: Request):
return JSONResponse({"message": "Hello World"})
app = Starlette(lifespan=lifespan, debug=True, routes=[Route("/tst", endpoint)])
async def test():
async with AsyncClient(app=app, base_url="http://testserver") as client:
print("this runs")
response = await client.get("/tst")
print(response)
print("this also runs")
if __name__ == "__main__":
asyncio.run(test()) I suspect there's probably a way to reproduce it without involving Starlette, maybe with only AnyIO, or maybe even with asyncio, and the problem is a bug upstream. I suspect @graingert might be the right person to discover the problem, I understand he has been working with the new |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
The segfault should be fixed in cpython 3.12.5+ python/cpython#122332 but programs won't work until they upgrade to anyio 4.8.0 I'd highly recommend avoiding eager tasks as they were added without updating the CPython test suite to run with them and so a number of features in asyncio broke and are only working in very recent bug fix releases. |
Beta Was this translation helpful? Give feedback.
The segfault should be fixed in cpython 3.12.5+ python/cpython#122332 but programs won't work until they upgrade to anyio 4.8.0
I'd highly recommend avoiding eager tasks as they were added without updating the CPython test suite to run with them and so a number of features in asyncio broke and are only working in very recent bug fix releases.