W jaki sposób w Pythonie można wychwycić ostrzeżenia, jakby były wyjątkami?

103

Biblioteka innej firmy (napisana w C), której używam w moim kodzie Pythona, generuje ostrzeżenia. Chcę móc używać try exceptskładni, aby poprawnie obsługiwać te ostrzeżenia. Czy jest na to sposób?

Boris Gorelik
źródło
2
Czy te ostrzeżenia to tylko wiadomości tekstowe napisane do standardowego błędu?
Fenikso
1
Fenikso: Nie wiem na pewno, wydaje się realne ostrzeżeń
Boris Gorelik
1
Jak rozpoznajesz „prawdziwe ostrzeżenie”? Myślałem, że w C otrzymujesz prawdziwe ostrzeżenie podczas kompilacji.
Fenikso
warnings.filterwarningsrobi dokładnie to, czego chcesz, nie rozumiem, jaki jest twój problem?
Rosh Oxymoron
4
@Fenikso, @Rosh Oxymoron miałeś rację. Mój błąd. warnings.filterwarnigns('error')wykonuje pracę. Nie mogę znaleźć oryginalnej odpowiedzi, która zaproponowała to rozwiązanie
Boris Gorelik

Odpowiedzi:

51

Cytując z podręcznika Pythona ( 27.6.4. Testing Warnings ):

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
źródło
6
Oto odpowiedź, która mówi, jak używać try exceptskładni.
Unapiedra
Ma to tę przewagę nad odpowiedzią niekasa, że ​​jeśli fnxcoś zwróci, zachowujesz ten wynik (i nadal możesz zarządzać ostrzeżeniem).
Pietro Battiston
Nie odpowiada to na pytanie OP, które dotyczyło radzenia sobie z awariami, a nie ich testowania. Jednak odpowiedź niekas poniżej pokazuje, jak radzić sobie z ostrzeżeniami.
Biggsy
Tylko uwaga, że ​​powyższa funkcja nie zadziała, jeśli Twoja funkcja tylko sporadycznie zwraca ostrzeżenie, ponieważ w przypadku, fxn()gdy nie zwróci ostrzeżenia, wbędzie pusta lista. Jeśli wjest pusta lista (to znaczy []), a następnie uruchomienie kodu daje następujący błąd: IndexError: list index out of range. Jeśli chcesz tylko sformatować lub sprawdzić właściwości przechwyconych ostrzeżeń, lepiej użyć pętli for:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer
130

Aby obsłużyć ostrzeżenia jako błędy, po prostu użyj tego:

import warnings
warnings.filterwarnings("error")

Po tym będziesz mógł łapać ostrzeżenia takie same jak błędy, np. To zadziała:

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

PS Dodałem tę odpowiedź, ponieważ najlepsza odpowiedź w komentarzach zawiera błąd ortograficzny: filterwarnignszamiast filterwarnings.

niekas
źródło
8
A jeśli chcesz tylko zobaczyć ślad stosu, potrzebujesz tylko pierwszych dwóch wierszy.
z0r
5
To jest doskonałe. Chciałem tylko, aby mój skrypt zatrzymał wykonywanie, gdy tylko pojawi się ostrzeżenie, aby móc wydrukować odpowiednie informacje debugowania i naprawić problem.
Praveen
1
Nie potrzebujesz filterwarningswywołania, aby złapać Warnings, przynajmniej w Pythonie 3. po prostu działa.
naught101
1
Przyjęta odpowiedź nie odpowiada na pytanie PO. Ta odpowiedź tak. To jest odpowiedź, której szukałem, gdy moje wyszukiwanie znalazło to pytanie.
Biggsy
15

Oto odmiana, która wyjaśnia, jak pracować tylko z niestandardowymi ostrzeżeniami.

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
źródło
4

W niektórych przypadkach musisz użyć ctypes, aby zmienić ostrzeżenia w błędy. Na przykład:

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
źródło
Ta odpowiedź jest konstruktywna tylko po to, aby pokazać, jak popełnić błąd tylko w niektórych typach ostrzeżeń. W przypadku prawie każdego dużego projektu oprogramowania, jeśli to zrobisz warnings.simplefilter('error'), nie uzyskasz śledzenia wstecznego dla ostrzeżenia, które widziałeś w dziennikach, ale zamiast tego uzyskasz dane śledzenia z wcześniej przefiltrowanych ostrzeżeń. Użycie simplefilterjest również najszybszym sposobem uzyskania odpowiedzi, jeśli masz jakieś wywołanie CLI.
AlanSE