# HG changeset patch # Parent 256d5c7146cb968e9d6e200c99b0a1ec9e2faf14 diff -r 256d5c7146cb Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst Wed Jul 29 12:55:33 2015 +1200 +++ b/Doc/library/exceptions.rst Wed Jul 29 09:08:02 2015 +0000 @@ -34,20 +34,21 @@ information on defining exceptions is available in the Python Tutorial under :ref:`tut-userexceptions`. -When raising (or re-raising) an exception in an :keyword:`except` or -:keyword:`finally` clause -:attr:`__context__` is automatically set to the last exception caught; if the -new exception is not handled the traceback that is eventually displayed will +When raising a new exception and an exception +is already being handled, the new exception's +:attr:`__context__` attribute is automatically set to the originating +exception. An exception may be handled when an :keyword:`except` or +:keyword:`finally` clause, or a :keyword:`with` statement, is used. If the +new exception is not handled, the traceback that is eventually displayed may include the originating exception(s) and the final exception. -When raising a new exception (rather than using a bare ``raise`` to re-raise -the exception currently being handled), the implicit exception context can be -supplemented with an explicit cause by using :keyword:`from` with +This implicit exception context can be +supplemented with an explicit cause by using :keyword:`!from` with :keyword:`raise`:: raise new_exc from original_exc -The expression following :keyword:`from` must be an exception or ``None``. It +The expression following :keyword:`!from` must be an exception or ``None``. It will be set as :attr:`__cause__` on the raised exception. Setting :attr:`__cause__` also implicitly sets the :attr:`__suppress_context__` attribute to ``True``, so that using ``raise new_exc from None`` diff -r 256d5c7146cb Doc/reference/simple_stmts.rst --- a/Doc/reference/simple_stmts.rst Wed Jul 29 12:55:33 2015 +1200 +++ b/Doc/reference/simple_stmts.rst Wed Jul 29 09:08:02 2015 +0000 @@ -499,9 +499,9 @@ .. productionlist:: raise_stmt: "raise" [`expression` ["from" `expression`]] -If no expressions are present, :keyword:`raise` re-raises the last exception -that was active in the current scope. If no exception is active in the current -scope, a :exc:`RuntimeError` exception is raised indicating that this is an +If no expressions are present, :keyword:`raise` re-raises the +exception that is being handled. If no exception is being handled, +a :exc:`RuntimeError` exception is raised indicating that this is an error. Otherwise, :keyword:`raise` evaluates the first expression as the exception @@ -517,8 +517,8 @@ A traceback object is normally created automatically when an exception is raised and attached to it as the :attr:`__traceback__` attribute, which is writable. You can create an exception and set your own traceback in one step using the -:meth:`with_traceback` exception method (which returns the same exception -instance, with its traceback set to its argument), like so:: +:meth:`~BaseException.with_traceback` exception method (which returns the +same exception instance, with its traceback set to its argument), like so:: raise Exception("foo occurred").with_traceback(tracebackobj) @@ -547,8 +547,10 @@ File "", line 4, in RuntimeError: Something bad happened -A similar mechanism works implicitly if an exception is raised inside an -exception handler or a :keyword:`finally` clause: the previous exception is then +A similar mechanism works implicitly if a new exception is raised when +an exception is already being handled. An exception may be handled +when an :keyword:`except` or :keyword:`finally` clause, or a +:keyword:`with` statement, is used. The previous exception is then attached as the new exception's :attr:`__context__` attribute:: >>> try: diff -r 256d5c7146cb Lib/test/test_raise.py --- a/Lib/test/test_raise.py Wed Jul 29 12:55:33 2015 +1200 +++ b/Lib/test/test_raise.py Wed Jul 29 09:08:02 2015 +0000 @@ -238,7 +238,7 @@ except: raise OSError() except OSError as e: - self.assertEqual(e.__context__, context) + self.assertIs(e.__context__, context) else: self.fail("No exception raised") @@ -250,7 +250,7 @@ except: raise OSError() except OSError as e: - self.assertNotEqual(e.__context__, context) + self.assertIsNot(e.__context__, context) self.assertIsInstance(e.__context__, context) else: self.fail("No exception raised") @@ -263,7 +263,7 @@ except: raise OSError except OSError as e: - self.assertNotEqual(e.__context__, context) + self.assertIsNot(e.__context__, context) self.assertIsInstance(e.__context__, context) else: self.fail("No exception raised") @@ -334,7 +334,7 @@ except ZeroDivisionError as e: raise e except ZeroDivisionError as e: - self.assertTrue(e.__context__ is None, e.__context__) + self.assertIsNone(e.__context__) def test_reraise_cycle_broken(self): # Non-trivial context cycles (through re-raising a previous exception) @@ -350,6 +350,22 @@ except NameError as e: self.assertTrue(e.__context__.__context__ is None) + def test_not_last(self): + # Context is not necessarily the last exception + context = Exception("context") + try: + raise context + except Exception: + try: + raise Exception("caught") + except Exception: + pass + try: + raise Exception("new") + except Exception as exc: + raised = exc + self.assertIs(raised.__context__, context) + def test_3118(self): # deleting the generator caused the __context__ to be cleared def gen():