在同一函数中设置和重置 ContextVar 失败 - 在不同的上下文中创建

问题描述 投票:0回答:1

我有这个功能:

async_session = contextvars.ContextVar("async_session")

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with async_session_maker() as session:
        try:
            _token = async_session.set(session)
            yield session
        finally:
            async_session.reset(_token)

失败并显示:

ValueError: <Token var=<ContextVar name='async_session' at 0x7e1470e00e00> at 0x7e14706d4680> was created in a different Context

怎么会发生这种事? AFAICT 更改

Context
的唯一方法是整个函数调用。 那么,在
yield
期间,上下文如何发生变化?

此函数被用作 FastAPI

Depends
,以防产生影响 - 但我看不出它是如何工作的。 它在 Python 3.8 下运行,FastAPI 的版本同样古老 - 0.54。

python python-3.x fastapi python-contextvars
1个回答
0
投票

如果生成器用作常规生成器,在我们控制的

for
循环中,真的很难思考 contextvars 的上下文如何改变。但框架可以做自己的事情:它可以将生成器存储在变量中,并从任意上下文调用其
__next__
方法。

实际上,ContextVars 甚至不打算与异步生成器一起工作 - 检查PEP 567的全文 - 它被重写,简化了PEP 550的原始提案,因为暂停帧和运行外帧的迭代代码变得太复杂,并且该功能被简单地删除了。

我有一个包可以使上下文变量的使用更简单 - “extracontext” - 并且我在那里支持异步生成器中的上下文变量。

无论哪种方式,我的建议是恢复“threading.local”命名空间,并且我确实向“contextvars”添加了“上下文管理器”功能。

你可以

pip install python-extracontext
。除了文档字符串以及 README 中嵌入的内容之外,我没有写太多文档 https://github.com/jsbueno/extracontext

有了这个软件包,您应该能够使用:

from extracontext import ContextLocal

# async_session = contextvars.ContextVar("async_session")
async_session_ns = ContextLocal()

async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
    async with (async_session_maker() as session, async_session_ns):
        with async_session_ns:
            async_session_ns.session = session
            # any code wanting this, should be able to use just
            # "async_session_ns.session" in any expression

            yield session

            # no finally blocks needed, as ContextLocal
            # works as a context manager.        

如果由于任何原因它对您不起作用,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.