Open rmorshea opened 1 day ago
vey interesting, I played a litttle with it, changing lifespan to the old way with on_startup / on_sutdown and the result is even weirder.
I didn't go through all this thread but that seems related https://github.com/pytest-dev/pytest-asyncio/issues/127
note on my "tests" I didnt use pytest-asyncio but anyio
It looks like this is expected behavior in Starlette/FastAPI. The response there is pretty turse so it's hard to see exactly why.
If this turns out to be a fundamental limitation it might be nice to support dependencies that have side effects but don't actually return a value. I could imagine something similar to Pytest's auto use fixtures:
VAR = ContextVar("VAR", default=0)
async def set_var() -> AsyncIterator[None]:
token = VAR.set(1)
try:
yield
finally:
VAR.reset(token)
@get("/", dependencies={"set_var": Provide(set_var, always_use=True)})
async def get_thing() -> None:
assert VAR.get() == 1
yep, pytest-asyncio might add another layer of complexity but this fails without regardless so the issue lies elsewhere,
now adding some goold old-style prints to this:
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from contextvars import ContextVar
from litestar import Litestar
from litestar import get
VAR = ContextVar("VAR", default=0)
print(f"1: {VAR}")
@asynccontextmanager
async def set_var() -> AsyncIterator[None]:
token = VAR.set(1)
try:
yield
# except Exception as exc:
# print(exc)
finally:
print("Finally")
VAR.reset(token)
@get("/example")
async def example() -> int:
print(VAR)
return VAR.get()
app = Litestar([example], lifespan=[set_var()], debug=True)
if __name__ == "__main__":
# asyncio.run(test())
print(f"1: {VAR}")
import uvicorn
uvicorn.run("cv:app", reload=True)
I get this log, note that there is something going on if I understand it clearly with the way the var is created / initialized as the handler seems to not use the same var that was created in lifespan
/home/lotso/PycharmProjects/abdul/.venv/bin/python /home/lotso/PycharmProjects/abdul/cv.py
1: <ContextVar name='VAR' default=0 at 0x7fb24500be20>
1: <ContextVar name='VAR' default=0 at 0x7fb24500be20>
INFO: Will watch for changes in these directories: ['/home/lotso/PycharmProjects/abdul']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [436898] using WatchFiles
1: <ContextVar name='VAR' default=0 at 0x7fabc7fedfd0>
1: <ContextVar name='VAR' default=0 at 0x7fabc57972e0>
INFO: Started server process [436904]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO - 2024-10-09 08:08:12,131 - watchfiles.main - main - 3 changes detected
<ContextVar name='VAR' default=0 at 0x7fabc57972e0>
INFO: 127.0.0.1:57262 - "GET /example HTTP/1.1" 200 OK
Here's a longer running issue: https://github.com/encode/uvicorn/issues/1151
Description
A
ContextVar
set within alifespan
context manager is not available inside request handlers. By contrast, doing the same thing via an application level dependency does appear set in the require handler.MCVE
Steps to reproduce
Run the example above. Either via
python example.py
oruvicorn example:app
and check the/example
route.We should expect the response to be
1
since that's what's set during thelifespan
context manager, but it's always 0.If you instead swap the
lifespan
context manager for a dependency it works as expected:Litestar Version
2.12.1
Platform