开发者

Wrapping exceptions in Python

开发者 https://www.devze.com 2023-01-18 07:07 出处:网络
I\'m working on a mail-sending library, and I开发者_如何学编程 want to be able to catch exceptions produced by the senders (SMTP, Google AppEngine, etc.) and wrap them in easily catchable exceptions s

I'm working on a mail-sending library, and I开发者_如何学编程 want to be able to catch exceptions produced by the senders (SMTP, Google AppEngine, etc.) and wrap them in easily catchable exceptions specific to my library (ConnectionError, MessageSendError, etc.), with the original traceback intact so it can be debugged. What is the best way to do this in Python 2?


The simplest way would be to reraise with the old trace object. The following example shows this:

import sys

def a():
    def b():
        raise AssertionError("1")
    b()

try:
    a()
except AssertionError: # some specific exception you want to wrap
    trace = sys.exc_info()[2]
    raise Exception("error description"), None, trace

Check the documentation of the raise statement for details of the three parameters. My example would print:

Traceback (most recent call last):
  File "C:\...\test.py", line 9, in <module>
    a()
  File "C:\...\test.py", line 6, in a
    b()
  File "C:\...\test.py", line 5, in b
    raise AssertionError("1")
Exception: error description

For completeness, in Python 3 you'd use the raise MyException(...) from e syntax.


Use raise_from from the future.utils package.

Relevant example copied below:

from future.utils import raise_from

class FileDatabase:
    def __init__(self, filename):
        try:
            self.file = open(filename)
        except IOError as exc:
            raise_from(DatabaseError('failed to open'), exc)

Within that package, raise_from is implemented as follows:

def raise_from(exc, cause):
    """
    Equivalent to:

        raise EXCEPTION from CAUSE

    on Python 3. (See PEP 3134).
    """
    # Is either arg an exception class (e.g. IndexError) rather than
    # instance (e.g. IndexError('my message here')? If so, pass the
    # name of the class undisturbed through to "raise ... from ...".
    if isinstance(exc, type) and issubclass(exc, Exception):
        e = exc()
        # exc = exc.__name__
        # execstr = "e = " + _repr_strip(exc) + "()"
        # myglobals, mylocals = _get_caller_globals_and_locals()
        # exec(execstr, myglobals, mylocals)
    else:
        e = exc
    e.__suppress_context__ = False
    if isinstance(cause, type) and issubclass(cause, Exception):
        e.__cause__ = cause()
        e.__suppress_context__ = True
    elif cause is None:
        e.__cause__ = None
        e.__suppress_context__ = True
    elif isinstance(cause, BaseException):
        e.__cause__ = cause
        e.__suppress_context__ = True
    else:
        raise TypeError("exception causes must derive from BaseException")
    e.__context__ = sys.exc_info()[1]
    raise e


This answer is probably a little bit late, but you can wrap the function in a python decorator.

Here is a simple cheatsheet on how different decorators.

Here is some sample code of how to do this. Just change the decorator to catch different errors in the different ways that you need.

def decorator(wrapped_function):
    def _wrapper(*args, **kwargs):
        try:
            # do something before the function call
            result = wrapped_function(*args, **kwargs)
            # do something after the function call
        except TypeError:
            print("TypeError")
        except IndexError:
            print("IndexError")
        # return result
    return _wrapper


@decorator
def type_error():
    return 1 / 'a'

@decorator
def index_error():
    return ['foo', 'bar'][5]


type_error()
index_error()
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号