Skip to content

Commit ad0a3f7

Browse files
gh-131927: Do not emit PEP 765 warnings in ast.parse() (GH-139642)
ast.parse() no longer emits syntax warnings for return/break/continue in finally (see PEP-765) -- they are only emitted during compilation.
1 parent 2a90426 commit ad0a3f7

File tree

7 files changed

+98
-61
lines changed

7 files changed

+98
-61
lines changed

Include/internal/pycore_compile.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ extern int _PyAST_Preprocess(
4949
PyObject *filename,
5050
int optimize,
5151
int ff_features,
52-
int syntax_check_only);
52+
int syntax_check_only,
53+
int enable_warnings);
5354

5455

5556
typedef struct {

Lib/test/test_ast/test_ast.py

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

1060-
def test_pep_765_warnings(self):
1061-
srcs = [
1062-
textwrap.dedent("""
1063-
def f():
1064-
try:
1065-
pass
1066-
finally:
1067-
return 42
1068-
"""),
1069-
textwrap.dedent("""
1070-
for x in y:
1071-
try:
1072-
pass
1073-
finally:
1074-
break
1075-
"""),
1076-
textwrap.dedent("""
1077-
for x in y:
1078-
try:
1079-
pass
1080-
finally:
1081-
continue
1082-
"""),
1083-
]
1084-
for src in srcs:
1085-
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
1086-
ast.parse(src)
1087-
1088-
def test_pep_765_no_warnings(self):
1089-
srcs = [
1090-
textwrap.dedent("""
1091-
try:
1092-
pass
1093-
finally:
1094-
def f():
1095-
return 42
1096-
"""),
1097-
textwrap.dedent("""
1098-
try:
1099-
pass
1100-
finally:
1101-
for x in y:
1102-
break
1103-
"""),
1104-
textwrap.dedent("""
1105-
try:
1106-
pass
1107-
finally:
1108-
for x in y:
1109-
continue
1110-
"""),
1111-
]
1112-
for src in srcs:
1113-
ast.parse(src)
1114-
11151060
def test_tstring(self):
11161061
# Test AST structure for simple t-string
11171062
tree = ast.parse('t"Hello"')

Lib/test/test_compile.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,6 +1745,66 @@ def test_compile_warning_in_finally(self):
17451745
self.assertEqual(wm.category, SyntaxWarning)
17461746
self.assertIn("\"is\" with 'int' literal", str(wm.message))
17471747

1748+
@support.subTests('src', [
1749+
textwrap.dedent("""
1750+
def f():
1751+
try:
1752+
pass
1753+
finally:
1754+
return 42
1755+
"""),
1756+
textwrap.dedent("""
1757+
for x in y:
1758+
try:
1759+
pass
1760+
finally:
1761+
break
1762+
"""),
1763+
textwrap.dedent("""
1764+
for x in y:
1765+
try:
1766+
pass
1767+
finally:
1768+
continue
1769+
"""),
1770+
])
1771+
def test_pep_765_warnings(self, src):
1772+
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
1773+
compile(src, '<string>', 'exec')
1774+
with warnings.catch_warnings():
1775+
warnings.simplefilter("error")
1776+
tree = ast.parse(src)
1777+
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
1778+
compile(tree, '<string>', 'exec')
1779+
1780+
@support.subTests('src', [
1781+
textwrap.dedent("""
1782+
try:
1783+
pass
1784+
finally:
1785+
def f():
1786+
return 42
1787+
"""),
1788+
textwrap.dedent("""
1789+
try:
1790+
pass
1791+
finally:
1792+
for x in y:
1793+
break
1794+
"""),
1795+
textwrap.dedent("""
1796+
try:
1797+
pass
1798+
finally:
1799+
for x in y:
1800+
continue
1801+
"""),
1802+
])
1803+
def test_pep_765_no_warnings(self, src):
1804+
with warnings.catch_warnings():
1805+
warnings.simplefilter("error")
1806+
compile(src, '<string>', 'exec')
1807+
17481808

17491809
class TestBooleanExpression(unittest.TestCase):
17501810
class Value:

Lib/test/test_pyrepl/test_interact.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
import io
3+
import warnings
34
import unittest
45
from unittest.mock import patch
56
from textwrap import dedent
@@ -273,3 +274,28 @@ def test_incomplete_statement(self):
273274
code = "if foo:"
274275
console = InteractiveColoredConsole(namespace, filename="<stdin>")
275276
self.assertTrue(_more_lines(console, code))
277+
278+
279+
class TestWarnings(unittest.TestCase):
280+
def test_pep_765_warning(self):
281+
"""
282+
Test that a SyntaxWarning emitted from the
283+
AST optimizer is only shown once in the REPL.
284+
"""
285+
# gh-131927
286+
console = InteractiveColoredConsole()
287+
code = dedent("""\
288+
def f():
289+
try:
290+
return 1
291+
finally:
292+
return 2
293+
""")
294+
295+
with warnings.catch_warnings(record=True) as caught:
296+
warnings.simplefilter("always")
297+
console.runsource(code)
298+
299+
count = sum("'return' in a 'finally' block" in str(w.message)
300+
for w in caught)
301+
self.assertEqual(count, 1)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`ast.parse` no longer emits syntax warnings for
2+
``return``/``break``/``continue`` in ``finally`` (see :pep:`765`) -- they are
3+
only emitted during compilation.

Python/ast_preprocess.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef struct {
1919
int optimize;
2020
int ff_features;
2121
int syntax_check_only;
22+
int enable_warnings;
2223

2324
_Py_c_array_t cf_finally; /* context for PEP 765 check */
2425
int cf_finally_used;
@@ -78,7 +79,7 @@ control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTPreprocessState
7879
static int
7980
before_return(_PyASTPreprocessState *state, stmt_ty node_)
8081
{
81-
if (state->cf_finally_used > 0) {
82+
if (state->enable_warnings && state->cf_finally_used > 0) {
8283
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
8384
if (ctx->in_finally && ! ctx->in_funcdef) {
8485
if (!control_flow_in_finally_warning("return", node_, state)) {
@@ -92,7 +93,7 @@ before_return(_PyASTPreprocessState *state, stmt_ty node_)
9293
static int
9394
before_loop_exit(_PyASTPreprocessState *state, stmt_ty node_, const char *kw)
9495
{
95-
if (state->cf_finally_used > 0) {
96+
if (state->enable_warnings && state->cf_finally_used > 0) {
9697
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
9798
if (ctx->in_finally && ! ctx->in_loop) {
9899
if (!control_flow_in_finally_warning(kw, node_, state)) {
@@ -968,14 +969,15 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_, _PyASTPreprocessState *st
968969

969970
int
970971
_PyAST_Preprocess(mod_ty mod, PyArena *arena, PyObject *filename, int optimize,
971-
int ff_features, int syntax_check_only)
972+
int ff_features, int syntax_check_only, int enable_warnings)
972973
{
973974
_PyASTPreprocessState state;
974975
memset(&state, 0, sizeof(_PyASTPreprocessState));
975976
state.filename = filename;
976977
state.optimize = optimize;
977978
state.ff_features = ff_features;
978979
state.syntax_check_only = syntax_check_only;
980+
state.enable_warnings = enable_warnings;
979981
if (_Py_CArray_Init(&state.cf_finally, sizeof(ControlFlowInFinallyContext), 20) < 0) {
980982
return -1;
981983
}

Python/compile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
136136
c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize;
137137
c->c_save_nested_seqs = false;
138138

139-
if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0)) {
139+
if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0, 1)) {
140140
return ERROR;
141141
}
142142
c->c_st = _PySymtable_Build(mod, filename, &c->c_future);
@@ -1502,7 +1502,7 @@ _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf,
15021502
if (optimize == -1) {
15031503
optimize = _Py_GetConfig()->optimization_level;
15041504
}
1505-
if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, no_const_folding)) {
1505+
if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, no_const_folding, 0)) {
15061506
return -1;
15071507
}
15081508
return 0;

0 commit comments

Comments
 (0)