aiocontextvars

raw JSON →
0.2.2 verified Thu Apr 16 auth: no python deprecated

aiocontextvars provides asyncio support for the PEP-567 contextvars backport, effectively offering 'task local' storage similar to 'threading.local' but scoped to asyncio tasks. It is primarily relevant for Python versions 3.5 and 3.6, as Python 3.7 and later include native `contextvars` support. The project is currently at version 0.2.2 and is explicitly marked for deprecation once native asyncio contextvars support is fully stable in older backports.

pip install aiocontextvars
error LookupError: 'ContextVar' has no value in this context
cause Attempting to retrieve the value of a `ContextVar` using `var.get()` before a value has been set in the current execution context.
fix
Ensure var.set(value) is called at least once in the current context (or a parent context if inherited) before calling var.get(). You can also provide a default value when initializing the ContextVar: my_variable = ContextVar('my_variable', default='initial_value').
error AttributeError: 'ContextVar' object has no attribute 'delete'
cause Attempting to use the `delete()` method on a `ContextVar` object, which was removed in version 0.2.0.
fix
Replace var.delete() with var.reset(token) where token was the result of the var.set() call you wish to revert. If no token is available, you cannot 'delete' a value, only reset to a prior state.
error Context not propagating correctly with uvloop or custom task factories.
cause `aiocontextvars` patches `asyncio.get_event_loop` and `loop.create_task`. Custom event loop implementations (like `uvloop`) or directly creating `asyncio.Task` instances via private APIs might bypass these patches, leading to incorrect context propagation.
fix
Always use public asyncio APIs (e.g., asyncio.run(), asyncio.create_task(), asyncio.get_event_loop()) and ensure aiocontextvars is imported at startup. If using uvloop, consider if aiocontextvars is the right solution, as uvloop might need specific integration not provided by this library.
deprecated This package is explicitly marked for deprecation. For Python 3.7+ environments, `contextvars` is built into the standard library and should be used directly. This library's primary purpose is a backport for Python 3.5/3.6.
fix Upgrade to Python 3.7+ and use `import contextvars` directly. For Python 3.5/3.6, continue using `aiocontextvars` but be aware of its maintenance status.
breaking Version 0.2.0 introduced significant breaking changes. `ContextVar.delete()` was removed, `enable_inherit()` and `disable_inherit()` functions were removed (inheritance is always enabled), and `ContextVar.set()` now returns a `Token` object which must be used with the newly added `ContextVar.reset(token)` to restore the previous value.
fix Update code to use `ContextVar.set()` with `token = var.set(value)` and later `var.reset(token)`. Remove calls to deprecated `delete()`, `enable_inherit()`, and `disable_inherit()`.
gotcha For correct behavior (especially patching `asyncio.get_event_loop` and `loop.create_task`), `aiocontextvars` MUST be imported at the very beginning of your application, before any asyncio event loops are created. Loops created before the import will not be patched.
fix Ensure `import aiocontextvars` is one of the first lines of code executed in your application's entry point.
gotcha Contexts are copied when a new task is created (e.g., via `asyncio.create_task` or `asyncio.gather`). Changes made to `ContextVar` values in a parent task *after* a child task has been spawned will NOT be visible to that already-running child task, as it operates on a snapshot of the context at its creation time.
fix Design your application with this snapshot behavior in mind. If you need dynamic updates across tasks, consider passing data explicitly or using other synchronization primitives.
gotcha When using `loop.call_soon()` (or similar low-level scheduling functions) in Python 3.5/3.6 and expecting context inheritance, you must explicitly pass the context. The standard `call_soon` in these versions does not automatically copy the context.
fix Wrap your callable with `copy_context().run`. Example: `loop.call_soon(copy_context().run, my_method)`.

This quickstart demonstrates defining and using a `ContextVar` within an asyncio application. It shows how values set in a parent task are inherited by child tasks upon creation, and how to set and retrieve values using `ContextVar.set()` and `ContextVar.get()`. `ContextVar.reset()` is used with the token returned by `set()` to restore previous context.

import asyncio
from aiocontextvars import ContextVar

# Define a ContextVar
my_variable = ContextVar('my_variable', default='default_value')

async def child_task(task_id):
    current_val = my_variable.get()
    print(f"Task {task_id}: Initial value in child: {current_val}")
    my_variable.set(f"value_from_task_{task_id}")
    print(f"Task {task_id}: New value set in child: {my_variable.get()}")
    await asyncio.sleep(0.1)

async def main():
    print(f"Main: Initial value: {my_variable.get()}")
    token = my_variable.set("main_context_value")
    print(f"Main: Value after set: {my_variable.get()}")

    # Spawning child tasks - they inherit the context at creation
    tasks = [
        asyncio.create_task(child_task(1)),
        asyncio.create_task(child_task(2))
    ]
    await asyncio.gather(*tasks)

    print(f"Main: Value after children complete: {my_variable.get()}")
    my_variable.reset(token)
    print(f"Main: Value after reset: {my_variable.get()}")

if __name__ == '__main__':
    # IMPORTANT: aiocontextvars must be imported BEFORE creating event loops.
    # For Python 3.5/3.6, ensure aiocontextvars is imported early in your application.
    asyncio.run(main())