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

    Código en una pantalla que representa t-strings en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings en Python 3.14: templates estructurados

    Aprende t-strings en Python 3.14, explora Template e Interpolation y crea renderizadores estructurados, extensibles y seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    16/10/2026
    CPU y módulos de memoria que representan shared_memory track en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    shared_memory track: controla el resource tracker

    Aprende shared_memory track en Python para controlar el resource tracker, la propiedad y la limpieza entre procesos.

    Ler mais

    Tempo de leitura: 5 minutos
    16/10/2026
    Unidad de almacenamiento de datos que representa compression.zstd en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: streaming y compresión segura

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, diccionarios y límites seguros.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código en pantalla para diagnosticar procesos con sys.remote_exec en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys.remote_exec: diagnostica procesos Python

    Aprende sys.remote_exec en Python para diagnosticar procesos activos con scripts controlados, permisos seguros y buenas prácticas.

    Ler mais

    Tempo de leitura: 7 minutos
    15/10/2026
    Código Python y configuración de variables de entorno con os.reload_environ
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.reload_environ: threads y variables de entorno

    Aprende os.reload_environ en Python para actualizar variables de entorno externas con validación, compatibilidad y precauciones de concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    14/10/2026
    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