Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping exceptions in Python

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?

like image 487
LeafStorm Avatar asked Oct 02 '10 20:10

LeafStorm


People also ask

What is wrapping an exception?

Exception wrapping is when you catch an exception, wrap it in a different exception and throw that exception. Here is an example: try{ dao.readPerson(); } catch (SQLException sqlException) { throw new MyException("error text", sqlException); }

Should I wrap exceptions?

Most importantly don't fail silently so don't wrap the exceptions in your own exception and then ignore them. Better to allow them to bubble up and find them in the web server log for example.

What are handling exceptions in Python?

In general, when a Python script encounters a situation that it cannot cope with, it raises an exception. An exception is a Python object that represents an error. When a Python script raises an exception, it must either handle the exception immediately otherwise it terminates and quits.


2 Answers

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.

like image 188
AndiDog Avatar answered Oct 15 '22 00:10

AndiDog


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 
like image 24
Jonathan Jin Avatar answered Oct 14 '22 23:10

Jonathan Jin