Wie fängt man in Python Warnungen ab, als wären sie Ausnahmen?

103

Eine Drittanbieter-Bibliothek (in C geschrieben), die ich in meinem Python-Code verwende, gibt Warnungen aus. Ich möchte in der Lage sein, die try exceptSyntax zu verwenden, um diese Warnungen richtig zu behandeln. Gibt es eine Möglichkeit, dies zu tun?

Boris Gorelik
quelle
2
Sind diese Warnungen nur geschriebene Textnachrichten von stderr?
Fenikso
1
Fenikso: Ich weiß es nicht genau, scheint eine echte Warnung zu sein
Boris Gorelik
1
Woran erkennt man "echte Warnung"? Ich dachte, dass man in C beim Kompilieren eine echte Warnung bekommt.
Fenikso
warnings.filterwarningsmacht genau das, was du willst, ich verstehe nicht, was dein Problem damit ist?
Rosh Oxymoron
4
@ Fenikso, @ Rosch Oxymoron du hattest recht. Mein Fehler. warnings.filterwarnigns('error')macht den Job. Ich kann die ursprüngliche Antwort, die diese Lösung vorschlug, nicht finden
Boris Gorelik

Antworten:

50

Um aus dem Python-Handbuch ( 27.6.4. Testen von Warnungen ) zu zitieren :

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
quelle
6
Hier ist eine Antwort, die Ihnen erklärt, wie Sie die try exceptSyntax verwenden.
Unapiedra
Dies hat gegenüber der Antwort von niekas den Vorteil, dass fnxSie das Ergebnis behalten (und die Warnung trotzdem verwalten können) , wenn Sie etwas zurückgeben.
Pietro Battiston
Dies beantwortet nicht die Frage des OP, bei der es darum ging, mit Wanringen umzugehen und sie nicht zu testen. Die Antwort von Niekas unten zeigt jedoch, wie mit Warnungen umgegangen wird.
Biggsy
Nur ein Hinweis, dass die obige Funktion nicht funktioniert, wenn Ihre Funktion nur zeitweise eine Warnung zurückgibt, da in dem Fall, dass fxn()keine Warnung zurückgegeben wwird, eine leere Liste angezeigt wird. Wenn wes sich um eine leere Liste handelt [], wird beim Ausführen des Codes der folgende Fehler angezeigt : IndexError: list index out of range. Wenn Sie nur die Eigenschaften der erfassten Warnungen formatieren oder überprüfen möchten, ist es besser, eine for-Schleife zu verwenden:for x in w: print(f'{x.category.__name__}: {str(x.message)}')
Steven M. Mortimer
130

Um Warnungen als Fehler zu behandeln, verwenden Sie einfach Folgendes:

import warnings
warnings.filterwarnings("error")

Danach können Sie Warnungen wie Fehler abfangen, z. B. funktioniert dies:

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

PS Diese Antwort wurde hinzugefügt, da die beste Antwort in Kommentaren Rechtschreibfehler enthält: filterwarnignsstatt filterwarnings.

Niekas
quelle
8
Und wenn Sie nur eine Stapelverfolgung sehen möchten, sind die ersten beiden Zeilen alles, was Sie brauchen.
z0r
5
Dies ist perfekt. Ich wollte nur, dass mein Skript die Ausführung stoppt, sobald die Warnung ausgegeben wurde, damit ich relevante Debug-Informationen drucken und das Problem beheben kann.
Praveen
1
Sie brauchen den filterwarningsAufruf nicht, um ihn abzufangen Warnings, zumindest in Python 3. Es funktioniert einfach.
naught101
Die akzeptierte Antwort beantwortet die Frage des OP nicht. Diese Antwort tut es. Dies ist die Antwort, nach der ich gesucht habe, als meine Suche diese Frage gefunden hat.
Biggsy
15

Hier ist eine Variante, die klarer macht, wie Sie nur mit Ihren benutzerdefinierten Warnungen arbeiten.

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
quelle
4

In einigen Fällen müssen Sie ctypes verwenden, um Warnungen in Fehler umzuwandeln. Beispielsweise:

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
quelle
Diese Antwort ist nur konstruktiv, um zu zeigen, wie Fehler nur bei bestimmten Warnungstypen auftreten können. Wenn Sie dies tun, erhalten warnings.simplefilter('error')Sie bei fast jedem großen Softwareprojekt nicht den Traceback für die Warnung, die Sie in den Protokollen gesehen haben, sondern Tracebacks von zuvor gefilterten Warnungen. Die Verwendung simplefilterist auch der schnellste Weg, um zu Ihrer Antwort zu gelangen, wenn Sie einen CLI-Aufruf haben.
AlanSE