在Python 3.7中,asyncio 协程加入了对上下文的支持。使用上下文就可以在一些场景下隐式地传递变量,比如数据库连接session等,而不需要在所有方法调用显示地传递这些变量。使用得当的话,可以提高接口的可读性和扩展性。
基本使用方式
协和的上下文是通过 contextvars 中的 ContextVar 对象来管理的。最基本的使用方式是在某一调用层次中设置上下文,然后在后续调用中使用。如下例所示:
import asyncio import contextvars from random import randint from unittest import TestCase request_id_context = contextvars.ContextVar('request-id') async def inner(x): request_id = request_id_context.get() if request_id != x: raise AssertionError('request_id %d from context does NOT equal with parameter x %d' % (request_id, x)) print('start handling inner request-%d, with x: %d' % (request_id, x)) await asyncio.sleep(randint(0, 3)) print('finish handling inner request-%d, with x: %d' % (request_id, x)) async def outer(i): print('start handling outer request-%d' % i) request_id_context.set(i) await inner(i) print('finish handling outer request-%d with request_id in context %d' % (i, request_id_context.get())) async def dispatcher(): await asyncio.gather(*[ outer(i) for i in range(0, 10) ]) class ContextTest(TestCase): def test(self): asyncio.run(dispatcher())上例中,在最后定义了一个单元测试用例对象 ContextTest 。它的方法 test 是程序的入口,使用 asyncio.run 方法来在协程中执行被测试的异步方法 dispatcher 。dispatcher 则并发启动10个异步方法 outer 。 outer方法首先将在模块层定义的上下文变量 request_id_context 设置为当前调用指定的值,这个值对于每个 outer 的调用都是不同的。 然后在后续被调用的 inner 方法,以及 outer 方法内部访问了这个上下文变更。在 inner 方法内容,则比较了显示传入的 i 和从上下文变量中取出的 request_id 。
测试用例的执行结果如下:
start handling outer request-0 start handling inner request-0, with x: 0 start handling
