catch_warnings: capture warnings em testes Python

Publicado em: 07/10/2026
Tempo de leitura: 5 minutos
Código Python exibindo avisos controlados com catch_warnings

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python em tela representando inspeção de módulos e pacotes
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifique pacotes Python

    Aprenda inspect.ispackage no Python para identificar pacotes, explorar módulos e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026
    Notebook exibindo código e gráficos de desempenho para análise do sys._jit no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sys._jit: detecte e meça o JIT experimental

    Aprenda sys._jit no Python para detectar suporte ao JIT experimental, medir desempenho e evitar decisões frágeis.

    Ler mais

    Tempo de leitura: 6 minutos
    05/10/2026
    Visualização de cálculos numéricos e precisão para math.fma no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    math.fma: cálculos com um único arredondamento

    Aprenda math.fma no Python para multiplicar e somar com um único arredondamento e melhorar cálculos numéricos.

    Ler mais

    Tempo de leitura: 6 minutos
    04/10/2026