Learning Python - Mark Lutz [460]
Python 3.0 Exception Chaining: raise from
Python 3.0 (but not 2.6) also allows raise statements to have an optional from clause:
raise exception from otherexception
When the from is used, the second expression specifies another exception class or instance to attach to the raised exception’s __cause__ attribute. If the raised exception is not caught, Python prints both exceptions as part of the standard error message:
>>> try:
... 1 / 0
... except Exception as E:
... raise TypeError('Bad!') from E
...
Traceback (most recent call last):
File " ZeroDivisionError: int division or modulo by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File " TypeError: Bad! When an exception is raised inside an exception handler, a similar procedure is followed implicitly: the previous exception is attached to the new exception’s __context__ attribute and is again displayed in the standard error message if the exception goes uncaught. This is an advanced and still somewhat obscure extension, so see Python’s manuals for more details. * * * Note * * * The assert Statement As a somewhat special case for debugging purposes, Python includes the assert statement. It is mostly just syntactic shorthand for a common raise usage pattern, and an assert can be thought of as a conditional raise statement. A statement of the form: assert works like the following code: if __debug__: if not raise AssertionError() In other words, if the test evaluates to false, Python raises an exception: the data item (if it’s provided) is used as the exception’s constructor argument. Like all exceptions, the AssertionError exception will kill your program if it’s not caught with a try, in which case the data item shows up as part of the error message. As an added feature, assert statements may be removed from a compiled program’s byte code if the -O Python command-line flag is used, thereby optimizing the program. AssertionError is a built-in exception, and the __debug__ flag is a built-in name that is automatically set to True unless the -O flag is used. Use a command line like python –O main.py to run in optimized mode and disable asserts. Example: Trapping Constraints (but Not Errors!) Assertions are typically used to verify program conditions during development. When displayed, their error message text automatically includes source code line information and the value listed in the assert statement. Consider the file asserter.py: def f(x): assert x < 0, 'x must be negative' return x ** 2 % python >>> import asserter >>> asserter.f(1) Traceback (most recent call last): File " File "asserter.py", line 2, in f assert x < 0, 'x must be negative' AssertionError: x must be negative It’s important to keep in mind that assert is mostly intended for trapping user-defined constraints, not for catching genuine programming errors. Because Python traps programming errors itself, there is usually no need to code asserts to catch things like out-of-bounds indexes, type mismatches, and zero divides: def reciprocal(x): assert x != 0 # A useless assert!
Version skew note: Python 3.0 no longer supports the raise Exc, Args form that is still available in Python 2.6. In 3.0, use the raise Exc(Args) instance-creation call form described in this book instead. The equivalent comma form in 2.6 is legacy syntax provided for compatibility with the now defunct string-based exceptions model, and it’s deprecated in 2.6. If used, it is converted to the 3.0 call form. As in earlier releases, a raise Exc form is also allowed—it is converted to raise Exc() in both versions, calling the class constructor with no arguments.