contextvars
The Python contextvars module supports managing context state, allowing you to store and retrieve data that is local to a context, such as a specific asynchronous task or thread, without interfering with other contexts.
This module is particularly useful in asynchronous programming where different tasks might need to maintain their own separate states.
Here’s a quick example:
>>> import contextvars
>>> var = contextvars.ContextVar("var", default="Hello!")
>>> var.get()
'Hello!'
Key Features
- Enables definition of context-local variables
- Supports asynchronous programming by maintaining separate states for tasks
- Ensures context management that doesn’t interfere with other contexts
- Enables deterministic context switching in asynchronous tasks and callback
- Integrates with tracing and logging by propagating context variables implicitly
- Supports snapshotting or copying of execution context via
copy_context()
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
contextvars.ContextVar |
Class | Declares a new context variable that can have different values in different contexts |
contextvars.ContextVar.get() |
Method | Returns a value for the context variable for the current context |
contextvars.ContextVar.set() |
Method | Sets a new value for the context variable in the current context — returns a Token |
contextvars.ContextVar.reset() |
Method | Resets the context variable to the value it had before the set() that created the Token |
contextvars.Token |
Class | Represents a token returned by ContextVar.set() used to restore the prior value |
contextvars.Token.var |
Property | Points to the ContextVar object that created the token |
contextvars.Token.old_value |
Property | Holds the value the variable had before the ContextVar.set() that created the token |
contextvars.Context |
Class | Represents a mapping of ContextVar to their values and encapsulates context state |
contextvars.Context.run() |
Method | Enters the context, executes the given callable within it, then exits the context |
contextvars.copy_context() |
Function | Returns a shallow copy of the current context |
Examples
Copying and using a context:
>>> from contextvars import ContextVar, copy_context
>>> var = ContextVar("var", default="Hello!")
>>> var.set("Hello, World!")
>>> print("In original context before copy:", var.get())
>>> ctx = copy_context()
>>> # Modify the var in the original context after copying
>>> var.set("Hello, Pythonista!")
>>> print("In original context after set():", var.get())
>>> def show_var():
... print("Inside copied context run():", var.get())
...
>>> ctx.run(show_var)
>>> print("Back in original context:", var.get())
In original context before copy: Hello, World!
In original context after set(): Hello, Pythonista!
Inside copied context run(): Hello, World!
Back in original context: Hello, Pythonista!
Common Use Cases
- Storing request-specific data in web applications
- Maintaining task-local data in asynchronous programming
- Managing state in multithreaded applications without using thread-local storage
- Storing and retrieving request-scoped data in asynchronous web frameworks
- Preserving task-local state across coroutines without using globals or passing state explicitly
- Propagating logging and tracing metadata automatically throughout an execution context
- Overriding or mocking context state in testing environments in a controlled way
- Managing context when mixing threads and async tasks to avoid state bleeding
Real-World Example
When you’re handling multiple incoming user requests concurrently. Each request must maintain its own metadata, such as a unique request ID, without interfering with other requests. The contextvars module enables exactly this kind of isolation:
>>> import asyncio
>>> from contextvars import ContextVar
>>> request_id = ContextVar("request_id")
>>> async def handle_request(user):
... token = request_id.set(f"req-{user}")
... print(f"[{user}] Set request_id to:", request_id.get())
...
... await process_user_action(user)
...
... request_id.reset(token)
...
>>> async def process_user_action(user):
... print(
... f"[{user}] Processing action for request_id:",
... request_id.get()
... )
... await asyncio.sleep(0.1) # Simulate async work
... print(f"[{user}] Final for request_id:", request_id.get())
...
>>> async def main():
... users = ["alice", "bob", "carol"]
... tasks = [
... asyncio.create_task(handle_request(u)) for u in users
... ]
... await asyncio.gather(*tasks)
...
>>> asyncio.run(main())
[alice] Set request_id to: req-alice
[alice] Processing action for request_id: req-alice
[bob] Set request_id to: req-bob
[bob] Processing action for request_id: req-bob
[carol] Set request_id to: req-carol
[carol] Processing action for request_id: req-carol
[alice] Final for request_id: req-alice
[bob] Final for request_id: req-bob
[carol] Final for request_id: req-carol
In this example, contextvars allows each request to maintain its own request_id state without interfering with other requests.
By Leodanis Pozo Ramos • Updated Nov. 21, 2025