Python Contextvars Module
The contextvars module provides APIs to manage, store, and access context-local state. The ContextVar class is used to declare and work with Context Variables.
This module was introduced in Python 3.7 and is designed to work correctly with asynchronous frameworks like asyncio, where multiple tasks might run concurrently in the same thread. Unlike threading.local(), which is thread-specific, contextvars are task-specific in an async environment.
Importing the Module
import contextvars
Context Variables
Declaration
Context variables should be declared at the top level of a module.
import contextvars
# Declare a context variable with a default value
request_id = contextvars.ContextVar('request_id', default=None)
Getting and Setting Values
get(): Returns the current value for the context variable. If no value has been set, returns the default value or raisesLookupError.set(value): Sets a new value for the context variable in the current context. Returns aTokenobject.
# Set a value
token = request_id.set('12345')
# Get the value
print(request_id.get()) # Output: 12345
Resetting Values
You can restore the variable to its previous value using the Token object returned by set().
# Reset to previous value
request_id.reset(token)
print(request_id.get()) # Output: None (default)
Usage with Asyncio
Context variables are natively supported in asyncio. When a new task is created, it inherits the context of its creator (a shallow copy). Changes in the new task do not affect the creator's context.
import asyncio
import contextvars
client_addr = contextvars.ContextVar('client_addr')
def render_goodbye():
# The address in the currently running Context
# is accessible directly
return f'Good bye, client @ {client_addr.get()}'
async def handle_request(addr):
client_addr.set(addr)
# sleep to simulate IO
await asyncio.sleep(1)
print(render_goodbye())
async def main():
await asyncio.gather(
handle_request('127.0.0.1'),
handle_request('192.168.0.1'),
)
# asyncio.run(main())
Manual Context Management
You can manually manage contexts using the Context class and copy_context().
import contextvars
var = contextvars.ContextVar('var')
var.set('spam')
def main():
var.set('ham')
ctx = contextvars.copy_context()
ctx.run(main)
print(var.get()) # Output: spam
print(ctx[var]) # Output: ham