简体   繁体   中英

What is the reason to use exception chaining

Lately I was reading about exception chaining, and i am not sure when i should use this syntax, and for what reason.

I am used to such pattern:

try:
    my_function()
exception MyError:
    my_logger.exception("Error occured")
    raise MyProcessError("Process failed")

I have noticed that if I will use here exception chaining, one text in traceback will be changed, and MyProcessError will have new attribute with message from MyError.

try:
    my_function()
exception MyError as err:
    my_logger.exception("Error occured")
    raise MyProcessError("Process failed") from err

The mention change in trace log is replacement of 'during handling of the above exception, another exception occured' to 'The above exception was the direct cause of the following exception'.

So what's the reason to use this syntax? When should I include it into my code? Examples are welcome:-)

In an exception handler, if you explicitly raise an exception you'll either want to use from err or from None (depending on if it makes more sense to suppress the original traceback or not).

Without from , the message you get: "during handling of the above exception, another exception occurred", suggests "something went wrong, and then, while trying to recover from that, something else (possibly unrelated) went wrong. This is the case when you, for example make a typo ( my_logger.expeption("Error occurred") ) or you try to save something to a file which can't be opened.

The with from , it suggests that only a single thing went wrong, that you're not handling yourself but you have some more information that is of use to the programmer or user. For example, you can catch a KeyError and raise a different exception with the message "tried to edit a non-existing widget" or whatever.

So raise SomeException("some description") from err is a way to be able to explain what when wrong if the code that raised err can't do that (because it is from the standard library or because it otherwise lacks the necessary context in which it is called), without having the user thing that two separate things went wrong right after each other.

The statement raise EXCEPTION from CAUSE is equivalent to:

exc = EXCEPTION
exc.__cause__ = CAUSE
raise exc

This feature is sometimes useful for storing information about the exception chain for handling, transmission, or logging at the last level.

For example, when creating our own library, we make our own hierarchy of exceptions with our base class, and we do not want to throw exceptions of third libraries from our functions. But at the same time, we want to save the information about the original exception as a detail.


class ThirdPartyLibException(Exception):
    pass


def third_party_lib_foo():
    ...
    raise ThirdPartyLibException('third party exception')

class MyLibException(Exception):
    pass


def my_lib_foo():
    try:
        third_party_lib_foo()
    except ThirdPartyLibException as e:
        raise MyLibException() from e

try:
    my_lib_foo()
except MyLibException as e:
    print('True reason: ', str(e.__cause__))
    # processing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM