Comments (9)
@florimondmanca I think you may be right, I'm using fastapi/starlette/uvicorn and andeed my context var setup needs tricky context saving/switching because lifespan is a separate call (from middleware's point of view).
from asgi-lifespan.
Hi,
Would this be a ASGI server/framework-dependant issue instead? Or do you mean that a no-server, plain asyncio code example produces this behavior as well?
Asking because I think I saw a similar discussion over either Uvicorn or Starlette repos — mostly likely Starlette, which would run startup handlers in a long running lifespan task, and handle requests in another task, leading to different contexts and more broadly a lack of contextvars support for lifespan in Starlette.
from asgi-lifespan.
Right. I think that’s the current state of things, so gently going to close this as being not related to ASGI-lifespan per se. Thanks :)
from asgi-lifespan.
I'm pretty sure I know what happens now:
I made a middelware that listens to lifespan events, and I'm using with fastapi/starlette/uvicorn.
It works fine in that config.
However, in the tests, I'm trying to use the context manager from this project.
And that does not work for me.
Key difference is that I await
something in my lifespan hook.
Both call the middleware __init__
the same,
Both call the middleware __call__
multiple times,
fastapi/starlette/uvicorn waits for lifespan.startup
processing to finish.
asgi-lifespan
does not wait for lifespan.startup
processing to finish.
This library kindof assumes that middleware lifespan processing is synchronous.
And in my code it's not.
Thus test requests arrive before my custom thing is actually set up.
from asgi-lifespan.
This library kindof assumes that middleware lifespan processing is synchronous.
I'm not sure this is accurate, as we do await
for an event that's set once we receive lifespan.startup.complete
, right?
asgi-lifespan/src/asgi_lifespan/_manager.py
Lines 29 to 33 in fbb0f44
I think at this point sharing a bit of minimal reproduction code would be super helpful to see if there's anything we should be doing differently.
In particular, you're mentioning "lifespan hook". asgi-lifespan
was made at a time when Starlette only had on_startup
/on_shutdown
parameters, and now there's a newer lifespan=<async gen>
style for which maybe we need to change something slightly to fully support?
from asgi-lifespan.
🤔 the code appears to do the right thing...
starlette lifecycle is indeed quite peculiar, and as @ojii pointed out (separately) my middleware may not be doing it right.
another possibility is that there's a subtle bug somewhere
from asgi-lifespan.
Hi, I've also been running into this issue 😢
I haven't dug into the code yet but it seems that startup and shutdown are being invoked in different contexts.
With this minimal example:
from fastapi import FastAPI
from contextvars import ContextVar
api = FastAPI()
ctx_var = ContextVar('mycontextvar')
async def lifespan(*args):
token = ctx_var.set(True)
try:
yield
finally:
ctx_var.reset(token)
api.router.lifespan_context = lifespan
The below exception occurs:
unhandled exception during asyncio.run() shutdown
task: <Task finished name='Task-6' coro=<<async_generator_athrow without __name__>()> exception=ValueError("<Token var=<ContextVar name='mycontextvar' at 0x7fe40ec69e50> at 0x7fe40ec78680> was created in a different Context")>
Traceback (most recent call last):
File "api.py", line 73, in lifespan
yield
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "api.py", line 75, in lifespan
ctx_var.reset(token)
ValueError: <Token var=<ContextVar name='mycontextvar' at 0x7fe40ec69e50> at 0x7fe40ec78680> was created in a different Context
from asgi-lifespan.
A few questions to get the investigation going…
- Is there an even simpler example to allow us to reproduce this? Does it reproduce without Starlette? (Manual ASGI lifespan app with this async context manager lifespan handler)
- Does it reproduce on other ASGI servers? Hypercorn?
If we know in what happens in these situations then we may be able to pinpoint where the problem might be.
Now that I think about it, I said at the beginning of this thread that lifespan is a long running task, but more precisely I believe that’s how Uvicorn does it. Still, this « token was created in a different Context » error seems different, even odd. I looked up Starlette code, and the lifespan_context there runs as a single async with call, no strange task stuff going on. Which strengthens my belief that we might want to look at how Uvicorn runs lifespan..
from asgi-lifespan.
It seems like ASGI lifespan state allows addressing this use case, by allowing to store state initialized in the lifespan task for use in other places in the app.
We just merged #57 which adds support for it in this package, so I'll close this now!
from asgi-lifespan.
Related Issues (16)
- Usage examples should be tested
- Testing use case guide HOT 3
- Can't use LifespanManager with pytest-asyncio HOT 2
- Remove dependency on anyio?
- Switch to register-on-init style for Lifespan event handlers
- Enforcing top-level imports
- Should we get rid of `Lifespan` and `LifespanMiddleware`? HOT 8
- Implement lifespan manager
- Rationale for strong dependency on starlette 0.13? HOT 4
- Implement lifespan middleware
- Handling `lifespan.shutdown.failed` ASGI messages HOT 1
- Test fail on FreeBSD HOT 1
- Error reproducing an example from the REAME.md HOT 3
- Support for lifespan state HOT 10
- Implement lifespan app
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from asgi-lifespan.