From 2572c670d42887e4b1d2475b8da2e9bf6e0aa558 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 27 Jan 2022 02:51:06 -0800 Subject: [PATCH] [3.10] bpo-23556: [doc] Fix inaccuracy in documentation for raise without args. Improve tests for context in nested except handlers. (GH-29236) (GH-30953) (cherry picked from commit 08c0ed2d9c0d01ad1a5adc0787bc75e4e90cbb85) Co-authored-by: Kinshuk Dua Automerge-Triggered-By: GH:iritkatriel --- Doc/library/exceptions.rst | 17 ++++++++--------- Doc/reference/simple_stmts.rst | 18 ++++++++++-------- Lib/test/test_raise.py | 22 +++++++++++++++++++--- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index f8a692e8127913..2f97bb8131d78a 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -38,15 +38,14 @@ information on defining exceptions is available in the Python Tutorial under Exception context ----------------- -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 -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 +When raising a new exception while another exception +is already being handled, the new exception's +:attr:`__context__` attribute is automatically set to the handled +exception. An exception may be handled when an :keyword:`except` or +:keyword:`finally` clause, or a :keyword:`with` statement, is used. + +This implicit exception context can be +supplemented with an explicit cause by using :keyword:`!from` with :keyword:`raise`:: raise new_exc from original_exc diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index bb1209dfc33beb..92dfcb0461e60c 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -563,10 +563,10 @@ The :keyword:`!raise` statement .. productionlist:: python-grammar 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 -error. +If no expressions are present, :keyword:`raise` re-raises the +exception that is currently being handled, which is also known as the *active exception*. +If there isn't currently an active exception, a :exc:`RuntimeError` exception is raised +indicating that this is an error. Otherwise, :keyword:`raise` evaluates the first expression as the exception object. It must be either a subclass or an instance of :class:`BaseException`. @@ -581,8 +581,8 @@ The :dfn:`type` of the exception is the exception instance's class, the 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) @@ -614,8 +614,10 @@ exceptions will be printed:: 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 --git a/Lib/test/test_raise.py b/Lib/test/test_raise.py index 8225504c4756db..5936d7535edd5f 100644 --- a/Lib/test/test_raise.py +++ b/Lib/test/test_raise.py @@ -303,7 +303,7 @@ def test_instance_context_instance_raise(self): except: raise OSError() except OSError as e: - self.assertEqual(e.__context__, context) + self.assertIs(e.__context__, context) else: self.fail("No exception raised") @@ -315,7 +315,7 @@ def test_class_context_instance_raise(self): 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") @@ -328,7 +328,7 @@ def test_class_context_class_raise(self): 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") @@ -415,6 +415,22 @@ def test_reraise_cycle_broken(self): except NameError as e: self.assertIsNone(e.__context__.__context__) + 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():