8000 gh-130080: implement PEP 765 (#130087) · python/cpython@ffc2f1d · GitHub
[go: up one dir, main page]

Skip to content

Commit ffc2f1d

Browse files
authored
gh-130080: implement PEP 765 (#130087)
1 parent 468a7aa commit ffc2f1d

File tree

14 files changed

+426
-109
lines changed

14 files changed

+426
-109
lines changed

Doc/reference/compound_stmts.rst

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -420,16 +420,16 @@ is executed. If there is a saved exception it is re-raised at the end of the
420420
:keyword:`!finally` clause. If the :keyword:`!finally` clause raises another
421421
exception, the saved exception is set as the context of the new exception.
422422
If the :keyword:`!finally` clause executes a :keyword:`return`, :keyword:`break`
423-
or :keyword:`continue` statement, the saved exception is discarded::
423+
or :keyword:`continue` statement, the saved exception is discarded. For example,
424+
this function returns 42.
424425

425-
>>> def f():
426-
... try:
427-
... 1/0
428-
... finally:
429-
... return 42
430-
...
431-
>>> f()
432-
42
426+
.. code-block::
427+
428+
def f():
429+
try:
430+
1/0
431+
finally:
432+
return 42
433433
434434
The exception information is not available to the program during execution of
435435
the :keyword:`!finally` clause.
@@ -446,21 +446,25 @@ statement, the :keyword:`!finally` clause is also executed 'on the way out.'
446446
The return value of a function is determined by the last :keyword:`return`
447447
statement executed. Since the :keyword:`!finally` clause always executes, a
448448
:keyword:`!return` statement executed in the :keyword:`!finally` clause will
449-
always be the last one executed::
449+
always be the last one executed. The following function returns 'finally'.
450450

451-
>>> def foo():
452-
... try:
453-
... return 'try'
454-
... finally:
455-
... return 'finally'
456-
...
457-
>>> foo()
458-
'finally'
451+
.. code-block::
452+
453+
def foo():
454+
try:
455+
return 'try'
456+
finally:
457+
return 'finally'
459458
460459
.. versionchanged:: 3.8
461460
Prior to Python 3.8, a :keyword:`continue` statement was illegal in the
462461
:keyword:`!finally` clause due to a problem with the implementation.
463462

463+
.. versionchanged:: next
464+
The compiler emits a :exc:`SyntaxWarning` when a :keyword:`return`,
465+
:keyword:`break` or :keyword:`continue` appears in a :keyword:`!finally`
466+
block (see :pep:`765`).
467+
464468

465469
.. _with:
466470
.. _as:

Doc/tutorial/errors.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,9 @@ points discuss more complex cases when an exception occurs:
418418

419419
* If the :keyword:`!finally` clause executes a :keyword:`break`,
420420
:keyword:`continue` or :keyword:`return` statement, exceptions are not
421-
re-raised.
421+
re-raised. This can be confusing and is therefore discouraged. From
422+
version 3.14 the compiler emits a :exc:`SyntaxWarning` for it
423+
(see :pep:`765`).
422424

423425
* If the :keyword:`!try` statement reaches a :keyword:`break`,
424426
:keyword:`continue` or :keyword:`return` statement, the
@@ -430,7 +432,9 @@ points discuss more complex cases when an exception occurs:
430432
statement, the returned value will be the one from the
431433
:keyword:`!finally` clause's :keyword:`!return` statement, not the
432434
value from the :keyword:`!try` clause's :keyword:`!return`
433-
statement.
435+
statement. This can be confusing and is therefore discouraged. From
436+
version 3.14 the compiler emits a :exc:`SyntaxWarning` for it
437+
(see :pep:`765`).
434438

435439
For example::
436440

Doc/whatsnew/3.14.rst

Li F438 nes changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Summary -- release highlights
6868
* :ref:`PEP 741: Python Configuration C API <whatsnew314-pep741>`
6969
* :ref:`PEP 761: Discontinuation of PGP signatures <whatsnew314-pep761>`
7070
* :ref:`A new type of interpreter <whatsnew314-tail-call>`
71+
* :ref:`PEP 765: Disallow return/break/continue that exit a finally block <whatsnew314-pep765>`
7172

7273

7374
Incompatible changes
@@ -370,6 +371,15 @@ Other language changes
370371
The testbed can also be used to run the test suite of projects other than
371372
CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.)
372373

374+
.. _whatsnew314-pep765:
375+
376+
PEP 765: Disallow return/break/continue that exit a finally block
377+
-----------------------------------------------------------------
378+
379+
The compiler emits a :exc:`SyntaxWarning` when a :keyword:`return`, :keyword:`break` or
380+
:keyword:`continue` statements appears where it exits a :keyword:`finally` block.
381+
This change is specified in :pep:`765`.
382+
373383
New modules
374384
===========
375385

Include/internal/pycore_compile.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,16 @@ extern int _PyCompile_AstOptimize(
4040
PyObject *filename,
4141
PyCompilerFlags *flags,
4242
int optimize,
43-
struct _arena *arena);
43+
struct _arena *< 10000 /span>arena,
44+
int syntax_check_only);
4445

4546
extern int _PyAST_Optimize(
4647
struct _mod *,
4748
struct _arena *arena,
49+
PyObject *filename,
4850
int optimize,
49-
int ff_features);
51+
int ff_features,
52+
int syntax_check_only);
5053

5154

5255
typedef struct {

Lib/test/test___all__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def check_all(self, modname):
3737
(".* (module|package)", DeprecationWarning),
3838
(".* (module|package)", PendingDeprecationWarning),
3939
("", ResourceWarning),
40+
("", SyntaxWarning),
4041
quiet=True):
4142
try:
4243
exec("import %s" % modname, names)
@@ -52,6 +53,7 @@ def check_all(self, modname):
5253
with warnings_helper.check_warnings(
5354
("", DeprecationWarning),
5455
("", ResourceWarning),
56+
("", SyntaxWarning),
5557
quiet=True):
5658
try:
5759
exec("from %s import *" % modname, names)

Lib/test/test_ast/test_ast.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,61 @@ def test_repr_large_input_crash(self):
820820
r"Exceeds the limit \(\d+ digits\)"):
821821
repr(ast.Constant(value=eval(source)))
822822

823+
def test_pep_765_warnings(self):
824+
srcs = [
825+
textwrap.dedent("""
826+
def f():
827+
try:
828+
pass
829+
finally:
830+
return 42
831+
"""),
832+
textwrap.dedent("""
833+
for x in y:
834+
try:
835+
pass
836+
finally:
837+
break
838+
"""),
839+
textwrap.dedent("""
840+
for x in y:
841+
try:
842+
pass
843+
finally:
844+
continue
845+
"""),
846+
]
847+
for src in srcs:
848+
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
849+
ast.parse(src)
850+
851+
def test_pep_765_no_warnings(self):
852+
srcs = [
853+
textwrap.dedent("""
854+
try:
855+
pass
856+
finally:
857+
def f():
858+
return 42
859+
"""),
860+
textwrap.dedent("""
861+
try:
862+
pass
863+
finally:
864+
for x in y:
865+
break
866+
"""),
867+
textwrap.dedent("""
868+
try:
869+
pass
870+
finally:
871+
for x in y:
872+
continue
873+
"""),
874+
]
875+
for src in srcs:
876+
ast.parse(src)
877+
823878

824879
class CopyTests(unittest.TestCase):
825880
"""Test copying and pickling AST nodes."""

Lib/test/test_except_star.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ def test_break_in_except_star(self):
8484
if i == 2:
8585
break
8686
finally:
87-
return 0
87+
pass
88+
return 0
8889
""")
8990

9091

@@ -117,7 +118,8 @@ def test_continue_in_except_star_block_invalid(self):
117118
if i == 2:
118119
continue
119120
finally:
120-
return 0
121+
pass
122+
return 0
121123
""")
122124

123125
def test_return_in_except_star_block_invalid(self):

0 commit comments

Comments
 (0)
0