summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/dis.rst4
-rw-r--r--Doc/reference/compound_stmts.rst13
-rw-r--r--Doc/reference/simple_stmts.rst5
-rw-r--r--Doc/whatsnew/3.8.rst5
-rw-r--r--Lib/test/test_compile.py4
-rw-r--r--Lib/test/test_exceptions.py9
-rw-r--r--Lib/test/test_grammar.py53
-rw-r--r--Lib/test/test_syntax.py68
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst2
-rw-r--r--Python/compile.c4
10 files changed, 97 insertions, 70 deletions
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index aa66128..47f226b 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 d7792f1..153e85b 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 ef9a5f0..9186210 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 8569341..fcc868b 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 acebdbd..6b45a24 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -856,7 +856,7 @@ class TestStackSizeStability(unittest.TestCase):
"""
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 @@ class TestStackSizeStability(unittest.TestCase):
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 9d10df5..2a9ec70 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -138,15 +138,6 @@ class ExceptionTests(unittest.TestCase):
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 d89bfdc..ee41362 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -859,6 +859,59 @@ class GrammarTests(unittest.TestCase):
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 2b96a94..fa1e7aa 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -298,7 +298,7 @@ continue in for loop under finally should be ok.
>>> 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 @@ Start simple, a continue in a finally should not be allowed.
... 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 @@ This is essentially a continue in a finally which should not be allowed.
... 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 @@ This is essentially a continue in a finally which should not be allowed.
... 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 0000000..68babeb
--- /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 fbd1fc9..c3ffaae 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;
}