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:

Python
>>> 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:

Python
>>> 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:

Python
>>> 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