如何在上下文中运行协程?

2024-04-08

在关于 Context Vars 的 Python 文档中 https://docs.python.org/3/library/contextvars.htmlContext::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

正如我已经指出的here https://stackoverflow.com/a/63131230/13782669, context variables本身支持asyncio无需任何额外配置即可使用。 应当指出的是:

  • 当前任务通过以下方式执行的协程await 分享相同的 context
  • 新产生的任务由create_task被执行在copy父任务上下文。

因此,为了在当前上下文的副本中执行协程,您可以将其作为任务执行:

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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在上下文中运行协程? 的相关文章

随机推荐