Commit fe2bbb18 authored by Serhiy Storchaka's avatar Serhiy Storchaka Committed by GitHub

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

parent 134cb01c
...@@ -698,8 +698,8 @@ iterations of the loop. ...@@ -698,8 +698,8 @@ iterations of the loop.
removed from the block stack. removed from the block stack.
It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode
counter nor raise an exception. Used for implementing :keyword:`break` counter nor raise an exception. Used for implementing :keyword:`break`,
and :keyword:`return` in the :keyword:`finally` block. :keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
.. versionadded:: 3.8 .. versionadded:: 3.8
......
...@@ -321,8 +321,8 @@ not handled, the exception is temporarily saved. The :keyword:`finally` clause ...@@ -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 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 :keyword:`finally` clause. If the :keyword:`finally` clause raises another
exception, the saved exception is set as the context of the new exception. 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` If the :keyword:`finally` clause executes a :keyword:`return`, :keyword:`break`
statement, the saved exception is discarded:: or :keyword:`continue` statement, the saved exception is discarded::
>>> def f(): >>> def f():
... try: ... try:
...@@ -343,10 +343,7 @@ the :keyword:`finally` clause. ...@@ -343,10 +343,7 @@ the :keyword:`finally` clause.
When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is
executed in the :keyword:`try` suite of a :keyword:`try`...\ :keyword:`finally` 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 statement, the :keyword:`finally` clause is also executed 'on the way out.'
: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).
The return value of a function is determined by the last :keyword:`return` The return value of a function is determined by the last :keyword:`return`
statement executed. Since the :keyword:`finally` clause always executes, a 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`, ...@@ -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 and information on using the :keyword:`raise` statement to generate exceptions
may be found in section :ref:`raise`. 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: .. _with:
.. _as: .. _as:
......
...@@ -686,9 +686,8 @@ The :keyword:`continue` statement ...@@ -686,9 +686,8 @@ The :keyword:`continue` statement
continue_stmt: "continue" continue_stmt: "continue"
:keyword:`continue` may only occur syntactically nested in a :keyword:`for` or :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:`while` loop, but not nested in a function or class definition within
:keyword:`finally` clause within that loop. It continues with the next that loop. It continues with the next cycle of the nearest enclosing loop.
cycle of the nearest enclosing loop.
When :keyword:`continue` passes control out of a :keyword:`try` statement with a When :keyword:`continue` passes control out of a :keyword:`try` statement with a
:keyword:`finally` clause, that :keyword:`finally` clause is executed before :keyword:`finally` clause, that :keyword:`finally` clause is executed before
......
...@@ -72,6 +72,11 @@ New Features ...@@ -72,6 +72,11 @@ New Features
Other Language Changes 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>`. * Added support of ``\N{name}`` escapes in :mod:`regular expressions <re>`.
(Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.) (Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.)
......
...@@ -856,7 +856,7 @@ class TestStackSizeStability(unittest.TestCase): ...@@ -856,7 +856,7 @@ class TestStackSizeStability(unittest.TestCase):
""" """
self.check_stack_size(snippet) self.check_stack_size(snippet)
def test_for_break_inside_finally_block(self): def test_for_break_continue_inside_finally_block(self):
snippet = """ snippet = """
for x in y: for x in y:
try: try:
...@@ -864,6 +864,8 @@ class TestStackSizeStability(unittest.TestCase): ...@@ -864,6 +864,8 @@ class TestStackSizeStability(unittest.TestCase):
finally: finally:
if z: if z:
break break
elif u:
continue
else: else:
a a
else: else:
......
...@@ -138,15 +138,6 @@ class ExceptionTests(unittest.TestCase): ...@@ -138,15 +138,6 @@ class ExceptionTests(unittest.TestCase):
else: else:
self.fail("failed to get expected SyntaxError") 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: s = '''if 1:
try: try:
continue continue
......
...@@ -859,6 +859,59 @@ class GrammarTests(unittest.TestCase): ...@@ -859,6 +859,59 @@ class GrammarTests(unittest.TestCase):
break break
self.assertEqual(count, 0) 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 test_return_in_finally(self):
def g1(): def g1():
try: try:
......
...@@ -298,7 +298,7 @@ continue in for loop under finally should be ok. ...@@ -298,7 +298,7 @@ continue in for loop under finally should be ok.
>>> test() >>> test()
9 9
Start simple, a continue in a finally should not be allowed. continue in a finally should be ok.
>>> def test(): >>> def test():
... for abc in range(10): ... for abc in range(10):
...@@ -306,11 +306,9 @@ Start simple, a continue in a finally should not be allowed. ...@@ -306,11 +306,9 @@ Start simple, a continue in a finally should not be allowed.
... pass ... pass
... finally: ... finally:
... continue ... continue
Traceback (most recent call last): ... print(abc)
... >>> test()
SyntaxError: 'continue' not supported inside 'finally' clause 9
This is essentially a continue in a finally which should not be allowed.
>>> def test(): >>> def test():
... for abc in range(10): ... for abc in range(10):
...@@ -321,53 +319,33 @@ This is essentially a continue in a finally which should not be allowed. ...@@ -321,53 +319,33 @@ This is essentially a continue in a finally which should not be allowed.
... continue ... continue
... except: ... except:
... pass ... pass
Traceback (most recent call last): ... print(abc)
... >>> test()
SyntaxError: 'continue' not supported inside 'finally' clause 9
>>> def foo(): >>> def test():
... for abc in range(10):
... try: ... try:
... pass ... pass
... finally: ... finally:
... continue
Traceback (most recent call last):
...
SyntaxError: 'continue' not supported inside 'finally' clause
>>> def foo():
... for a in ():
... try: ... try:
... pass ... pass
... finally: ... except:
... continue ... continue
Traceback (most recent call last): ... print(abc)
... >>> test()
SyntaxError: 'continue' not supported inside 'finally' clause 9
>>> def foo(): A continue outside loop should not be allowed.
... for a in ():
... try:
... pass
... finally:
... try:
... continue
... finally:
... pass
Traceback (most recent call last):
...
SyntaxError: 'continue' not supported inside 'finally' clause
>>> def foo(): >>> def foo():
... for a in ():
... try: pass
... finally:
... try: ... try:
... pass ... pass
... except: ... finally:
... continue ... continue
Traceback (most recent call last): 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 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, uses a single data structure to keep track of try-finally and loops,
......
A :keyword:`continue` statement is now allowed in the :keyword:`finally`
clause.
...@@ -2625,10 +2625,6 @@ compiler_continue(struct compiler *c) ...@@ -2625,10 +2625,6 @@ compiler_continue(struct compiler *c)
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block); ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
return 1; 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)) if (!compiler_unwind_fblock(c, info, 0))
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment