同一函数内ContextVar的set与reset失败:上下文不一致问题求助
问题:ContextVar重置时出现跨上下文错误
我编写了如下函数:
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
我对此感到困惑:据我所知,Context的唯一变更方式是触发完整函数调用,那yield期间上下文为何会发生变化?该函数被用作FastAPI的Depends依赖项,运行环境为Python 3.8及旧版FastAPI 0.54。
解决方案与分析
问题根源
在Python 3.8搭配旧版FastAPI(0.54)的环境中,FastAPI对生成器类型依赖的调度存在缺陷:请求处理过程中如果发生异步任务切换(比如异步IO操作挂起后被调度到其他事件循环线程),会导致yield前后的上下文环境不一致。而ContextVar的reset操作严格要求必须在创建Token的同一个上下文中执行,这就触发了跨上下文的报错。
修复方案
完全不需要手动调用async_session.reset(_token),Python的ContextVar本身就具备上下文自动恢复的特性。修改后的代码如下:
async_session = contextvars.ContextVar("async_session") async def get_async_session() -> AsyncGenerator[AsyncSession, None]: async with async_session_maker() as session: _token = async_session.set(session) yield session # 无需手动reset,上下文退出时自动恢复原有值
如果保留try-finally结构,只需移除reset调用即可:
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: pass # 移除reset调用
补充说明
Python 3.8及以上版本的ContextVar设计中,当你在某个上下文中调用set()获取到Token后,一旦该上下文结束(比如生成器执行完毕、异步任务完成),ContextVar会自动回滚到Token对应的旧值。旧版FastAPI的调度逻辑让finally块中的代码跑在了和set()不同的上下文,手动reset就触发了错误,去掉手动调用就能解决问题。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

