diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 39a3e130afd3e0..76b24f5bb89a69 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -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 diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 988eec6d254e10..bfea8949848d16 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -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: @@ -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 @@ -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: diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 0a043a90050c4e..66fd7fa57559d6 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -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 diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index e8238251d6ea25..8e62b1a9562358 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -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 diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 9d77f7af05d6fd..20435c2a5e62b4 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -917,7 +917,7 @@ 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: @@ -925,8 +925,6 @@ def test_for_break_continue_inside_finally_block(self): 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 10c1e076464e2c..52912907761c53 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -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 diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 78d94593c7f322..33d62dae7ec2fa 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -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: diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 3829746f1799a2..d413f049a2b65e 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -354,7 +354,7 @@ >>> 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): @@ -362,9 +362,11 @@ ... 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): @@ -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: @@ -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, diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-08-12-21-17-22.bpo-37830.Qokspy.rst b/Misc/NEWS.d/next/Core and Builtins/2019-08-12-21-17-22.bpo-37830.Qokspy.rst new file mode 100644 index 00000000000000..c2dd6277a61d2b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-08-12-21-17-22.bpo-37830.Qokspy.rst @@ -0,0 +1 @@ +Reverted :issue:`32489`: "Allow 'continue' in 'finally' clause." diff --git a/Python/compile.c b/Python/compile.c index d2de7a72a811ff..3db77295143fff 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -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; }