Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,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`,
:keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
counter nor raise an exception. Used for implementing :keyword:`break`
and :keyword:`return` in the :keyword:`finally` block.

.. versionadded:: 3.8

Expand Down
13 changes: 6 additions & 7 deletions Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,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`, :keyword:`break`
or :keyword:`continue` statement, the saved exception is discarded::
If the :keyword:`!finally` clause executes a :keyword:`return` or :keyword:`break`
statement, the saved exception is discarded::

>>> def f():
... try:
Expand All @@ -347,7 +347,10 @@ 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.'
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).

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 @@ -367,10 +370,6 @@ 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: 3 additions & 2 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -702,8 +702,9 @@ 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 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 or
:keyword:`finally` clause 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
5 changes: 0 additions & 5 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -379,11 +379,6 @@ See :pep:`574` for a full description.
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`.)

* The :class:`bool`, :class:`int`, and :class:`fractions.Fraction` types
now have an :meth:`~int.as_integer_ratio` method like that found in
:class:`float` and :class:`decimal.Decimal`. This minor API extension
Expand Down
4 changes: 1 addition & 3 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,16 +917,14 @@ def test_for_break_continue_inside_try_finally_block(self):
"""
self.check_stack_size(snippet)

def test_for_break_continue_inside_finally_block(self):
def test_for_break_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: 9 additions & 0 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ 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: 0 additions & 53 deletions Lib/test/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,59 +916,6 @@ 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: 45 additions & 23 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,17 +354,19 @@
>>> test()
9

continue in a finally should be ok.
Start simple, a continue in a finally should not be allowed.

>>> def test():
... for abc in range(10):
... try:
... pass
... finally:
... continue
... print(abc)
>>> test()
9
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.

>>> def test():
... for abc in range(10):
Expand All @@ -375,24 +377,9 @@
... continue
... except:
... pass
... 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.
Traceback (most recent call last):
...
SyntaxError: 'continue' not supported inside 'finally' clause

>>> def foo():
... try:
Expand All @@ -401,7 +388,42 @@
... continue
Traceback (most recent call last):
...
SyntaxError: 'continue' not properly in loop
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

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 @@
Reverted :issue:`32489`: "Allow 'continue' in 'finally' clause."
4 changes: 4 additions & 0 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,10 @@ 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