try - python3 except exception e




Wie kann man feststellen, ob eine Ausnahme ausgelöst wurde, wenn Sie sich im finally-Block befinden? (4)

Einen Kontextmanager verwenden

Sie könnten beispielsweise einen benutzerdefinierten Kontextmanager verwenden:

class DidWeRaise:
    __slots__ = ('exception_happened', )  # instances will take less memory

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # If no exception happened the `exc_type` is None
        self.exception_happened = exc_type is not None

Und dann benutze das innerhalb des try :

try:
    with DidWeRaise() as error_state:
        # funky code
finally:
    if error_state.exception_happened:
        print('the funky code raised')

Es ist immer noch eine zusätzliche Variable, aber es ist wahrscheinlich viel einfacher wiederzuverwenden, wenn Sie es an mehreren Stellen verwenden möchten. Und Sie müssen es nicht selbst umschalten.

Eine Variable verwenden

Für den Fall, dass Sie den Kontextmanager nicht möchten, würde ich die Logik des Triggers umkehren und sie nur für den Fall umschalten, dass keine Ausnahme aufgetreten ist. Auf diese Weise brauchen Sie keinen except für Ausnahmen, die Sie nicht behandeln möchten. Der am besten geeignete Ort wäre die else Klausel, die eingegeben wird, falls der try keine Ausnahme ausgelöst hat:

exception_happened = True
try:
    # funky code
except HandleThis:
    # handle this kind of exception
else:
    exception_happened = False
finally:
    if exception_happened:
        print('the funky code raised')

Und wie bereits erwähnt, anstatt eine "Toggle" -Variable zu haben, könnte man sie (in diesem Fall) durch die gewünschte Logging-Funktion ersetzen:

mylog = mylogger.WARNING
try:
    with LogCapture() as log:
        funky_code()
except HandleThis:
    # handle this kind of exception
else:
    # In case absolutely no exception was thrown in the try we can log on debug level
    mylog = mylogger.DEBUG
finally:
    for record in log.captured:
        mylog(record.msg, record.args)

Natürlich würde es auch funktionieren, wenn Sie es am Ende Ihres try (wie andere Antworten hier vorgeschlagen), aber ich bevorzuge die else Klausel, weil es mehr Bedeutung hat ("dieser Code soll nur ausgeführt werden, wenn es keine Ausnahme gab der try Block ") und auf lange Sicht einfacher zu pflegen sein. Obwohl es noch mehr zu verwalten ist als der Kontextmanager, weil die Variable an verschiedenen Stellen gesetzt und umgeschaltet wird.

sys.exc_info (funktioniert nur für nicht behandelte Ausnahmen)

Der letzte Ansatz, den ich erwähnen möchte, ist wahrscheinlich nicht nützlich für Sie, aber vielleicht nützlich für zukünftige Leser, die nur wissen wollen, ob es eine unbehandelte Ausnahme gibt (eine Ausnahme, die in keinem Block gefangen wurde oder innerhalb eines except ausgelöst wurde). In diesem Fall können Sie sys.exc_info :

import sys

try:
    # funky code
except HandleThis:
    pass
finally:
    if sys.exc_info()[0] is not None:
        # only entered if there's an *unhandled* exception, e.g. NOT a HandleThis exception
        print('funky code raised')

Ist es möglich zu sagen, ob es eine Ausnahme gab, sobald Sie in der finally Klausel sind? Etwas wie:

try:
    funky code
finally:
    if ???:
        print('the funky code raised')

Ich möchte etwas wie dieses TROCKENER machen:

try:
    funky code
except HandleThis:
    # handle it
    raised = True
except DontHandleThis:
    raised = True
    raise
else:
    raised = False
finally:
    logger.info('funky code raised %s', raised)

Ich mag es nicht, dass es erfordert, um eine Ausnahme zu fangen, die Sie nicht behandeln wollen, nur um eine Flagge zu setzen.

Da einige comments im MCVE nach weniger "M" fragen, hier ein paar Hintergrundinformationen zum Anwendungsfall. Das eigentliche Problem besteht in der Eskalation der Protokollierungsstufen.

  • Der funky Code ist ein Drittanbieter und kann nicht geändert werden.
  • Die logger.exception und die Stack-Ablaufverfolgung enthalten keine nützlichen Diagnoseinformationen. logger.exception Verwendung von logger.exception in einem Ausnahmeblock hier nicht hilfreich.
  • Wenn der Funky-Code ausgelöst wird, wurden einige Informationen, die ich sehen muss, bereits auf der Ebene DEBUG protokolliert. Wir können und können den Fehler nicht behandeln, aber wir möchten die DEBUG-Protokollierung eskalieren, da die benötigte Information dort ist.
  • Der funky Code erhöht die meiste Zeit nicht. Ich möchte die Protokollierungsstufen für den allgemeinen Fall nicht eskalieren, weil es zu ausführlich ist.

Daher wird der Code unter einem Protokollaufzeichnungskontext ausgeführt (der benutzerdefinierte Handler zum Abfangen von Protokolldatensätzen einrichtet), und einige Debug-Informationen werden nachträglich erneut protokolliert:

try:
    with LogCapture() as log:
        funky_code()  # <-- third party badness
finally:
    mylog = mylogger.WARNING if <there was exception> else mylogger.DEBUG
    for record in log.captured:
        mylog(record.msg, record.args)

Okay, so klingt es, als ob Sie eigentlich nur Ihren vorhandenen Context Manager ändern oder einen ähnlichen Ansatz verwenden möchten: logbook eigentlich einen FingersCrossedHandler , der genau das tut, was Sie wollen. Aber du könntest es selbst tun, wie:

@contextmanager
def LogCapture():
    # your existing buffer code here
    level = logging.WARN
    try:
        yield
    except UselessException:
        level = logging.DEBUG
        raise  # Or don't, if you just want it to go away
    finally:
        # emit logs here

Ursprüngliche Antwort

Du denkst ein bisschen seitlich darüber nach.

Sie beabsichtigen, die Ausnahme zu behandeln - Sie behandeln sie, indem Sie eine Markierung setzen. Vielleicht kümmert es dich auch nicht um etwas anderes (was wie eine schlechte Idee aussieht), aber wenn du etwas machen willst, wenn eine Ausnahme ausgelöst wird, dann möchtest du das explizit sagen.

Die Tatsache, dass Sie eine Variable setzen, aber die Ausnahme weitermachen soll, bedeutet, dass Sie Ihre eigene spezifische Ausnahme von der ausgelösten Ausnahme auslösen möchten:

class MyPkgException(Exception):  pass
class MyError(PyPkgException): pass  # If there's another exception type, you can also inherit from that

def do_the_badness():
    try:
        raise FileNotFoundError('Or some other code that raises an error')
    except FileNotFoundError as e:
        raise MyError('File was not found, doh!') from e
    finally:
        do_some_cleanup()

try:
    do_the_badness()
except MyError as e:
    print('The error? Yeah, it happened')

Dies löst:

  • Explizite Behandlung der Ausnahme (n), die Sie behandeln möchten
  • Stack-Traces und ursprüngliche Ausnahmen verfügbar machen
  • Zulassen, dass Ihr Code, der die ursprüngliche Ausnahme behandelt, anderswo Ihre ausgelöste Ausnahme behandelt
  • MyPkgException , dass ein Ausnahmefehlerbehandlungscode der obersten Ebene MyPkgException so MyPkgException , dass alle Ausnahmen MyPkgException , damit etwas protokolliert und mit einem schönen Status beendet werden kann, anstatt mit einem hässlichen Stapel-Trace

Wenn ich es wäre, würde ich deinen Code ein wenig nachbestellen.

raised = False
try:
    # funky code
except HandleThis:
    # handle it
    raised = True
except Exception as ex:
    # Don't Handle This 
    raise ex
finally:
    if raised:
        logger.info('funky code was raised')

Ich habe die raised boolean-Zuweisung außerhalb der try-Anweisung platziert, um den Gültigkeitsbereich zu gewährleisten, und die final except-Anweisung zu einem allgemeinen Ausnahmebehandler für Ausnahmen gemacht, die Sie nicht behandeln möchten.

Dieser Stil bestimmt, ob der Code fehlgeschlagen ist. Ein anderer Ansatz könnte ich feststellen, wenn Ihr Code erfolgreich ist.

success = False
try:
    # funky code
    success = True
except HandleThis:
    # handle it
    pass
except Exception as ex:
    # Don't Handle This 
    raise ex
finally:
    if success:
        logger.info('funky code was successful')
    else:
        logger.info('funky code was raised')

raised = True
try:
    funky code
    raised = False
except HandleThis:
    # handle it
finally:
    logger.info('funky code raised %s', raised)

Angesichts der zusätzlichen Hintergrundinformationen, die der Frage zur Auswahl eines Log-Levels hinzugefügt wurden, scheint dies sehr leicht an den beabsichtigten Anwendungsfall angepasst zu sein:

mylog = WARNING
try:
    funky code
    mylog = DEBUG
except HandleThis:
    # handle it
finally:
    mylog(...)




try-finally