Context Suppression in Python Exceptions
It is sometimes useful to catch one exception and raise another exception. For example, a higher software layer may not care where exactly an algorithm fails, but merely that the input is invalid. Or it may not care which parsing routine fails, just that the data was truncated.
For example:
def process(iterable): try: x = next(iterable) except StopIteration: raise ValueError("can't process empty iterable") continue_processing()
def reciprocal(x): try: return 1/x except ZeroDivisionError: raise ValueError("divisor must not be zero")
In Python 2, this only displays the inner exception:
Traceback (most recent call last): File "test.py", line 12, in <module> reciprocal(0) File "test.py", line 10, in reciprocal raise ValueError("divisor must not be zero") ValueError: divisor must not be zero
In Python 3, this displays the context of the exception as well:
Traceback (most recent call last): File "test.py", line 8, in reciprocal return 1/x ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "test.py", line 12, in <module> reciprocal(0) File "test.py", line 10, in reciprocal raise ValueError("divisor must not be zero") ValueError: divisor must not be zero
Similarly, in Python 3 it is possible to set the cause of an Exception:
raise ValueError("divisor must not be zero") from ZeroDivisionError('division by zero')
which prints:
ZeroDivisionError: division by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "test.py", line 13, in <module> raise ValueError("divisor must not be zero") from ZeroDivisionError('division by zero') ValueError: divisor must not be zero
In Python 3.3 is possible to suppress also suppress the context by setting the cause to None:
def reciprocal(x): try: return 1/x except ZeroDivisionError: raise ValueError("divisor must not be zero") from None
which prints:
Traceback (most recent call last): File "test.py", line 12, in <module> reciprocal(0) File "test.py", line 10, in reciprocal raise ValueError("divisor must not be zero") from None ValueError: divisor must not be zero
Note that this is not valid Python 2 syntax. If you want to write your code such that it works with Python 2, the following syntax is equivalent:
def reciprocal(x): try: return 1/x except ZeroDivisionError: _exception = ValueError("divisor must not be zero") _exception.__cause__ = None raise _exception
However, this will still print the exception in Python 3.1 and Python 3.2. If you want to suppress the context there too, the following hack-ish solutions works accross all Python versions (2.6, 2.7 and 3):
_exception = None try: i = 1/0 except ZeroDivisionError as e: _exception = ValueError("divisor must not be zero") finally: if _exception: raise _exception