diff options
-rw-r--r-- | Doc/library/dis.rst | 4 | ||||
-rw-r--r-- | Doc/reference/compound_stmts.rst | 13 | ||||
-rw-r--r-- | Doc/reference/simple_stmts.rst | 5 | ||||
-rw-r--r-- | Doc/whatsnew/3.8.rst | 5 | ||||
-rw-r--r-- | Lib/test/test_compile.py | 4 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 9 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 53 | ||||
-rw-r--r-- | Lib/test/test_syntax.py | 68 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2018-01-03-23-12-43.bpo-32489.SDEPHB.rst | 2 | ||||
-rw-r--r-- | Python/compile.c | 4 |
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; } |