我有一个程序(ASGI 服务器),其结构大致如下:
import asyncio
import contextvars
ctxvar = contextvars.ContextVar("ctx")
async def lifepsan():
ctxvar.set("spam")
async def endpoint():
assert ctxvar.get() == "spam"
async def main():
ctx = contextvars.copy_context()
task = asyncio.create_task(lifepsan())
await task
task = asyncio.create_task(endpoint())
await task
asyncio.run(main())
因为生命周期事件/端点在任务中运行,所以它们不能共享上下文变量。这是设计使然:任务在执行之前复制上下文,因此lifespan
无法ctxvar
正确设置。这是端点的期望行为,但我希望执行看起来像这样(从用户的角度来看):
async def lifespan():
ctxvar.set("spam")
await endpoint()
换句话说,端点在它们自己的独立上下文中执行,但在生命周期的上下文中。
我试图通过使用来使它工作contextlib.copy_context()
:
import asyncio
import contextvars
ctxvar = contextvars.ContextVar("ctx")
async def lifepsan():
ctxvar.set("spam")
print("set")
async def endpoint():
print("get")
assert ctxvar.get() == "spam"
async def main():
ctx = contextvars.copy_context()
task = ctx.run(asyncio.create_task, lifepsan())
await task
endpoint_ctx = ctx.copy()
task = endpoint_ctx.run(asyncio.create_task, endpoint())
await task
asyncio.run(main())
也:
async def main():
ctx = contextvars.copy_context()
task = asyncio.create_task(ctx.run(lifespan))
await task
endpoint_ctx = ctx.copy()
task = asyncio.create_task(endpoint_ctx.run(endpoint))
await task
但是,这种方式似乎contextvars.Context.run
不起作用(我猜上下文是在创建协程时绑定的,但不是在执行时绑定的)。
有没有一种简单的方法来实现所需的行为,而无需重新构建任务的创建方式等?