Em Python, como alguém detecta avisos como se fossem exceções?

103

Uma biblioteca de terceiros (escrita em C) que uso em meu código python está emitindo avisos. Quero ser capaz de usar a try exceptsintaxe para lidar adequadamente com esses avisos. Existe uma maneira de fazer isso?

Boris Gorelik
fonte
2
Esses avisos são apenas mensagens de texto escritas do stderr?
Fenikso
1
Fenikso: Não sei ao certo, parecem avisos reais
Boris Gorelik
1
Como você reconhece o "aviso real"? Eu pensei que em C você recebe avisos reais durante a compilação.
Fenikso
warnings.filterwarningsfaz exatamente o que você quer, não entendo qual é o seu problema com isso?
Rosh Oxymoron
4
@Fenikso, @Rosh Oxymoron você estava certo. Meu erro. warnings.filterwarnigns('error')faz o trabalho. Não consigo encontrar a resposta original que propôs esta solução
Boris Gorelik

Respostas:

51

Para citar o manual do python ( 27.6.4. Advertências de teste ):

import warnings

def fxn():
    warnings.warn("deprecated", DeprecationWarning)

with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")
    # Trigger a warning.
    fxn()
    # Verify some things
    assert len(w) == 1
    assert issubclass(w[-1].category, DeprecationWarning)
    assert "deprecated" in str(w[-1].message)
Bobby Powers
fonte
6
Aqui está uma resposta que lhe diz como usar a try exceptsintaxe.
Unapiedra de
Isso tem a vantagem, em relação à resposta de niekas, que se fnxretornar algo, você mantém esse resultado (e ainda pode gerenciar o aviso).
Pietro Battiston
Isso não responde à pergunta do OP, que era sobre lidar com wanrings, não testá-los. No entanto, a resposta de niekas abaixo mostra como lidar com os avisos.
Biggsy
Apenas uma observação de que a função acima não funcionará se sua função retornar apenas intermitentemente um aviso, pois no caso de fxn()não retornar um aviso, então wserá uma lista vazia. Se wé uma lista vazia (ou seja []), então executar o código lhe dará o seguinte erro: IndexError: list index out of range. Se você está apenas procurando formatar ou verificar as propriedades dos avisos capturados, é melhor usar um loop for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer
130

Para lidar com avisos como erros, basta usar isto:

import warnings
warnings.filterwarnings("error")

Depois disso, você será capaz de capturar os mesmos avisos que os erros, por exemplo, isso funcionará:

try:
    some_heavy_calculations()
except RuntimeWarning:
    import ipdb; ipdb.set_trace()

PS Adicionada esta resposta porque a melhor resposta nos comentários contém erro ortográfico: em filterwarnignsvez de filterwarnings.

niekas
fonte
8
E se você quiser apenas ver um rastreamento de pilha, as primeiras duas linhas são tudo que você precisa.
z0r
5
Isto é perfeito. Eu só queria que meu script parasse de executar assim que o aviso fosse emitido, para que eu pudesse imprimir informações de depuração relevantes e corrigir o problema.
Praveen
1
Você não precisa da filterwarningschamada para capturar Warnings, pelo menos em python 3. ela simplesmente funciona.
naught101
1
A resposta aceita não responde à pergunta do OP. Essa resposta sim. Esta é a resposta que eu estava procurando quando minha pesquisa encontrou esta pergunta.
Biggsy
15

Aqui está uma variação que torna mais claro como trabalhar apenas com seus avisos personalizados.

import warnings
with warnings.catch_warnings(record=True) as w:
    # Cause all warnings to always be triggered.
    warnings.simplefilter("always")

    # Call some code that triggers a custom warning.
    functionThatRaisesWarning()

    # ignore any non-custom warnings that may be in the list
    w = filter(lambda i: issubclass(i.category, UserWarning), w)

    if len(w):
        # do something with the first warning
        email_admins(w[0].message)
Mcqwerty
fonte
4

Em alguns casos, você precisa usar ctypes para transformar avisos em erros. Por exemplo:

str(b'test')  # no error
import warnings
warnings.simplefilter('error', BytesWarning)
str(b'test')  # still no error
import ctypes
ctypes.c_int.in_dll(ctypes.pythonapi, 'Py_BytesWarningFlag').value = 2
str(b'test')  # this raises an error
Collin Anderson
fonte
Essa resposta é construtiva simplesmente por mostrar como errar apenas em certos tipos de aviso. Para quase todos os grandes projetos de software, se você fizer warnings.simplefilter('error')isso, você não obterá o rastreamento do aviso que viu nos logs, mas sim obterá os rastreamentos de avisos filtrados anteriormente. Usar simplefiltertambém é a maneira mais rápida de chegar à sua resposta se você tiver alguma chamada de CLI.
AlanSE