Nox automatiza tarefas em ambientes isolados usando um noxfile.py. Cada sessão instala dependências e executa comandos, reduzindo diferenças entre a máquina do desenvolvedor e a CI.

Definir sessões

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", ".")

Execute tudo com nox, liste sessões com nox -l ou escolha nox -s lint. session.posargs permite repassar filtros sem editar o arquivo.

Dependências reproduzíveis

Evite instalar versões diferentes localmente e na CI. Use o mecanismo de lock adotado pelo projeto e faça a sessão consumir a mesma fonte. Reutilizar ambientes acelera a execução, mas uma CI limpa ainda deve provar que a instalação começa do zero.

Compare com tox para múltiplas versões e use uv quando ele fizer parte do fluxo de dependências. Não mantenha comandos divergentes em Makefile, scripts e CI; faça a automação chamar uma fonte comum.

A documentação oficial do Nox, consultada em 22 de julho de 2026, cobre sessões, parametrização e backends. Fixe a versão da ferramenta em automações críticas e revise mudanças antes de atualizar.

O que uma sessão isola

Cada sessão cria um ambiente virtual. session.install() instala ferramentas nele; session.run() executa um programa e falha quando o código de saída não é zero. Nox não substitui pytest, Ruff ou Sphinx: ele coordena essas ferramentas. Defina python= quando o interpretador faz parte do teste. Em CI, instale todas as versões da matriz e não conte uma sessão ignorada como aprovação.

Instalar como o usuário

Teste o pacote instalado, não apenas arquivos visíveis no diretório:

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")

Declare extras como test e typing no pyproject.toml. A sessão então usa a mesma fonte de dependências da equipe. Em bibliotecas, a instalação normal também revela arquivos ausentes do wheel que um ajuste de PYTHONPATH esconderia.

Argumentos e cobertura

Tudo depois de -- chega em session.posargs. nox -s tests -- tests/test_api.py -k login escolhe um subconjunto sem editar o arquivo:

@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")

Não monte uma string de shell com a entrada. Argumentos separados evitam regras de escape específicas da plataforma.

Parametrizar com 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")

Evite um produto cartesiano sem necessidade. Cada combinação custa instalação e execução. Escolha versões mínima e máxima suportadas, documente a política e deixe uma matriz maior para tarefas agendadas.

Reutilização, documentação e build

nox -r reutiliza ambientes e acelera o ciclo local, mas não prova que dependências removidas desapareceram nem que uma instalação limpa funciona. CI deve criar ambientes novos regularmente. Cachear downloads é diferente de restaurar todo o ambiente 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 transforma avisos da documentação em falhas. Mantenha publicação fora das sessões padrão: upload altera estado externo e exige credenciais, revisão e uma origem confiável.

Usar o mesmo fluxo na CI

A pipeline deve chamar nox -s tests, em vez de copiar instalação e comandos para YAML. A CI pode selecionar a matriz, mas a lógica permanece no noxfile.py. Use nox -l para conferir nomes e descrições.

Fixe versões conforme a política do projeto e mantenha cada sessão pequena, com entradas e saídas claras. Isso permite reproduzir localmente a mesma falha vista na integração contínua. Quando houver inconsistência, recrie o ambiente antes de atribuir o problema ao código.

Comandos externos e variáveis

Prefira programas instalados na sessão. Quando uma ferramenta precisa vir do sistema, declare essa dependência visivelmente, em vez de silenciar avisos. Isso evita uma aprovação causada por um executável inesperado na máquina do autor.

Passe somente as variáveis necessárias. Tokens e senhas não pertencem ao noxfile.py; a CI deve fornecê-los apenas à sessão autorizada. Coloque publicação ou ações destrutivas em uma sessão separada, fora do conjunto padrão, e valide uma condição de release.

Manter sessões observáveis

Use nomes descritivos e códigos de saída confiáveis. Não capture uma falha apenas para continuar e terminar com sucesso. Se uma etapa opcional puder falhar, explique o motivo no log. A automação deve mostrar comando, versão do Python e entradas não sensíveis.

Revise noxfile.py como código de produção: formate, aplique lint e teste helpers complexos. Extraia uma função pequena quando houver repetição, mas preserve a leitura direta do fluxo.