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.
This commit is contained in:
Serhiy Storchaka 2025-10-30 13:00:42 +02:00 committed by GitHub
parent 2a904263aa
commit ad0a3f733b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 98 additions and 61 deletions

View file

@ -49,7 +49,8 @@ extern int _PyAST_Preprocess(
PyObject *filename,
int optimize,
int ff_features,
int syntax_check_only);
int syntax_check_only,
int enable_warnings);
typedef struct {

View file

@ -1057,61 +1057,6 @@ def test_repr_large_input_crash(self):
r"Exceeds the limit \(\d+ digits\)"):
repr(ast.Constant(value=eval(source)))
def test_pep_765_warnings(self):
srcs = [
textwrap.dedent("""
def f():
try:
pass
finally:
return 42
"""),
textwrap.dedent("""
for x in y:
try:
pass
finally:
break
"""),
textwrap.dedent("""
for x in y:
try:
pass
finally:
continue
"""),
]
for src in srcs:
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
ast.parse(src)
def test_pep_765_no_warnings(self):
srcs = [
textwrap.dedent("""
try:
pass
finally:
def f():
return 42
"""),
textwrap.dedent("""
try:
pass
finally:
for x in y:
break
"""),
textwrap.dedent("""
try:
pass
finally:
for x in y:
continue
"""),
]
for src in srcs:
ast.parse(src)
def test_tstring(self):
# Test AST structure for simple t-string
tree = ast.parse('t"Hello"')

View file

@ -1745,6 +1745,66 @@ def test_compile_warning_in_finally(self):
self.assertEqual(wm.category, SyntaxWarning)
self.assertIn("\"is\" with 'int' literal", str(wm.message))
@support.subTests('src', [
textwrap.dedent("""
def f():
try:
pass
finally:
return 42
"""),
textwrap.dedent("""
for x in y:
try:
pass
finally:
break
"""),
textwrap.dedent("""
for x in y:
try:
pass
finally:
continue
"""),
])
def test_pep_765_warnings(self, src):
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
compile(src, '<string>', 'exec')
with warnings.catch_warnings():
warnings.simplefilter("error")
tree = ast.parse(src)
with self.assertWarnsRegex(SyntaxWarning, 'finally'):
compile(tree, '<string>', 'exec')
@support.subTests('src', [
textwrap.dedent("""
try:
pass
finally:
def f():
return 42
"""),
textwrap.dedent("""
try:
pass
finally:
for x in y:
break
"""),
textwrap.dedent("""
try:
pass
finally:
for x in y:
continue
"""),
])
def test_pep_765_no_warnings(self, src):
with warnings.catch_warnings():
warnings.simplefilter("error")
compile(src, '<string>', 'exec')
class TestBooleanExpression(unittest.TestCase):
class Value:

View file

@ -1,5 +1,6 @@
import contextlib
import io
import warnings
import unittest
from unittest.mock import patch
from textwrap import dedent
@ -273,3 +274,28 @@ def test_incomplete_statement(self):
code = "if foo:"
console = InteractiveColoredConsole(namespace, filename="<stdin>")
self.assertTrue(_more_lines(console, code))
class TestWarnings(unittest.TestCase):
def test_pep_765_warning(self):
"""
Test that a SyntaxWarning emitted from the
AST optimizer is only shown once in the REPL.
"""
# gh-131927
console = InteractiveColoredConsole()
code = dedent("""\
def f():
try:
return 1
finally:
return 2
""")
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
console.runsource(code)
count = sum("'return' in a 'finally' block" in str(w.message)
for w in caught)
self.assertEqual(count, 1)

View file

@ -0,0 +1,3 @@
:func:`ast.parse` no longer emits syntax warnings for
``return``/``break``/``continue`` in ``finally`` (see :pep:`765`) -- they are
only emitted during compilation.

View file

@ -19,6 +19,7 @@ typedef struct {
int optimize;
int ff_features;
int syntax_check_only;
int enable_warnings;
_Py_c_array_t cf_finally; /* context for PEP 765 check */
int cf_finally_used;
@ -78,7 +79,7 @@ control_flow_in_finally_warning(const char *kw, stmt_ty n, _PyASTPreprocessState
static int
before_return(_PyASTPreprocessState *state, stmt_ty node_)
{
if (state->cf_finally_used > 0) {
if (state->enable_warnings && state->cf_finally_used > 0) {
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
if (ctx->in_finally && ! ctx->in_funcdef) {
if (!control_flow_in_finally_warning("return", node_, state)) {
@ -92,7 +93,7 @@ before_return(_PyASTPreprocessState *state, stmt_ty node_)
static int
before_loop_exit(_PyASTPreprocessState *state, stmt_ty node_, const char *kw)
{
if (state->cf_finally_used > 0) {
if (state->enable_warnings && state->cf_finally_used > 0) {
ControlFlowInFinallyContext *ctx = get_cf_finally_top(state);
if (ctx->in_finally && ! ctx->in_loop) {
if (!control_flow_in_finally_warning(kw, node_, state)) {
@ -968,7 +969,7 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_, _PyASTPreprocessState *st
int
_PyAST_Preprocess(mod_ty mod, PyArena *arena, PyObject *filename, int optimize,
int ff_features, int syntax_check_only)
int ff_features, int syntax_check_only, int enable_warnings)
{
_PyASTPreprocessState state;
memset(&state, 0, sizeof(_PyASTPreprocessState));
@ -976,6 +977,7 @@ _PyAST_Preprocess(mod_ty mod, PyArena *arena, PyObject *filename, int optimize,
state.optimize = optimize;
state.ff_features = ff_features;
state.syntax_check_only = syntax_check_only;
state.enable_warnings = enable_warnings;
if (_Py_CArray_Init(&state.cf_finally, sizeof(ControlFlowInFinallyContext), 20) < 0) {
return -1;
}

View file

@ -136,7 +136,7 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level : optimize;
c->c_save_nested_seqs = false;
if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0)) {
if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0, 1)) {
return ERROR;
}
c->c_st = _PySymtable_Build(mod, filename, &c->c_future);
@ -1502,7 +1502,7 @@ _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf,
if (optimize == -1) {
optimize = _Py_GetConfig()->optimization_level;
}
if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, no_const_folding)) {
if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags, no_const_folding, 0)) {
return -1;
}
return 0;