This seems pretty relevant: Accept explicit contextvars.Context in asyncio create_task() API · Issue #91150 · python/cpython · GitHub
Because said libraries might not even be aware of the framework around them?
Maybe they should be? I have no idea what libraries or actual concrete examples we are talking about here. I’ve glanced over the thread and discovered this comment by Sebastian, that actually suggested a very similar idea: Ability to change contextvars in sync dependencies · Issue #953 · tiangolo/fastapi · GitHub
Or am I not understanding something?
Perhaps you’re not taking into account that I’m not up to speed here and still trying to understand the actual problem. I’d appreciate actual examples of actual libraries (not links to mountains of code, but you explaining with simple English the actual scenarios).
The solution you want here:
Would be a major foot gun. IMO a better solution would be to add a special kind of context vars that interanally use the mutable container trick. Rereading this thread made me flip from -1 to +0 for doing that.
Hi Yury hopefully this helps give some context.
The general situation is:
- ASGI web framework uses threads and/or asyncio tasks
- Other libraries use contextvars
Examples of ASGI frameworks that have encountered these issues:
- FastAPI, although it’s inheriting the problem from it’s dependency Starlette, which I am a maintainer of)
- Django (via asgiref, although I’m not 100% sure how this impacts Django since I don’t use it or maintain it)
Examples of libraries using contextvars that would likely get used in a web app:
- structlog (issue in FastAPI)
- Probably others (I’ll have to do a bit of tracking down of issues)
Would you mind elaborating? I’m just looking to brainstorm ideas here, so understanding why the ideas proposed would not work out would be a really helpful outcome.
Sorry I missed this earlier. I’m sure it’s just me being bad at explaining things I don’t know to someone that really does
The point I was trying to make is that there are many different API options that would satisfy the use case. I’m making up arbitrary names for the concepts.
Back propagation
The trick implemented in Django and proposed in Starlette. The basic idea is to make a copy of the context (contextvar.copy_context()
) use that in the thread/task and then once execution returns to the parent thread/task we compare the state of the context from the child and parent and update the parent to match the child.
Choosing which context you execute in
This is along the lines of the new context
parameter to create_task()
in 3.11. I think we’d need an equivalent parameter for asyncio.to_thread
(asyncio.to_thread(sync_func, context=...)
) and a way to get the current context (I know you just said this is a bad idea). Then the web framework could do asyncio.create_task(..., context=contextvars.current_context())
.
Choosing if the context gets copied or not
Same as the above but instead of providing the current context you’d provide an explicit parameter: asyncio.create_task(..., copy_context=False)
.
Has the idea of using contextvars to hold a mutable container been tried?
In a process that has many initialization parameters. Some are set at startup from configparser however others are changed only after runtime based on various factors. For clarity of code and to avoid issues with threads setting different runtime vars we created a singleton class/object to hold all modified runtime parameters. Basically a key value list similar to how configparser holds data. With the option to lock it after all parameters are set. In order to share the parameters across the entire process and to allow each thread to access the same general parameters without threading conflicts the option to use configvars to hold the runtime variable class was raised. However we are having trouble figuring out what the proper way to implement using it would be - or if it’s even possible. Should the runtime parameters class be set once as a singleton on startup and the contextvars be included in the class? With contextvars holding a dictionary of key-values? Or create contextvars separately and have the runtime class access it - which would mean each file would need two imports one for contextvars and one for the class? Has anyone implementing something similar? Or is there a better way of managing this scenario without using contextvars?