contextvars en Python: contexto async

Publicado el: 28/08/2026
Tempo de leitura: 5 minutos
Detailed close-up texture of a snake's patterned skin showcasing natural patterns and scales.

El módulo contextvars almacena información asociada al contexto actual de ejecución. A diferencia de una variable global, un ContextVar puede tener valores distintos en tasks asíncronas, threads y contextos copiados. Es útil para IDs de request, tracing, usuario actual, locale, transacciones y datos de observabilidad que deben acompañar una cadena de llamadas sin pasarse manualmente en cada función.

Las context variables no deberían sustituir parámetros explícitos para datos de negocio. Funcionan mejor para contexto transversal y controlado. Demasiado estado oculto dificulta tests y comprensión. Define quién configura el valor, cuándo se restaura y hasta dónde puede propagarse.

Crea un ContextVar

Decláralo normalmente a nivel de módulo.

from contextvars import ContextVar

request_id = ContextVar("request_id")

El nombre aparece en diagnósticos. Elige un identificador claro y estable.

Define y lee un valor

set() asocia un valor al contexto actual y get() lo recupera.

request_id.set("req-123")
print(request_id.get())

El valor no es una global compartida por todo el proceso; cada contexto puede tener su propia asociación.

Valores predeterminados

Se puede definir un default en el constructor.

locale_actual = ContextVar("locale_actual", default="es-ES")

Usa default solo cuando la ausencia sea válida. Para contexto obligatorio, omitirlo ayuda a detectar configuración faltante.

LookupError

Llamar get() sin valor ni default lanza LookupError.

try:
    identificador = request_id.get()
except LookupError:
    identificador = "sin-contexto"

No ocultes automáticamente el error cuando el contexto debería existir.

Tokens

set() devuelve un Token que representa el estado anterior.

token = request_id.set("req-456")
try:
    ejecutar()
finally:
    request_id.reset(token)

El patrón try/finally evita que el valor se filtre a trabajo posterior en el mismo contexto.

Restaura, no solo limpies

reset(token) restaura el estado anterior, que puede ser otro valor o ausencia.

Asignar None no es equivalente y puede destruir un contexto exterior que debería volver después.

Crea un context manager

Un wrapper vuelve reutilizable el patrón set/reset.

from contextlib import contextmanager

@contextmanager
def usar_request_id(valor):
    token = request_id.set(valor)
    try:
        yield
    finally:
        request_id.reset(token)

Consulta contextlib en Python.

Integración con asyncio

Las tasks normalmente reciben una copia apropiada del contexto actual al ser creadas.

import asyncio

async def worker(nombre):
    print(nombre, request_id.get())

async def main():
    token = request_id.set("req-main")
    try:
        await asyncio.gather(worker("a"), worker("b"))
    finally:
        request_id.reset(token)

Cada task puede cambiar su valor sin sobrescribir a las otras.

Momento de creación de la task

El instante en que una task se crea influye en el contexto capturado.

Define el valor antes de crearla cuando necesite heredarlo. Usa opciones explícitas de contexto disponibles en la versión objetivo cuando sea necesario.

No uses threading.local en async

threading.local() separa datos por thread, pero muchas tasks asíncronas comparten una sola thread.

ContextVar conserva aislamiento lógico entre esas tasks.

Threads

Cada thread tiene su propia pila de contextos. Los valores no aparecen automáticamente en una nueva thread.

Copia el contexto explícitamente o pasa los datos como argumentos.

copy_context

copy_context() crea una copia superficial del contexto actual.

from contextvars import copy_context

contexto = copy_context()
contexto.run(funcion)

Las asociaciones se copian, pero los objetos mutables usados como valores siguen siendo los mismos.

Propaga a un executor

Captura el contexto antes de enviar trabajo a una thread.

contexto = copy_context()
futuro = executor.submit(contexto.run, procesar, item)

No entres simultáneamente en el mismo Context desde varias threads. Crea una copia por envío.

Valores mutables

Guardar un diccionario o lista en un ContextVar no lo vuelve inmutable ni aislado.

Prefiere valores inmutables o copia antes de modificar. De lo contrario, varios contextos pueden compartir estado interno.

IDs de request

Un middleware puede definir el ID al entrar y restaurarlo al salir.

def atender(request):
    token = request_id.set(request.id)
    try:
        return procesar(request)
    finally:
        request_id.reset(token)

La asociación exterior vuelve incluso después de una excepción.

Logging

Filtros o adapters pueden leer el valor actual y añadirlo al registro.

class ContextFilter(logging.Filter):
    def filter(self, record):
        record.request_id = request_id.get("-")
        return True

No guardes contraseñas, tokens o datos personales innecesarios en el contexto de logging.

Tracing

Trace ID y span ID son ejemplos comunes de contexto transversal.

Las bibliotecas de observabilidad pueden usar contextvars internamente. Integra mediante su API pública para evitar fuentes de verdad duplicadas.

Locale y timezone

Una aplicación puede guardar locale o timezone por request.

Valida y restaura el valor. Las funciones puras con parámetros explícitos siguen siendo más fáciles de probar.

Transacciones

Un identificador o sesión puede ser contextual, pero conexiones, commit y rollback poseen lifecycle crítico.

No ocultes el cierre. Combina contextvars con context managers y ownership explícito.

Callbacks

El contexto de ejecución de un callback depende de cuándo y cómo fue registrado.

No supongas propagación en bibliotecas externas. Haz tests de integración o captura el contexto.

Background tasks

Una tarea en background no debería heredar todo el contexto de una request, especialmente credenciales y objetos grandes.

Crea un contexto limpio o copia únicamente los campos necesarios.

Context.run

Context.run(callable, ...) entra en el contexto, ejecuta la función y restaura el anterior.

resultado = contexto.run(funcion, argumento)

Las excepciones se propagan normalmente.

Inspecciona un contexto

Un objeto Context puede recorrerse como mapping.

for variable, valor in copy_context().items():
    print(variable.name, valor)

Clasifica sensibilidad antes de registrar valores.

Rendimiento

Las operaciones son eficientes para contexto transversal normal, pero no deberían sustituir variables locales en loops críticos.

Mide antes de añadir muchas lecturas en caminos muy calientes.

APIs públicas

Una biblioteca puede usar contextvars internamente, pero debe documentar cómo se establece y restaura el contexto.

No obligues a usuarios a manipular tokens privados.

Pruebas

Cada test debe establecer el contexto que necesita y restaurarlo después.

token = request_id.set("test")
try:
    assert ejecutar() == esperado
finally:
    request_id.reset(token)

Prueba tasks concurrentes con valores distintos para detectar filtraciones.

Aislamiento entre tests

Fixtures que olvidan resetear pueden contaminar casos posteriores en la misma thread.

Usa context managers o fixtures con cleanup garantizado.

Seguridad

El contexto implícito puede transportar identidad o autorización. Nunca confíes únicamente en ese valor sin validar la operación.

Reduce propagación a background jobs y no expongas todo el contexto en errores públicos.

Errores comunes

Los fallos frecuentes son usar globales o threading.local en asyncio, olvidar reset(), asignar None en vez de restaurar token, guardar objetos mutables, suponer propagación a threads y ocultar datos de negocio en contexto.

Conclusión

contextvars ofrece contexto local a la ejecución para threads y tasks async. Usa ContextVar para datos transversales, tokens para restaurar y copy_context() para propagación explícita.

Mantén valores pequeños, evita secretos, prueba aislamiento y usa parámetros explícitos para reglas de negocio. Consulta la documentación oficial de contextvars y contextlib en Python.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Person writing appointments on a calendar with a blue pen. High angle view.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sched en Python: programa eventos

    Aprende sched en Python para programar eventos, usar prioridades, cancelar tareas, crear recurrencia sin drift e integrar executors.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    bisect en Python: listas ordenadas

    Aprende bisect en Python para búsqueda binaria, inserción ordenada, duplicados, funciones key, rangos, rankings y concurrencia segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Crop unrecognizable person in gray sweater applying glue stick on paper while forming photo album on floor
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    heapq en Python: colas de prioridad

    Aprende heapq en Python para colas de prioridad, top-k, merge, empates estables, actualización de prioridades, lazy deletion y backpressure.

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up of hands using a compact black calculator on a white marble surface, displaying numbers.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    array en Python: números compactos

    Aprende array en Python para almacenar números compactos, usar typecodes, bytes, archivos, memoryview y layouts binarios portables.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of a man with binary code projected on his face, symbolizing cybersecurity.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    struct en Python: datos binarios

    Aprende struct en Python para empacar datos binarios, controlar endianness, offsets, padding, buffers, sockets y validación segura.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A young girl exploring a library's card catalog, symbolizes research and curiosity.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mmap en Python: archivos en memoria

    Aprende mmap en Python para mapear archivos, buscar bytes, editar regiones, compartir memoria, alinear offsets y sincronizar accesos.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026