Declarar compatibilidad con varias versiones requiere ejecutar la suite en cada una. tox crea entornos aislados y ejecuta comandos coherentes para pruebas, lint y tipado.

Crear tox.toml

requires = ["tox>=4.20"]
env_list = ["3.13", "3.12", "lint"]

[env_run_base]
description = "run tests"
deps = ["pytest"]
commands = [["pytest", "tests"]]

[env.lint]
skip_install = true
deps = ["ruff"]
commands = [["ruff", "check", "."]]

Ejecuta tox o tox -e 3.13. El intérprete correspondiente debe existir. Un entorno omitido no equivale a una prueba aprobada.

La guía de pytest ayuda a crear la suite y GitHub Actions para Python permite ejecutar la matriz completa.

Mantén los mismos comandos y restricciones en local y CI. El caché acelera descargas, pero cada entorno debe poder recrearse desde cero.

La documentación oficial de tox, consultada el 22 de julio de 2026, recomienda TOML para proyectos nuevos y explica entornos y matrices. La matriz debe representar versiones realmente mantenidas.

Qué aísla tox

Cada entorno de tox tiene su propio entorno virtual, dependencias y comandos explícitos. Esa separación impide que un paquete instalado globalmente haga pasar una prueba por accidente. En una ejecución, tox lee la configuración, localiza el intérprete solicitado, crea o reutiliza el entorno, instala el proyecto y sus dependencias y finalmente ejecuta los comandos.

El aislamiento resulta especialmente valioso en bibliotecas. El código puede funcionar en la versión de Python del desarrollador y fallar en otra por la sintaxis, las restricciones de dependencias o cambios en la biblioteca estándar. Las aplicaciones también se benefician: separar pruebas, lint y tipado permite reproducir cada fallo sin mezclar herramientas.

tox no descarga automáticamente todos los intérpretes de Python. Instala las versiones necesarias con las herramientas del sistema o deja que la imagen de CI las proporcione. Para ver y seleccionar entornos:

tox list
tox run -e 3.13

Lee el error antes de activar skip_missing_interpreters. Omitir versiones puede facilitar el trabajo local, pero la CI que respalda una promesa de compatibilidad debe fallar si falta un intérprete obligatorio.

Instalar el proyecto como lo recibe el usuario

Los entornos de ejecución normalmente instalan el paquete del proyecto. Así detectan defectos de empaquetado que un pytest lanzado desde el repositorio puede ocultar, como datos ausentes o imports que solo funcionan porque el directorio actual aparece en la ruta. Mantén los metadatos de construcción en pyproject.toml y prueba el artefacto instalado.

Usa skip_install = true únicamente en tareas que no importan el proyecto, por ejemplo una comprobación de formato. En una aplicación que deliberadamente no es un paquete, package = "skip" puede ser correcto. No lo uses para ocultar un error de construcción que debería corregirse.

Las dependencias exclusivas de pruebas pertenecen a deps. Si el proyecto mantiene un archivo de requisitos, tox puede instalarlo:

[env_run_base]
deps = [
  "-r requirements-test.txt",
]
commands = [
  ["pytest", "-q", { replace = "posargs", default = ["tests"], extend = true }],
]

Con esta configuración, tox -e 3.13 -- tests/test_api.py -x envía una selección concreta a pytest. El separador -- indica que los argumentos restantes pertenecen al comando y no a tox. Un valor predeterminado razonable simplifica la ejecución habitual sin dificultar el diagnóstico.

Separar responsabilidades

Una matriz de compatibilidad no necesita repetir todas las herramientas en cada versión. Prueba las versiones que el proyecto declara compatibles y ejecuta las verificaciones independientes en entornos con nombres claros:

env_list = ["3.11", "3.12", "3.13", "lint", "type"]

[env.lint]
skip_install = true
deps = ["ruff"]
commands = [["ruff", "check", "src", "tests"]]

[env.type]
deps = ["mypy"]
commands = [["mypy", "src"]]

Así, un fallo en type no se confunde con una incompatibilidad de Python 3.11. También se evita ejecutar el mismo lint tres veces. Si una herramienta debe importar o inspeccionar el proyecto instalado, no actives skip_install en ese entorno.

Los factores y nombres generados ayudan en matrices grandes, pero conviene comenzar con una configuración legible. Una pequeña repetición es preferible a una expresión compacta que nadie sabe interpretar con seguridad.

Recrear entornos y diagnosticar fallos

tox reutiliza entornos para acelerar las siguientes ejecuciones y los recrea cuando cambia una parte relevante de la configuración. Si sospechas del estado de las dependencias, fuerza una creación limpia:

tox run -r -e 3.13
tox run -e 3.13 -- -vv

El primer comando recrea el entorno. El segundo puede pasar mayor verbosidad a pytest si se configuró posargs. No borres directorios como primera reacción: la salida de tox identifica el intérprete seleccionado, el paso de instalación y el comando que falló.

Un error al crear el entorno es distinto de una prueba fallida. Comprueba la disponibilidad del intérprete, la construcción del paquete, la resolución de dependencias y la salida del comando, en ese orden. Si las pruebas pasan fuera de tox, compara las variables y el directorio de ejecución. Declara las variables necesarias y evita que las pruebas unitarias dependan de credenciales personales.

Elegir una estrategia para CI

La CI puede instalar varios intérpretes en una tarea y ejecutar tox, o crear una matriz y llamar a un entorno de tox en cada tarea. La segunda opción distribuye el trabajo e identifica inmediatamente la versión que falla. La primera mantiene más lógica de coordinación dentro de tox. Ambas funcionan si los mismos entornos pueden reproducirse en local.

No mantengas listas de compatibilidad distintas en CI y tox. Alinéalas con la política de soporte del proyecto. Añade una versión nueva de Python a la documentación solo después de verla pasar regularmente. Del mismo modo, retirar un entorno debe reflejar una decisión de soporte, no un intento de esconder un fallo difícil.

Usa caché únicamente si la clave considera la versión de Python, el sistema operativo y los archivos de dependencias. Una caché antigua puede producir resultados engañosos. Ejecuta periódicamente la matriz sin caché para demostrar que todos los entornos todavía pueden construirse desde cero.

Mantener una matriz confiable

Restringe dependencias cuando la reproducibilidad lo exija, pero conserva una tarea programada que pruebe versiones recientes permitidas. Así se descubren incompatibilidades antes de una actualización urgente. Mantén las pruebas ordinarias deterministas y sin red siempre que sea posible. Las integraciones con servicios externos merecen un entorno separado y una política explícita de credenciales.

Usa allowlist_externals con moderación. Los ejecutables del sistema aumentan las diferencias entre plataformas y deben existir tanto en local como en CI. Prefiere herramientas Python instaladas mediante deps. Cuando un programa externo sea imprescindible, documenta el requisito y verifica su presencia.

Trata la configuración de tox como código de producción. Revisa los cambios, ejecuta el entorno principal durante el desarrollo y exige la matriz completa antes de publicar una versión. Una matriz pequeña y comprendida aporta más confianza que muchos entornos inestables o ignorados.

Mantén la política de soporte alineada con esta matriz y con los metadatos del paquete. Revisa las tres fuentes al adoptar una versión nueva de Python o retirar una antigua. Así, una ejecución correcta representa una promesa deliberada de compatibilidad y no solamente una marca verde aislada.