In Python, come si catturano gli avvisi come se fossero eccezioni?


103

Una libreria di terze parti (scritta in C) che utilizzo nel mio codice Python emette avvisi. Voglio essere in grado di utilizzare la try exceptsintassi per gestire correttamente questi avvisi. C'è un modo per fare questo?


2
Questi avvertimenti sono solo messaggi di testo scritti in stderr?
Fenikso

1
Fenikso: Non lo so per certo, sembrano veri avvertimenti
Boris Gorelik

1
Come riconosci il "vero avvertimento"? Ho pensato che in C ricevi un vero avvertimento durante la compilazione.
Fenikso

warnings.filterwarningsfa esattamente quello che vuoi, non capisco qual è il tuo problema con esso?
Rosh Oxymoron

4
@Fenikso, @Rosh Oxymoron avevi ragione. Errore mio. warnings.filterwarnigns('error')fa il lavoro. Non riesco a trovare la risposta originale che ha proposto questa soluzione
Boris Gorelik

Risposte:


51

Per citare dal manuale di python ( 27.6.4. Avvertenze sui test ):

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)

6
Ecco una risposta, che ti dice come usare la try exceptsintassi.
Unapiedra

Questo ha il vantaggio, rispetto alla risposta di niekas, che se fnxrestituisce qualcosa, mantieni quel risultato (e puoi comunque gestire l'avviso).
Pietro Battiston

Questo non risponde alla domanda dell'OP, che riguardava la gestione degli anelli, non il test. Tuttavia, la risposta di niekas di seguito mostra come gestire gli avvisi.
Biggsy

Solo una nota che la funzione di cui sopra non funzionerà se la tua funzione restituisce solo in modo intermittente un avviso perché nel caso in cui fxn()non restituisca un avviso, allora wsarà un elenco vuoto. Se wè una lista vuota (cioè []), quindi l'esecuzione del codice vi darà il seguente errore: IndexError: list index out of range. Se stai solo cercando di formattare o controllare le proprietà degli avvisi catturati, è meglio usare un ciclo for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer,

130

Per gestire gli avvisi come errori, usa semplicemente questo:

import warnings
warnings.filterwarnings("error")

Dopo questo sarai in grado di rilevare gli avvisi come gli errori, ad esempio questo funzionerà:

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

PS Aggiunta questa risposta perché la migliore risposta nei commenti contiene errori di ortografia: filterwarnignsinvece di filterwarnings.


8
E se vuoi solo vedere una traccia dello stack, le prime due righe sono tutto ciò di cui hai bisogno.
z0r

5
Questo è perfetto. Volevo solo che il mio script interrompesse l'esecuzione non appena è stato emesso l'avviso, in modo da poter stampare le informazioni di debug pertinenti e risolvere il problema.
Praveen

1
Non hai bisogno della filterwarningschiamata per catturare Warnings, almeno in python 3. funziona e basta.
nought101

1
La risposta accettata non risponde alla domanda del PO. Questa risposta sì. Questa è la risposta che stavo cercando quando la mia ricerca ha trovato questa domanda.
Biggsy


15

Ecco una variazione che rende più chiaro come lavorare solo con i tuoi avvisi personalizzati.

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)

4

In alcuni casi, è necessario utilizzare ctypes per trasformare gli avvisi in errori. Per esempio:

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

Questa risposta è costruttiva semplicemente per mostrare come l'errore solo in alcuni tipi di avviso. Per quasi tutti i progetti software di grandi dimensioni, se lo fai warnings.simplefilter('error')non otterrai il traceback per l'avviso che hai visto nei log, ma riceverai invece i traceback da avvisi precedentemente filtrati. L'utilizzo simplefilterè anche il modo più rapido per arrivare alla tua risposta se hai qualche invocazione CLI.
AlanSE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.