Declarar compatibilidade com várias versões do Python exige executar a suíte em cada uma delas. tox cria ambientes isolados e roda os mesmos comandos de forma reproduzível, incluindo testes, lint e verificação de tipos.
Criar 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", "."]]
Execute tox, ou apenas tox -e 3.13. O interpretador correspondente precisa existir na máquina. Se uma versão não está disponível, não trate o ambiente ignorado como teste aprovado.
O guia de pytest ajuda a construir a suíte, enquanto GitHub Actions para Python mostra onde executar a matriz completa.
Evitar divergência com a CI
Use os mesmos comandos e versões de dependências localmente e na automação. Passe argumentos adicionais conscientemente e não esconda falhas com comandos que sempre retornam sucesso. Cache pode acelerar downloads, mas cada ambiente deve continuar recriável do zero.
A documentação oficial do tox, consultada em 22 de julho de 2026, recomenda TOML para projetos novos e descreve ambientes, matrizes e empacotamento. tox aumenta a confiança de compatibilidade somente quando a matriz representa versões realmente suportadas.
O que o tox realmente isola
Cada ambiente do tox recebe um diretório virtual próprio, dependências próprias e um conjunto explícito de comandos. Isso evita que um pacote instalado globalmente faça o teste passar por acidente. Ao iniciar uma execução, tox lê a configuração, localiza o interpretador solicitado, cria ou reutiliza o ambiente, instala o projeto e as dependências declaradas e só então executa os comandos.
Essa separação é especialmente útil para bibliotecas. Um projeto pode funcionar no Python usado pelo desenvolvedor e falhar em outra versão por causa de sintaxe, dependências ou mudanças na biblioteca padrão. Aplicações também se beneficiam: ambientes separados para testes, lint e tipagem tornam cada falha mais fácil de reproduzir.
tox não baixa automaticamente qualquer versão do Python. Instale os interpretadores necessários com o gerenciador adequado ao sistema ou deixe a imagem da CI fornecê-los. Confira o que está disponível com:
tox list
tox run -e 3.13
Se o ambiente não puder ser criado, leia a mensagem antes de mudar skip_missing_interpreters. Ignorar uma versão pode ser conveniente no notebook do desenvolvedor, mas a CI responsável pela compatibilidade deve falhar quando um interpretador obrigatório estiver ausente.
Instalar o projeto da mesma forma que o usuário
Por padrão, ambientes de execução instalam o pacote do projeto. Esse detalhe encontra problemas de empacotamento que um simples pytest no diretório de trabalho pode esconder, como arquivos ausentes no artefato ou imports que só funcionam por causa da pasta atual. Mantenha os metadados de build no pyproject.toml e teste o pacote instalado.
Use skip_install = true apenas em tarefas que não precisam importar o projeto, como uma verificação de formatação. Para uma aplicação sem pacote instalável, avalie package = "skip" conscientemente. Não aplique essa opção a toda a configuração apenas para contornar um erro de build: corrija o empacotamento ou documente por que ele não faz parte do fluxo.
Dependências usadas somente nos testes pertencem a deps. Quando houver arquivos de requisitos mantidos pelo projeto, tox também pode instalá-los:
[env_run_base]
deps = [
"-r requirements-test.txt",
]
commands = [
["pytest", "-q", { replace = "posargs", default = ["tests"], extend = true }],
]
Com essa configuração, tox -e 3.13 -- tests/test_api.py -x encaminha argumentos ao pytest. O separador -- deixa claro que o restante não é uma opção do tox. Definir um valor padrão mantém a execução comum simples e permite investigar um teste específico sem duplicar ambientes.
Separar responsabilidades por ambiente
Uma matriz não precisa repetir todas as ferramentas em todas as versões. Rode os testes nas versões de Python declaradas como compatíveis e mantenha tarefas independentes com nomes descritivos:
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"]]
Isso produz um diagnóstico mais claro: uma falha em type não parece uma incompatibilidade com Python 3.11. Também evita executar lint três vezes sem benefício. Se uma ferramenta precisa da versão mais recente do projeto instalada, não use skip_install nesse ambiente.
Nomes compostos e fatores são úteis em matrizes maiores, mas comece com uma configuração legível. Duplicação pequena é preferível a uma expressão tão compacta que ninguém consegue prever quais dependências serão instaladas.
Recriar ambientes e investigar falhas
tox reutiliza ambientes para acelerar execuções. Quando a configuração ou as dependências mudam, ele decide se precisa recriá-los. Para eliminar dúvidas durante um diagnóstico, force uma criação limpa:
tox run -r -e 3.13
tox run -e 3.13 -- -vv
O primeiro comando recria o ambiente. O segundo encaminha maior verbosidade ao pytest quando posargs está configurado. Não apague diretórios manualmente como primeira reação, porque o log do tox costuma revelar o interpretador selecionado, o comando executado e a etapa que falhou.
Uma falha na criação do ambiente é diferente de uma falha de teste. Verifique, nesta ordem, a disponibilidade do interpretador, a construção do pacote, a resolução das dependências e a saída do comando. Se o teste passa fora do tox, compare variáveis de ambiente e o diretório de execução. Declare variáveis necessárias explicitamente e nunca dependa de credenciais locais para testes unitários.
Usar tox na integração contínua
Há duas estratégias razoáveis. Uma tarefa da CI pode instalar vários interpretadores e executar tox, ou a própria plataforma pode criar uma matriz e chamar apenas o ambiente correspondente em cada tarefa. A segunda opção distribui o trabalho e mostra qual versão falhou; a primeira mantém mais lógica dentro do tox.
Evite manter duas matrizes divergentes. Se a CI enumera versões, alinhe essa lista a env_list e à política de suporte do projeto. Um novo Python só deve aparecer na documentação como suportado depois que sua execução passar de forma regular. Da mesma forma, remover uma versão exige uma decisão de suporte, não apenas apagar um job inconveniente.
Cacheie downloads e ambientes apenas quando a chave inclui versão do Python, sistema operacional e arquivos de dependência relevantes. Um cache incorreto pode mascarar mudanças ou produzir falhas difíceis de repetir. Periodicamente, valide uma execução sem cache para confirmar que o projeto continua construível do zero.
Práticas que mantêm a matriz confiável
Fixe limites de dependências onde a estabilidade exigir, mas permita que uma tarefa programada teste versões recentes para detectar incompatibilidades cedo. Mantenha comandos determinísticos, sem acesso desnecessário à rede. Testes que dependem de serviços externos devem usar ambientes próprios e sinalização explícita.
Não use allowlist_externals sem entender o motivo. Comandos externos ampliam as diferenças entre sistemas e precisam existir na imagem local e na CI. Prefira invocar ferramentas Python instaladas em deps. Quando um executável do sistema for indispensável, documente a pré-condição e valide sua presença.
Por fim, trate o arquivo do tox como código de produção: revise mudanças, execute ao menos o ambiente principal localmente e confira a matriz completa antes de lançar uma versão. Uma configuração pequena e previsível entrega mais confiança do que dezenas de ambientes que ninguém acompanha.