Skip to content

Commit

Permalink
bpo-32489: Allow 'continue' in 'finally' clause.
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka committed Feb 14, 2018
1 parent 24667a4 commit ef8a61d
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 70 deletions.
4 changes: 2 additions & 2 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 7 additions & 6 deletions Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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:
Expand Down
5 changes: 2 additions & 3 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ 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`.)


New Modules
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,14 +862,16 @@ 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:
t
finally:
if z:
break
elif u:
continue
else:
a
else:
Expand Down
9 changes: 0 additions & 9 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 53 additions & 0 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
68 changes: 23 additions & 45 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,19 +298,17 @@
>>> 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):
... try:
... 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):
Expand All @@ -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:
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
A :keyword:`continue` statement is now allowed in the :keyword:`finally`
clause.
4 changes: 0 additions & 4 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2629,10 +2629,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;
}
Expand Down

0 comments on commit ef8a61d

Please sign in to comment.