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.







