如何 运行 上下文中的协程?
How to run a coroutine inside a context?
In the Python docs about Context Vars Context::run 方法被描述为能够在上下文中执行可调用,因此对上下文的可调用执行的更改包含在复制的上下文中。如果你需要执行协程怎么办?为了实现相同的行为,您应该怎么做?
在我的例子中,我想要的是这样的东西来处理具有可能的嵌套事务的事务上下文:
my_ctxvar = ContextVar("my_ctxvar")
async def coro(func, transaction):
token = my_ctxvar.set(transaction)
r = await func()
my_ctxvar.reset(token) # no real need for this, but why not either
return r
async def foo():
ctx = copy_context()
# simplification to one case here: let's use the current transaction if there is one
if tx_owner := my_ctxvar not in ctx:
tx = await create_transaction()
else:
tx = my_ctxvar.get()
try:
r = await ctx.run(coro) # not actually possible
if tx_owner:
await tx.commit()
except Exception as e:
if tx_owner:
await tx.rollback()
raise from e
return r
正如我已经指出的 ,context variables
由 asyncio
原生支持,无需任何额外配置即可使用。
需要注意的是:
- 当前任务通过
await
执行的oroutines 共享相同的上下文
create_task
生成的新任务在父任务上下文的副本 中执行。
因此,为了在当前上下文的副本中执行协程,您可以将其作为任务执行:
await asyncio.create_task(coro())
小例子:
import asyncio
from contextvars import ContextVar
var = ContextVar('var')
async def foo():
await asyncio.sleep(1)
print(f"var inside foo {var.get()}")
var.set("ham") # change copy
async def main():
var.set('spam')
await asyncio.create_task(foo())
print(f"var after foo {var.get()}")
asyncio.run(main())
var inside foo spam
var after foo spam
In the Python docs about Context Vars Context::run 方法被描述为能够在上下文中执行可调用,因此对上下文的可调用执行的更改包含在复制的上下文中。如果你需要执行协程怎么办?为了实现相同的行为,您应该怎么做?
在我的例子中,我想要的是这样的东西来处理具有可能的嵌套事务的事务上下文:
my_ctxvar = ContextVar("my_ctxvar")
async def coro(func, transaction):
token = my_ctxvar.set(transaction)
r = await func()
my_ctxvar.reset(token) # no real need for this, but why not either
return r
async def foo():
ctx = copy_context()
# simplification to one case here: let's use the current transaction if there is one
if tx_owner := my_ctxvar not in ctx:
tx = await create_transaction()
else:
tx = my_ctxvar.get()
try:
r = await ctx.run(coro) # not actually possible
if tx_owner:
await tx.commit()
except Exception as e:
if tx_owner:
await tx.rollback()
raise from e
return r
正如我已经指出的 context variables
由 asyncio
原生支持,无需任何额外配置即可使用。
需要注意的是:
- 当前任务通过
await
执行的oroutines 共享相同的上下文 create_task
生成的新任务在父任务上下文的副本 中执行。
因此,为了在当前上下文的副本中执行协程,您可以将其作为任务执行:
await asyncio.create_task(coro())
小例子:
import asyncio
from contextvars import ContextVar
var = ContextVar('var')
async def foo():
await asyncio.sleep(1)
print(f"var inside foo {var.get()}")
var.set("ham") # change copy
async def main():
var.set('spam')
await asyncio.create_task(foo())
print(f"var after foo {var.get()}")
asyncio.run(main())
var inside foo spam
var after foo spam