Nox automatiza tareas en entornos aislados mediante noxfile.py. Cada sesión instala dependencias y ejecuta comandos, reduciendo diferencias entre el equipo y CI.

Definir sesiones

python -m pip install nox
import nox


@nox.session(python=["3.12", "3.13"])
def tests(session: nox.Session) -> None:
    session.install(".", "pytest")
    session.run("pytest", "-q", *session.posargs)


@nox.session
def lint(session: nox.Session) -> None:
    session.install("ruff")
    session.run("ruff", "check", ".")

Ejecuta todo con nox, lista sesiones con nox -l o elige nox -s lint. session.posargs reenvía filtros sin editar el archivo.

Dependencias reproducibles

No instales versiones distintas localmente y en CI. Usa el lock del proyecto y haz que las sesiones consuman esa fuente. Reutilizar entornos acelera el trabajo, pero CI debe demostrar una instalación limpia.

Compara tox para varias versiones y usa uv si gestiona dependencias. Evita comandos divergentes entre scripts y CI.

La documentación oficial de Nox, consultada el 22 de julio de 2026, cubre sesiones, parametrización y entornos. Fija versiones críticas y revisa cambios antes de actualizar.

Entender el aislamiento

Cada sesión crea un entorno virtual. session.install() instala herramientas; session.run() ejecuta un programa y falla con un código distinto de cero. Nox no sustituye pytest, Ruff o Sphinx: los coordina. Define python= cuando el intérprete es parte de la prueba. Instala todas las versiones de la matriz en CI y no cuentes una sesión omitida como aprobada.

Instalar como un usuario

Prueba el paquete instalado, no solo archivos visibles en el directorio:

import nox


@nox.session(python=["3.11", "3.12", "3.13"])
def tests(session: nox.Session) -> None:
    session.install(".[test]")
    session.run("pytest", "-q", *session.posargs)


@nox.session
def typecheck(session: nox.Session) -> None:
    session.install(".[typing]")
    session.run("mypy", "src")

Declara extras como test y typing en pyproject.toml. Así las sesiones usan la fuente de dependencias del equipo. En bibliotecas, una instalación normal revela archivos ausentes del wheel que un ajuste de PYTHONPATH ocultaría.

Pasar argumentos y medir cobertura

Todo lo escrito después de -- llega a session.posargs. nox -s tests -- tests/test_api.py -k login elige un subconjunto sin editar la configuración:

@nox.session
def coverage(session: nox.Session) -> None:
    session.install(".[test]")
    args = session.posargs or ["tests"]
    session.run("coverage", "run", "-m", "pytest", *args)
    session.run("coverage", "report", "--fail-under=90")

No construyas una cadena de shell con la entrada. Los argumentos separados evitan escapes específicos de la plataforma.

Parametrizar con un propósito

@nox.session(python=["3.12", "3.13"])
@nox.parametrize("django", ["4.2", "5.1"])
def compatibility(session: nox.Session, django: str) -> None:
    session.install(".", f"django~={django}.0", "pytest")
    session.run("pytest", "tests/compat")

Evita un producto cartesiano accidental. Cada combinación cuesta instalación y tiempo. Elige versiones mínima y máxima soportadas, documenta la política y reserva una matriz mayor para tareas programadas.

Reutilización, documentación y build

nox -r reutiliza entornos y acelera el ciclo local, pero no demuestra que desaparecieron dependencias retiradas ni que funciona una instalación limpia. CI debe crear entornos nuevos regularmente. Cachear descargas es distinto de restaurar todo el entorno virtual.

@nox.session
def docs(session: nox.Session) -> None:
    session.install(".[docs]")
    session.run("sphinx-build", "-W", "docs", "build/docs")


@nox.session
def build(session: nox.Session) -> None:
    session.install("build")
    session.run("python", "-m", "build")

-W convierte avisos de documentación en fallos. Mantén la publicación fuera de las sesiones predeterminadas: subir cambia estado externo y requiere credenciales, revisión y una fuente fiable.

Usar el mismo flujo en CI

La pipeline debe llamar nox -s tests en vez de copiar instalación y pruebas en YAML. CI puede elegir la matriz, mientras la lógica permanece en noxfile.py. Usa nox -l para revisar nombres y descripciones.

Fija versiones según la política del proyecto y conserva sesiones pequeñas, con entradas y salidas claras. Esto permite reproducir localmente el mismo fallo visto en integración continua. Ante resultados incoherentes, recrea el entorno antes de culpar al código.

Comandos externos y variables

Prefiere programas instalados dentro de la sesión. Cuando una herramienta deba venir del sistema, declara esa dependencia de forma visible en vez de silenciar avisos. Así evitas una aprobación causada por un ejecutable inesperado en una máquina.

Entrega solo las variables necesarias. Tokens y contraseñas no pertenecen a noxfile.py; CI debe exponerlos únicamente a la sesión autorizada. Pon publicación o acciones destructivas en una sesión separada, fuera del conjunto predeterminado, y comprueba una condición de release.

Mantener sesiones observables

Usa nombres descriptivos y códigos de salida fiables. No captures un fallo solo para continuar y terminar con éxito. Si un paso opcional puede fallar, explica la razón en el log. La automatización debe mostrar comando, versión de Python y entradas no sensibles.

Revisa noxfile.py como código de producción: aplica formato, lint y pruebas a helpers complejos. Extrae una función pequeña cuando haya repetición, pero conserva un flujo fácil de seguir.

Añade descripciones a las sesiones públicas para que nox -l funcione como una guía breve. Elimina sesiones obsoletas en lugar de mantener alias indefinidamente, y revisa que los comandos funcionen desde un clon limpio sin configuración personal.