How to determine if an exception was raised once you're in the finally block? How to determine if an exception was raised once you're in the finally block? python python

How to determine if an exception was raised once you're in the finally block?


Using a contextmanager

You could use a custom contextmanager, for example:

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

And then use that inside the try:

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

It's still an additional variable but it's probably a lot easier to reuse if you want to use it in multiple places. And you don't need to toggle it yourself.

Using a variable

In case you don't want the contextmanager I would reverse the logic of the trigger and toggle it only in case no exception has happened. That way you don't need an except case for exceptions that you don't want to handle. The most appropriate place would be the else clause that is entered in case the try didn't threw an exception:

exception_happened = Truetry:    # funky codeexcept HandleThis:    # handle this kind of exceptionelse:    exception_happened = Falsefinally:    if exception_happened:        print('the funky code raised')

And as already pointed out instead of having a "toggle" variable you could replace it (in this case) with the desired logging function:

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

Of course it would also work if you put it at the end of your try (as other answers here suggested) but I prefer the else clause because it has more meaning ("that code is meant to be executed only if there was no exception in the try block") and may be easier to maintain in the long run. Although it's still more to maintain than the context manager because the variable is set and toggled in different places.

Using sys.exc_info (works only for unhandled exceptions)

The last approach I want to mention is probably not useful for you but maybe useful for future readers who only want to know if there's an unhandled exception (an exception that was not caught in any except block or has been raised inside an except block). In that case you can use sys.exc_info:

import systry:    # funky codeexcept HandleThis:    passfinally:    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')


raised = Truetry:    funky code    raised = Falseexcept HandleThis:    # handle itfinally:    logger.info('funky code raised %s', raised)

Given the additional background information added to the question about selecting a log level, this seems very easily adapted to the intended use-case:

mylog = WARNINGtry:    funky code    mylog = DEBUGexcept HandleThis:    # handle itfinally:    mylog(...)


You can easily assign your caught exception to a variable and use it in the finally block, eg:

>>> x = 1>>> error = None>>> try:...     x.foo()... except Exception as e:...     error = e... finally:...     if error is not None:...             print(error)...'int' object has no attribute 'foo'