O contexto warnings.catch_warnings permite controlar avisos temporariamente em testes, bibliotecas, scripts de migração e integrações com código legado. Em vez de alterar filtros globais de forma permanente, ele salva o estado atual do módulo warnings, aplica regras dentro de um bloco e restaura a configuração ao final. Essa característica torna o recurso útil para capturar, transformar, ignorar ou validar avisos sem contaminar outras partes da aplicação.
A ideia central é simples: avisos não são exceções. Eles sinalizam situações que merecem atenção, como APIs obsoletas, comportamentos que mudarão no futuro, perda potencial de precisão ou uso inadequado de recursos. O programa normalmente continua executando. Por isso, testes maduros precisam verificar não apenas o resultado final, mas também quais avisos foram emitidos.
Uso básico
import warnings
with warnings.catch_warnings(record=True) as captured:
warnings.simplefilter("always")
warnings.warn("recurso antigo", DeprecationWarning)
assert len(captured) == 1
assert captured[0].category is DeprecationWarning
assert "recurso antigo" in str(captured[0].message)
Quando record=True, o contexto devolve uma lista de objetos WarningMessage. Cada item informa a mensagem, a categoria, o nome do arquivo, a linha e outros detalhes. O filtro always é importante em testes porque o Python pode suprimir avisos repetidos conforme o registro interno do módulo.
Por que restaurar o estado importa
Chamadas como warnings.simplefilter e warnings.filterwarnings afetam o estado do módulo. Se um teste ignora todos os avisos e não desfaz essa alteração, os testes seguintes podem produzir falsos positivos. O contexto reduz esse risco ao restaurar os filtros quando o bloco termina, inclusive se ocorrer uma exceção.
A restauração não significa que qualquer uso é automaticamente seguro em concorrência. O módulo trabalha com estado compartilhado e a documentação da versão do Python usada pela aplicação deve ser consultada. Em projetos com múltiplas threads ou tarefas assíncronas, evite manter contextos amplos enquanto outras partes alteram filtros simultaneamente. Prefira blocos pequenos, testes isolados e configuração centralizada.
Capturando uma categoria específica
import warnings
def legacy_api():
warnings.warn(
"legacy_api será removida",
DeprecationWarning,
stacklevel=2,
)
return 42
with warnings.catch_warnings(record=True) as captured:
warnings.simplefilter("always", DeprecationWarning)
result = legacy_api()
assert result == 42
assert len(captured) == 1
assert issubclass(captured[0].category, DeprecationWarning)
Filtrar por categoria evita esconder avisos não relacionados. Ignorar tudo com ignore pode mascarar ResourceWarning, RuntimeWarning ou alertas importantes de dependências. Uma regra específica comunica melhor a intenção do teste.
Transformando avisos em erros
Durante testes e integração contínua, pode ser útil tratar avisos como exceções. Isso força a equipe a resolver APIs obsoletas antes de uma atualização quebrar o sistema.
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
legacy_api()
Nesse exemplo, a chamada gera uma exceção DeprecationWarning. A estratégia funciona bem em suítes controladas, mas ativá-la indiscriminadamente em produção pode interromper fluxos por avisos de bibliotecas externas. Comece por categorias e módulos específicos.
Filtros por mensagem e módulo
filterwarnings aceita critérios mais detalhados. É possível combinar ação, expressão regular da mensagem, categoria, módulo e número da linha.
with warnings.catch_warnings(record=True) as captured:
warnings.filterwarnings(
"always",
message=r".*parâmetro antigo.*",
category=DeprecationWarning,
module=r"meu_pacote\..*",
)
executar_fluxo()
Use expressões simples e estáveis. Testes muito dependentes do texto completo de uma biblioteca podem quebrar por mudanças editoriais sem alteração real de comportamento. Prefira validar categoria, trecho significativo e origem.
O papel de stacklevel
Ao emitir um aviso em uma biblioteca, configure stacklevel para apontar ao código do usuário, não à linha interna que chamou warnings.warn. Normalmente stacklevel=2 aponta um nível acima, mas wrappers adicionais podem exigir outro valor.
def novo_nome():
return 10
def nome_antigo():
warnings.warn(
"use novo_nome()",
DeprecationWarning,
stacklevel=2,
)
return novo_nome()
Um aviso bem localizado reduz o tempo de correção. Testes podem verificar filename e lineno quando a localização faz parte do contrato.
Registro interno de avisos
O Python mantém registros para evitar repetir certas mensagens. Isso explica por que um aviso pode aparecer uma vez e desaparecer em chamadas posteriores. Em testes, simplefilter("always") dentro de catch_warnings torna o comportamento previsível. Mesmo assim, módulos já importados podem possuir registros próprios. Evite depender da ordem de execução da suíte.
Integração com logging
logging.captureWarnings(True) redireciona avisos para o sistema de logs. Isso pode ser útil em serviços, mas é diferente de capturar uma lista para asserções. Em testes unitários, catch_warnings(record=True) costuma ser mais direto. Em produção, o logging facilita incluir timestamps, correlação e destino centralizado.
Boas práticas em bibliotecas
Use categorias adequadas. DeprecationWarning comunica remoções futuras para desenvolvedores; FutureWarning pode ser apropriado para mudanças que afetam usuários finais; categorias personalizadas ajudam a separar domínios. Documente a versão de introdução, a alternativa recomendada e a previsão de remoção.
Não use avisos para erros que tornam o resultado inválido. Nesses casos, lance uma exceção. Também não emita o mesmo aviso dentro de loops intensivos sem necessidade, pois isso adiciona custo e ruído.
Testando ausência de avisos inesperados
with warnings.catch_warnings(record=True) as captured:
warnings.simplefilter("always")
executar_operacao_estavel()
unexpected = [w for w in captured if w.category is not UserWarning]
assert not unexpected
Essa abordagem permite aceitar uma categoria conhecida enquanto rejeita outras. Em projetos grandes, crie auxiliares reutilizáveis para padronizar mensagens de falha e reduzir repetição.
Concorrência e escopo
O maior risco operacional é tratar filtros como se fossem locais quando outra execução está alterando o mesmo estado. Mantenha o contexto o menor possível e não envolva longas operações de rede, espera ou processamento paralelo. Em servidores, configure a política de avisos na inicialização e use captura temporária principalmente em testes ou tarefas controladas.
Quando usar
Use catch_warnings para testar depreciações, validar bibliotecas, silenciar um aviso conhecido em trecho estreito, converter categorias específicas em erro ou inspecionar origem e mensagem. Evite usá-lo como solução genérica para “limpar o terminal”. Um aviso recorrente normalmente indica dependência desatualizada, API antiga ou comportamento que precisa ser corrigido.
Para aprofundar o assunto, consulte a documentação oficial de warnings e a seção sobre integração com logging. Na Academify, veja também os conteúdos sobre Python, testes em Python, programação e o curso de Python.
Conclusão
warnings.catch_warnings oferece controle temporário e verificável sobre avisos. Uma implementação robusta usa filtros específicos, blocos curtos, record=True em testes, stacklevel correto ao emitir mensagens e cautela com estado compartilhado. Assim, avisos deixam de ser ruído e se tornam parte útil da qualidade, compatibilidade e manutenção do software.







