mirror of
https://github.com/python/cpython.git
synced 2025-11-02 14:41:33 +00:00
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:
parent
2a904263aa
commit
ad0a3f733b
7 changed files with 98 additions and 61 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue