Skip to content

Commit fe2bbb1

Browse files
bpo-32489: Allow 'continue' in 'finally' clause. (GH-5822)
1 parent 134cb01 commit fe2bbb1

File tree

10 files changed

+97
-70
lines changed

10 files changed

+97
-70
lines changed

Doc/library/dis.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,8 @@ iterations of the loop.
698698
removed from the block stack.
699699

700700
It is similar to :opcode:`END_FINALLY`, but doesn't change the bytecode
701-
counter nor raise an exception. Used for implementing :keyword:`break`
702-
and :keyword:`return` in the :keyword:`finally` block.
701+
counter nor raise an exception. Used for implementing :keyword:`break`,
702+
:keyword:`continue` and :keyword:`return` in the :keyword:`finally` block.
703703

704704
.. versionadded:: 3.8
705705

Doc/reference/compound_stmts.rst

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,8 @@ not handled, the exception is temporarily saved. The :keyword:`finally` clause
321321
is executed. If there is a saved exception it is re-raised at the end of the
322322
:keyword:`finally` clause. If the :keyword:`finally` clause raises another
323323
exception, the saved exception is set as the context of the new exception.
324-
If the :keyword:`finally` clause executes a :keyword:`return` or :keyword:`break`
325-
statement, the saved exception is discarded::
324+
If the :keyword:`finally` clause executes a :keyword:`return`, :keyword:`break`
325+
or :keyword:`continue` statement, the saved exception is discarded::
326326

327327
>>> def f():
328328
... try:
@@ -343,10 +343,7 @@ the :keyword:`finally` clause.
343343

344344
When a :keyword:`return`, :keyword:`break` or :keyword:`continue` statement is
345345
executed in the :keyword:`try` suite of a :keyword:`try`...\ :keyword:`finally`
346-
statement, the :keyword:`finally` clause is also executed 'on the way out.' A
347-
:keyword:`continue` statement is illegal in the :keyword:`finally` clause. (The
348-
reason is a problem with the current implementation --- this restriction may be
349-
lifted in the future).
346+
statement, the :keyword:`finally` clause is also executed 'on the way out.'
350347

351348
The return value of a function is determined by the last :keyword:`return`
352349
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`,
366363
and information on using the :keyword:`raise` statement to generate exceptions
367364
may be found in section :ref:`raise`.
368365

366+
.. versionchanged:: 3.8
367+
Prior to Python 3.8, a :keyword:`continue` statement was illegal in the
368+
:keyword:`finally` clause due to a problem with the implementation.
369+
369370

370371
.. _with:
371372
.. _as:

Doc/reference/simple_stmts.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -686,9 +686,8 @@ The :keyword:`continue` statement
686686
continue_stmt: "continue"
687687

688688
:keyword:`continue` may only occur syntactically nested in a :keyword:`for` or
689-
:keyword:`while` loop, but not nested in a function or class definition or
690-
:keyword:`finally` clause within that loop. It continues with the next
691-
cycle of the nearest enclosing loop.
689+
:keyword:`while` loop, but not nested in a function or class definition within
690+
that loop. It continues with the next cycle of the nearest enclosing loop.
692691

693692
When :keyword:`continue` passes control out of a :keyword:`try` statement with a
694693
:keyword:`finally` clause, that :keyword:`finally` clause is executed before

Doc/whatsnew/3.8.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ New Features
7272
Other Language Changes
7373
======================
7474

75+
* A :keyword:`continue` statement was illegal in the :keyword:`finally` clause
76+
due to a problem with the implementation. In Python 3.8 this restriction
77+
was lifted.
78+
(Contributed by Serhiy Storchaka in :issue:`32489`.)
79+
7580
* Added support of ``\N{name}`` escapes in :mod:`regular expressions <re>`.
7681
(Contributed by Jonathan Eunice and Serhiy Storchaka in :issue:`30688`.)
7782

Lib/test/test_compile.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,14 +856,16 @@ def test_for_break_continue_inside_try_finally_block(self):
856856
"""
857857
self.check_stack_size(snippet)
858858

859-
def test_for_break_inside_finally_block(self):
859+
def test_for_break_continue_inside_finally_block(self):
860860
snippet = """
861861
for x in y:
862862
try:
863863
t
864864
finally:
865865
if z:
866866
break
867+
elif u:
868+
continue
867869
else:
868870
a
869871
else:

Lib/test/test_exceptions.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,6 @@ def ckmsg(src, msg):
138138
else:
139139
self.fail("failed to get expected SyntaxError")
140140

141-
s = '''while 1:
142-
try:
143-
pass
144-
finally:
145-
continue'''
146-
147-
if not sys.platform.startswith('java'):
148-
ckmsg(s, "'continue' not supported inside 'finally' clause")
149-
150141
s = '''if 1:
151142
try:
152143
continue

Lib/test/test_grammar.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,59 @@ def test_break_in_finally(self):
859859
break
860860
self.assertEqual(count, 0)
861861

862+
def test_continue_in_finally(self):
863+
count = 0
864+
while count < 2:
865+
count += 1
866+
try:
867+
pass
868+
finally:
869+
continue
870+
break
871+
self.assertEqual(count, 2)
872+
873+
count = 0
874+
while count < 2:
875+
count += 1
876+
try:
877+
break
878+
finally:
879+
continue
880+
self.assertEqual(count, 2)
881+
882+
count = 0
883+
while count < 2:
884+
count += 1
885+
try:
886+
1/0
887+
finally:
888+
continue
889+
break
890+
self.assertEqual(count, 2)
891+
892+
for count in [0, 1]:
893+
try:
894+
pass
895+
finally:
896+
continue
897+
break
898+
self.assertEqual(count, 1)
899+
900+
for count in [0, 1]:
901+
try:
902+
break
903+
finally:
904+
continue
905+
self.assertEqual(count, 1)
906+
907+
for count in [0, 1]:
908+
try:
909+
1/0
910+
finally:
911+
continue
912+
break
913+
self.assertEqual(count, 1)
914+
862915
def test_return_in_finally(self):
863916
def g1():
864917
try:

Lib/test/test_syntax.py

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -298,19 +298,17 @@
298298
>>> test()
299299
9
300300
301-
Start simple, a continue in a finally should not be allowed.
301+
continue in a finally should be ok.
302302
303303
>>> def test():
304304
... for abc in range(10):
305305
... try:
306306
... pass
307307
... finally:
308308
... continue
309-
Traceback (most recent call last):
310-
...
311-
SyntaxError: 'continue' not supported inside 'finally' clause
312-
313-
This is essentially a continue in a finally which should not be allowed.
309+
... print(abc)
310+
>>> test()
311+
9
314312
315313
>>> def test():
316314
... for abc in range(10):
@@ -321,9 +319,24 @@
321319
... continue
322320
... except:
323321
... pass
324-
Traceback (most recent call last):
325-
...
326-
SyntaxError: 'continue' not supported inside 'finally' clause
322+
... print(abc)
323+
>>> test()
324+
9
325+
326+
>>> def test():
327+
... for abc in range(10):
328+
... try:
329+
... pass
330+
... finally:
331+
... try:
332+
... pass
333+
... except:
334+
... continue
335+
... print(abc)
336+
>>> test()
337+
9
338+
339+
A continue outside loop should not be allowed.
327340
328341
>>> def foo():
329342
... try:
@@ -332,42 +345,7 @@
332345
... continue
333346
Traceback (most recent call last):
334347
...
335-
SyntaxError: 'continue' not supported inside 'finally' clause
336-
337-
>>> def foo():
338-
... for a in ():
339-
... try:
340-
... pass
341-
... finally:
342-
... continue
343-
Traceback (most recent call last):
344-
...
345-
SyntaxError: 'continue' not supported inside 'finally' clause
346-
347-
>>> def foo():
348-
... for a in ():
349-
... try:
350-
... pass
351-
... finally:
352-
... try:
353-
... continue
354-
... finally:
355-
... pass
356-
Traceback (most recent call last):
357-
...
358-
SyntaxError: 'continue' not supported inside 'finally' clause
359-
360-
>>> def foo():
361-
... for a in ():
362-
... try: pass
363-
... finally:
364-
... try:
365-
... pass
366-
... except:
367-
... continue
368-
Traceback (most recent call last):
369-
...
370-
SyntaxError: 'continue' not supported inside 'finally' clause
348+
SyntaxError: 'continue' not properly in loop
371349
372350
There is one test for a break that is not in a loop. The compiler
373351
uses a single data structure to keep track of try-finally and loops,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
A :keyword:`continue` statement is now allowed in the :keyword:`finally`
2+
clause.

Python/compile.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,10 +2625,6 @@ compiler_continue(struct compiler *c)
26252625
ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
26262626
return 1;
26272627
}
2628-
if (info->fb_type == FINALLY_END) {
2629-
return compiler_error(c,
2630-
"'continue' not supported inside 'finally' clause");
2631-
}
26322628
if (!compiler_unwind_fblock(c, info, 0))
26332629
return 0;
26342630
}

0 commit comments

Comments
 (0)