contextvars.Token: restaura contexto async

Publicado el: 14/10/2026
Tempo de leitura: 7 minutos
Código Python que representa contextvars.Token y aislamiento de contexto asíncrono

contextvars.Token representa el cambio producido cuando se asigna un valor a una ContextVar. Desde Python 3.14, el token también puede utilizarse como administrador de contexto, de modo que un valor temporal se instala con with y el estado anterior se restaura automáticamente al salir del bloque. Esta mejora resulta útil en servicios asíncronos, middleware, logs estructurados, rastreo de solicitudes, pruebas, localización y otros sistemas donde los datos de contexto deben acompañar una ejecución sin depender de variables globales inseguras.

Las variables de contexto mantienen valores aislados entre tareas concurrentes. En una aplicación con asyncio, varias corrutinas pueden ejecutarse en el mismo hilo y aun así ver valores distintos para la misma ContextVar. El token registra la información necesaria para deshacer un cambio específico, tanto si existía un valor anterior como si la variable nunca había sido definida en ese contexto.

Por qué las variables globales fallan

Una variable global puede parecer suficiente para guardar el identificador de una solicitud, el usuario actual, el tenant o un código de trazabilidad. Sin embargo, con concurrencia una solicitud puede sobrescribir el valor de otra. Los logs se mezclan, una comprobación de permisos puede usar una identidad equivocada y los errores son difíciles de reproducir. El almacenamiento local por hilo tampoco resuelve por completo el problema en servidores asíncronos, porque muchas tareas comparten el mismo hilo del sistema operativo.

ContextVar asocia el valor al contexto de ejecución actual. Python propaga ese contexto cuando se crean tareas, lo que permite que el código de infraestructura lea información contextual sin añadir un parámetro a cada función. Esto es especialmente práctico en frameworks web, consumidores de colas, bibliotecas de observabilidad, instrumentación de bases de datos y trabajos en segundo plano.

Uso tradicional con set y reset

from contextvars import ContextVar

request_id = ContextVar("request_id", default="sin-id")

def procesar():
    token = request_id.set("req-123")
    try:
        print(request_id.get())
    finally:
        request_id.reset(token)

procesar()
print(request_id.get())

El método set() devuelve un token. Ese token pertenece exactamente a la variable y al cambio que lo produjo. reset(token) restaura el estado previo. El bloque finally es esencial porque las excepciones, cancelaciones y salidas anticipadas no deben dejar el valor temporal activo para operaciones posteriores.

Token como administrador de contexto

En Python 3.14 o posterior, el token devuelto por set() implementa el protocolo de administrador de contexto. El código queda más corto y el alcance del cambio se entiende por la indentación.

from contextvars import ContextVar

usuario_actual = ContextVar("usuario_actual", default="anónimo")

with usuario_actual.set("ana"):
    print(usuario_actual.get())

print(usuario_actual.get())

Dentro del bloque, el valor es ana. Al terminar, se restaura automáticamente el valor anterior. Esta forma es adecuada para alcances breves, configuración de pruebas, middleware, flags temporales y adaptadores de bibliotecas.

Valores anidados

Los cambios pueden anidarse. Cada token restaura el estado que existía inmediatamente antes de su propio set().

nivel = ContextVar("nivel", default="info")

with nivel.set("debug"):
    print(nivel.get())
    with nivel.set("trace"):
        print(nivel.get())
    print(nivel.get())

print(nivel.get())

Los valores serán debug, trace, debug e info. Este comportamiento predecible evita que una capa externa restaure un valor incorrecto después de que un componente interno haya modificado temporalmente la misma variable.

Aislamiento con asyncio

import asyncio
from contextvars import ContextVar

correlation_id = ContextVar("correlation_id", default="-")

async def worker(nombre):
    with correlation_id.set(f"job-{nombre}"):
        await asyncio.sleep(0.01)
        print(nombre, correlation_id.get())

async def main():
    await asyncio.gather(worker("A"), worker("B"))

asyncio.run(main())

Aunque las tareas se intercalan, cada worker observa su propio identificador. No hace falta un bloqueo global ni pasar el valor manualmente por todas las funciones auxiliares. Aun así, las variables de contexto no deberían sustituir todos los parámetros explícitos. Los datos centrales del dominio suelen ser más claros y fáciles de probar cuando forman parte de la interfaz de la función.

Logs estructurados

Un patrón habitual consiste en incluir automáticamente el identificador de solicitud en cada registro de log.

import logging
from contextvars import ContextVar

request_id = ContextVar("request_id", default="-")

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

handler = logging.StreamHandler()
handler.addFilter(ContextFilter())
handler.setFormatter(logging.Formatter(
    "%(request_id)s %(levelname)s %(message)s"
))

logger = logging.getLogger("app")
logger.setLevel(logging.INFO)
logger.addHandler(handler)

Una capa de middleware puede establecer el ID una vez y todo el código ejecutado dentro de ese alcance obtiene el valor correcto. Al finalizar la solicitud, la restauración automática impide que el siguiente trabajo del mismo worker herede contexto obsoleto.

Middleware, excepciones y cancelación

async def middleware(handler, solicitud):
    identificador = solicitud.headers.get(
        "X-Request-ID", "generado-localmente"
    )
    with request_id.set(identificador):
        return await handler(solicitud)

El administrador restaura el estado incluso si el handler lanza una excepción o la tarea es cancelada. En servidores reales, los timeouts, errores de validación, cancelaciones y respuestas anticipadas forman parte del flujo normal y deben dejar el contexto limpio.

Reglas de seguridad del token

Un token solo puede restablecerse mediante la variable que lo creó. Tampoco puede reutilizarse después de haber restaurado el estado. Python rechaza operaciones inválidas para evitar una corrupción silenciosa del contexto. Conserva el token únicamente durante el tiempo necesario y prefiere un bloque with cuando el alcance sea local.

No transfieras tokens entre tareas. Una tarea hija puede heredar valores del contexto, pero el token describe una mutación concreta del contexto donde fue creado. Debe tratarse como un identificador temporal de restauración, no como dato general de la aplicación.

Token.MISSING

Si la variable no tenía un valor previo, token.old_value puede ser Token.MISSING. Esto es distinto de un valor anterior explícito igual a None.

from contextvars import ContextVar, Token

clave = ContextVar("clave")
token = clave.set(None)

if token.old_value is Token.MISSING:
    print("no existía un valor anterior")

clave.reset(token)

La diferencia permite restaurar con precisión el estado de variable no definida en vez de sustituirlo por un valor ordinario.

Compatibilidad con versiones anteriores

La sintaxis del token como administrador de contexto requiere Python 3.14 o posterior. Las bibliotecas que admiten versiones anteriores deben mantener el patrón explícito de set(), try, finally y reset(), o proporcionar una función auxiliar compatible.

from contextlib import contextmanager

@contextmanager
def valor_temporal(var, valor):
    token = var.set(valor)
    try:
        yield
    finally:
        var.reset(token)

Esta abstracción ofrece una experiencia casi idéntica y puede eliminarse cuando el proyecto adopte Python 3.14 como versión mínima.

Pruebas automatizadas

Los contextos temporales hacen que las pruebas sean independientes y expresivas. Cada prueba puede establecer un usuario, idioma, tenant o identificador sin dejar estado persistente.

def test_contexto_temporal():
    idioma = ContextVar("idioma", default="es")

    with idioma.set("en"):
        assert idioma.get() == "en"

    assert idioma.get() == "es"

También conviene probar rutas con excepciones para confirmar la restauración. En código asíncrono, crea tareas concurrentes con valores distintos y verifica que ninguna observa el contexto de otra. Evita pruebas dependientes del orden de ejecución salvo que ese orden sea parte del requisito.

Objetos mutables

Una variable de contexto aísla la referencia almacenada; no vuelve inmutable al objeto referenciado. Si varios contextos apuntan al mismo diccionario o lista, las mutaciones pueden seguir siendo visibles entre tareas. Cuando el aislamiento sea importante, prefiere valores inmutables, tuplas, identificadores, pequeñas clases de datos o nuevos objetos creados para cada contexto.

Rendimiento y arquitectura

ContextVar está diseñada para accesos frecuentes, pero añade una capa de indirección. Úsala para información transversal como trazabilidad, contexto de seguridad, localización y telemetría. No ocultes dependencias esenciales del dominio solo para reducir la cantidad de parámetros.

Las bibliotecas deberían documentar qué variables leen, quién debe definirlas y qué sucede cuando faltan. Los valores predeterminados seguros son útiles, pero inventar silenciosamente datos sensibles puede ser peligroso. En autenticación y autorización, falla de forma explícita si no existe el contexto requerido.

Copiar y ejecutar contextos

El módulo también ofrece copy_context(), que captura el contexto actual. Los frameworks avanzados pueden ejecutar una función dentro de un contexto seleccionado, aunque la mayoría de las aplicaciones debería confiar en la propagación normal de tareas. La copia manual es útil al conectar ejecutores personalizados o sistemas de callbacks, pero necesita pruebas de concurrencia porque la propiedad del contexto se vuelve menos evidente.

Buenas prácticas

Define las ContextVar a nivel de módulo, usa nombres descriptivos, elige valores predeterminados seguros, limita el alcance de los cambios y restaura siempre el estado. Prefiere with var.set(valor) en Python 3.14 y try/finally en versiones anteriores. Almacena valores pequeños e inmutables cuando sea posible y evita que un token sobreviva a la operación que lo creó.

Para seguir estudiando, consulta los artículos de Academify sobre programación asíncrona con asyncio, logging en Python, administradores de contexto y threads en Python. Como fuentes externas, revisa la documentación oficial de contextvars y la referencia oficial de with.

Conclusión

El soporte de administrador de contexto en contextvars.Token hace que los cambios temporales sean más claros y seguros. Reduce el código manual de limpieza, restaura el estado ante excepciones y conserva el aislamiento entre tareas asíncronas. Bien utilizado, mejora el rastreo de solicitudes, middleware, pruebas, localización y observabilidad. La regla arquitectónica principal es reservar el contexto para metadatos transversales y mantener explícitas las dependencias importantes del negocio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Visualización de datos y distribución estadística para statistics.kde_random en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    statistics.kde_random: genera muestras de una KDE

    Aprende statistics.kde_random en Python para generar muestras desde una densidad estimada por kernel con ancho de banda, semillas, validación y

    Ler mais

    Tempo de leitura: 6 minutos
    13/10/2026
    Entorno Linux que representa namespaces y automatización con os.setns en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.setns: entra en namespaces Linux con seguridad

    Aprende os.setns en Python para entrar en namespaces Linux con permisos, validación, pruebas y limpieza segura.

    Ler mais

    Tempo de leitura: 5 minutos
    13/10/2026
    Computadora con código que representa copia de archivos con Path.copy en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Path.copy: copia archivos y carpetas con seguridad

    Aprende Path.copy en Python para copiar archivos y directorios con seguridad, control, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    12/10/2026
    Código en un monitor que representa acceso a memoria nativa con ctypes.memoryview_at en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ctypes.memoryview_at: memoria nativa sin copias

    Aprende ctypes.memoryview_at en Python para acceder a memoria nativa sin copias, validar punteros y reducir riesgos.

    Ler mais

    Tempo de leitura: 7 minutos
    12/10/2026
    Código Python en pantalla representando EnumDict y enumeraciones avanzadas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: personaliza Enums con validación

    Aprende EnumDict en Python para personalizar EnumType, validar miembros y crear enumeraciones avanzadas con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/10/2026
    Rack de servidores que representa el balanceo de conexiones con SO_REUSEPORT_LB en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: reparte conexiones entre workers

    Aprende SO_REUSEPORT_LB en Python para distribuir conexiones entre workers con pruebas, portabilidad y cierre ordenado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026