As an antidote to the ill-informed negativity of Ranting Rick's illusionary "PyWarts", I thought I'd present a few of Python's more awesome features, starting with exception contexts.
If you've ever written an exception handler, you've probably written a *buggy* exception handler: def getitem(items, index): # One-based indexing. try: return items[index-1] except IndexError: print ("Item at index %d is missing" % index - 1) # Oops! Unfortunately, when an exception occurs inside an except or finally block, the second exception masks the first, and the reason for the original exception is lost: py> getitem(['one', 'two', 'three'], 5) # Python 2.6 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in getitem TypeError: unsupported operand type(s) for -: 'str' and 'int' But never fear! In Python 3.1 and better, Python now shows you the full chain of multiple exceptions, and exceptions grow two new special attributes: __cause__ and __context__. If an exception occurs while handling another exception, Python sets the exception's __context__ and displays an extended error message: py> getitem(['one', 'two', 'three'], 5) # Python 3.1 Traceback (most recent call last): File "<stdin>", line 4, in getitem IndexError: list index out of range During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in getitem TypeError: unsupported operand type(s) for -: 'str' and 'int' Python 3 also allows you to explicitly set the exception's __cause__ using "raise...from" syntax: py> try: ... len(None) ... except TypeError as e: ... raise ValueError('bad value') from e ... Traceback (most recent call last): File "<stdin>", line 2, in <module> TypeError: object of type 'NoneType' has no len() The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> ValueError: bad value Note the slight difference in error message. If both __cause__ and __context__ are set, the __cause__ takes priority. Sometimes you actually want to deliberately catch one exception and raise another, without showing the first exception. A very common idiom in Python 2: try: do_work() except SomeInternalError: raise PublicError(error_message) Starting with Python 3.3, there is now support from intentionally suppressing the __context__: py> try: ... len(None) ... except TypeError: ... raise ValueError('bad value') from None # Python 3.3 ... Traceback (most recent call last): File "<stdin>", line 4, in <module> ValueError: bad value You can read more about exception chaining here: http://www.python.org/dev/peps/pep-3134/ http://www.python.org/dev/peps/pep-0409/ -- Steven -- http://mail.python.org/mailman/listinfo/python-list