跳到主要内容

2017-0567-Context-Variables

https://peps.python.org/pep-0567/

Abstract

This PEP proposes a new contextvars module and a set of new CPython C APIs to support context variables. This concept is similar to thread-local storage (TLS), but, unlike TLS, it also allows correctly keeping track of values per asynchronous task, e.g. asyncio.Task

Introduction

The PEP proposes a new mechanism for managing context variables. The key classes involved in this mechanism are contextvars.Context and contextvars.ContextVar

The proposed mechanism for accessing context variables uses the ContextVar class. A module (such as decimal) that wishes to use the new mechanism should:

  • declare a module-global variable holding a ContextVar to serve as a key;
  • access the current value via the get() method on the key variable;
  • modify the current value via the set() method on the key variable

Instead, there is the notion of the “current Context” which is stored in thread-local storage. Manipulation of the current context is the responsibility of the task framework, e.g. asyncio

It is not possible to get a direct reference to the current Context object, but it is possible to obtain a shallow copy of it using the contextvars.copy_context() function

Specification

contextvars.Context

Context.run() raises a RuntimeError when called on the same context object from more than one OS thread, or when called recursively

Context objects implement the collections.abc.Mapping ABC. This can be used to introspect contexts

asyncio

def call_soon(self, callback, *args, context=None):
if context is None:
context = contextvars.copy_context()

# ... some time later
context.run(callback, *args)
class Task:
def __init__(self, coro):
...
# Get the current context snapshot.
self._context = contextvars.copy_context()
self._loop.call_soon(self._step, context=self._context)

def _step(self, exc=None):
...
# Every advance of the wrapped coroutine is done in
# the task's context.
self._loop.call_soon(self._step, context=self._context)
...