[Python-checkins] bpo-32489: Allow 'continue' in 'finally' clause. (GH-5822)

Serhiy Storchaka webhook-mailer at python.org
Sun Mar 18 03:56:55 EDT 2018


https://github.com/python/cpython/commit/fe2bbb1869b42222a3f331a3dfb8b304a19a5819
commit: fe2bbb1869b42222a3f331a3dfb8b304a19a5819
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2018-03-18T09:56:52+02:00
summary:

bpo-32489: Allow 'continue' in 'finally' clause. (GH-5822)

files:
A Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst
M Doc/library/dis.rst
M Doc/reference/compound_stmts.rst
M Doc/reference/simple_stmts.rst
M Doc/whatsnew/3.8.rst
M Lib/test/test_compile.py
M Lib/test/test_exceptions.py
M Lib/test/test_grammar.py
M Lib/test/test_syntax.py
M Python/compile.c

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index aa66128d60dd..47f226bd1625 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -698,8 +698,8 @@ iterations of the loop.
      removed from the block stack.
 
    It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode
-   counter nor raise an exception.  Used for implementing :keyword:`break`
-   and :keyword:`return` in the :keyword:`finally` block.
+   counter nor raise an exception.  Used for implementing :keyword:`break`,
+   :keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
 
    .. versionadded:: 3.8
 
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index d7792f1eaf82..153e85b39491 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -321,8 +321,8 @@ not handled, the exception is temporarily saved. The :keyword:`finally` clause
 is executed.  If there is a saved exception it is re-raised at the end of the
 :keyword:`finally` clause.  If the :keyword:`finally` clause raises another
 exception, the saved exception is set as the context of the new exception.
-If the :keyword:`finally` clause executes a :keyword:`return` or :keyword:`break`
-statement, the saved exception is discarded::
+If the :keyword:`finally` clause executes a :keyword:`return`, :keyword:`break`
+or :keyword:`continue` statement, the saved exception is discarded::
 
    >>> def f():
    ...     try:
@@ -343,10 +343,7 @@ the :keyword:`finally` clause.
 
 When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is
 executed in the :keyword:`try` suite of a :keyword:`try`...\ :keyword:`finally`
-statement, the :keyword:`finally` clause is also executed 'on the way out.' A
-:keyword:`continue` statement is illegal in the :keyword:`finally` clause. (The
-reason is a problem with the current implementation --- this restriction may be
-lifted in the future).
+statement, the :keyword:`finally` clause is also executed 'on the way out.'
 
 The return value of a function is determined by the last :keyword:`return`
 statement executed.  Since the :keyword:`finally` clause always executes, a
@@ -366,6 +363,10 @@ Additional information on exceptions can be found in section :ref:`exceptions`,
 and information on using the :keyword:`raise` statement to generate exceptions
 may be found in section :ref:`raise`.
 
+.. versionchanged:: 3.8
+   Prior to Python 3.8, a :keyword:`continue` statement was illegal in the
+   :keyword:`finally` clause due to a problem with the implementation.
+
 
 .. _with:
 .. _as:
diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst
index ef9a5f0dc854..9186210d951f 100644
--- a/Doc/reference/simple_stmts.rst
+++ b/Doc/reference/simple_stmts.rst
@@ -686,9 +686,8 @@ The :keyword:`continue` statement
    continue_stmt: "continue"
 
 :keyword:`continue` may only occur syntactically nested in a :keyword:`for` or
-:keyword:`while` loop, but not nested in a function or class definition or
-:keyword:`finally` clause within that loop.  It continues with the next
-cycle of the nearest enclosing loop.
+:keyword:`while` loop, but not nested in a function or class definition within
+that loop.  It continues with the next cycle of the nearest enclosing loop.
 
 When :keyword:`continue` passes control out of a :keyword:`try` statement with a
 :keyword:`finally` clause, that :keyword:`finally` clause is executed before
diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst
index 8569341055e1..fcc868b041ad 100644
--- a/Doc/whatsnew/3.8.rst
+++ b/Doc/whatsnew/3.8.rst
@@ -72,6 +72,11 @@ New Features
 Other Language Changes
 ======================
 
+* A :keyword:`continue` statement was illegal in the :keyword:`finally` clause
+  due to a problem with the implementation.  In Python 3.8 this restriction
+  was lifted.
+  (Contributed by Serhiy Storchaka in :issue:`32489`.)
+
 * Added support of ``\N{name}`` escapes in :mod:`regular expressions <re>`.
   (Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.)
 
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index acebdbdc4636..6b45a2433438 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -856,7 +856,7 @@ def test_for_break_continue_inside_try_finally_block(self):
             """
         self.check_stack_size(snippet)
 
-    def test_for_break_inside_finally_block(self):
+    def test_for_break_continue_inside_finally_block(self):
         snippet = """
             for x in y:
                 try:
@@ -864,6 +864,8 @@ def test_for_break_inside_finally_block(self):
                 finally:
                     if z:
                         break
+                    elif u:
+                        continue
                     else:
                         a
             else:
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 9d10df5f9425..2a9ec706467f 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -138,15 +138,6 @@ def ckmsg(src, msg):
             else:
                 self.fail("failed to get expected SyntaxError")
 
-        s = '''while 1:
-            try:
-                pass
-            finally:
-                continue'''
-
-        if not sys.platform.startswith('java'):
-            ckmsg(s, "'continue' not supported inside 'finally' clause")
-
         s = '''if 1:
         try:
             continue
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index d89bfdc06335..ee4136286ba1 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -859,6 +859,59 @@ def test_break_in_finally(self):
                 break
         self.assertEqual(count, 0)
 
+    def test_continue_in_finally(self):
+        count = 0
+        while count < 2:
+            count += 1
+            try:
+                pass
+            finally:
+                continue
+            break
+        self.assertEqual(count, 2)
+
+        count = 0
+        while count < 2:
+            count += 1
+            try:
+                break
+            finally:
+                continue
+        self.assertEqual(count, 2)
+
+        count = 0
+        while count < 2:
+            count += 1
+            try:
+                1/0
+            finally:
+                continue
+            break
+        self.assertEqual(count, 2)
+
+        for count in [0, 1]:
+            try:
+                pass
+            finally:
+                continue
+            break
+        self.assertEqual(count, 1)
+
+        for count in [0, 1]:
+            try:
+                break
+            finally:
+                continue
+        self.assertEqual(count, 1)
+
+        for count in [0, 1]:
+            try:
+                1/0
+            finally:
+                continue
+            break
+        self.assertEqual(count, 1)
+
     def test_return_in_finally(self):
         def g1():
             try:
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 2b96a94401a8..fa1e7aa5d4f2 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -298,7 +298,7 @@
     >>> test()
     9
 
-Start simple, a continue in a finally should not be allowed.
+continue in a finally should be ok.
 
     >>> def test():
     ...    for abc in range(10):
@@ -306,11 +306,9 @@
     ...            pass
     ...        finally:
     ...            continue
-    Traceback (most recent call last):
-      ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
-
-This is essentially a continue in a finally which should not be allowed.
+    ...    print(abc)
+    >>> test()
+    9
 
     >>> def test():
     ...    for abc in range(10):
@@ -321,9 +319,24 @@
     ...                continue
     ...            except:
     ...                pass
-    Traceback (most recent call last):
-      ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
+    ...    print(abc)
+    >>> test()
+    9
+
+    >>> def test():
+    ...    for abc in range(10):
+    ...        try:
+    ...            pass
+    ...        finally:
+    ...            try:
+    ...                pass
+    ...            except:
+    ...                continue
+    ...    print(abc)
+    >>> test()
+    9
+
+A continue outside loop should not be allowed.
 
     >>> def foo():
     ...     try:
@@ -332,42 +345,7 @@
     ...         continue
     Traceback (most recent call last):
       ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
-
-    >>> def foo():
-    ...     for a in ():
-    ...       try:
-    ...           pass
-    ...       finally:
-    ...           continue
-    Traceback (most recent call last):
-      ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
-
-    >>> def foo():
-    ...     for a in ():
-    ...         try:
-    ...             pass
-    ...         finally:
-    ...             try:
-    ...                 continue
-    ...             finally:
-    ...                 pass
-    Traceback (most recent call last):
-      ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
-
-    >>> def foo():
-    ...  for a in ():
-    ...   try: pass
-    ...   finally:
-    ...    try:
-    ...     pass
-    ...    except:
-    ...     continue
-    Traceback (most recent call last):
-      ...
-    SyntaxError: 'continue' not supported inside 'finally' clause
+    SyntaxError: 'continue' not properly in loop
 
 There is one test for a break that is not in a loop.  The compiler
 uses a single data structure to keep track of try-finally and loops,
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst
new file mode 100644
index 000000000000..68babebdad3c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst	
@@ -0,0 +1,2 @@
+A :keyword:`continue` statement is now allowed in the :keyword:`finally`
+clause.
diff --git a/Python/compile.c b/Python/compile.c
index fbd1fc960ab8..c3ffaae8ce20 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -2625,10 +2625,6 @@ compiler_continue(struct compiler *c)
             ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
             return 1;
         }
-        if (info->fb_type == FINALLY_END) {
-            return compiler_error(c,
-                    "'continue' not supported inside 'finally' clause");
-        }
         if (!compiler_unwind_fblock(c, info, 0))
             return 0;
     }



More information about the Python-checkins mailing list