Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions Include/cpython/warnings.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,3 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat(

// DEPRECATED: Use PyErr_WarnEx() instead.
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)

int _PyErr_WarnExplicitObjectWithContext(
PyObject *category,
PyObject *message,
PyObject *filename,
int lineno);
3 changes: 2 additions & 1 deletion Include/internal/pycore_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
55 changes: 0 additions & 55 deletions Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"')
Expand Down
126 changes: 108 additions & 18 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1664,22 +1664,21 @@ class WeirdDict(dict):
self.assertRaises(NameError, ns['foo'])

def test_compile_warnings(self):
# See gh-131927
# Compile warnings originating from the same file and
# line are now only emitted once.
# Each invocation of compile() emits compiler warnings, even if they
# have the same message and line number.
source = textwrap.dedent(r"""
# tokenizer
1or 0 # line 3
# code generator
1 is 1 # line 5
""")
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("default")
compile('1 is 1', '<stdin>', 'eval')
compile('1 is 1', '<stdin>', 'eval')

self.assertEqual(len(caught), 1)
for i in range(2):
# Even if compile() is at the same line.
compile(source, '<stdin>', 'exec')

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
compile('1 is 1', '<stdin>', 'eval')
compile('1 is 1', '<stdin>', 'eval')

self.assertEqual(len(caught), 2)
self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2)

def test_compile_warning_in_finally(self):
# Ensure that warnings inside finally blocks are
Expand All @@ -1690,16 +1689,107 @@ def test_compile_warning_in_finally(self):
try:
pass
finally:
1 is 1
1 is 1 # line 5
try:
pass
finally: # nested
1 is 1 # line 9
""")

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("default")
warnings.simplefilter("always")
compile(source, '<stdin>', 'exec')

self.assertEqual(len(caught), 1)
self.assertEqual(caught[0].category, SyntaxWarning)
self.assertIn("\"is\" with 'int' literal", str(caught[0].message))
self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9])
for wm in caught:
self.assertEqual(wm.category, SyntaxWarning)
self.assertIn("\"is\" with 'int' literal", str(wm.message))

# Other code path is used for "try" with "except*".
source = textwrap.dedent("""
try:
pass
except *Exception:
pass
finally:
1 is 1 # line 7
try:
pass
except *Exception:
pass
finally: # nested
1 is 1 # line 13
""")

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
compile(source, '<stdin>', 'exec')

self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13])
for wm in caught:
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:
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_pyrepl/test_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def f():
""")

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("default")
warnings.simplefilter("always")
console.runsource(code)

count = sum("'return' in a 'finally' block" in str(w.message)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix swallowing some syntax warnings in different modules if they
accidentally have the same message and are emitted from the same line.
:func:`ast.parse` no longer emits syntax warnings for
``return``/``break``/``continue`` in ``finally`` (see :pep:`765`) -- they are
only emitted during compilation.
22 changes: 0 additions & 22 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -1473,28 +1473,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
return 0;
}

/* Like PyErr_WarnExplicitObject, but automatically sets up context */
int
_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message,
PyObject *filename, int lineno)
{
PyObject *unused_filename, *module, *registry;
int unused_lineno;
int stack_level = 1;

if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno,
&module, &registry)) {
return -1;
}

int rc = PyErr_WarnExplicitObject(category, message, filename, lineno,
module, registry);
Py_DECREF(unused_filename);
Py_DECREF(registry);
Py_DECREF(module);
return rc;
}

int
PyErr_WarnExplicit(PyObject *category, const char *text,
const char *filename_str, int lineno,
Expand Down
8 changes: 5 additions & 3 deletions Python/ast_preprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand All @@ -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)) {
Expand Down Expand Up @@ -968,14 +969,15 @@ 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));
state.filename = filename;
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;
}
Expand Down
14 changes: 12 additions & 2 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ typedef struct _PyCompiler {
bool c_save_nested_seqs; /* if true, construct recursive instruction sequences
* (including instructions for nested code objects)
*/
int c_disable_warning;
} compiler;

static int
Expand Down Expand Up @@ -135,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);
Expand Down Expand Up @@ -765,6 +766,9 @@ _PyCompile_PushFBlock(compiler *c, location loc,
f->fb_loc = loc;
f->fb_exit = exit;
f->fb_datum = datum;
if (t == COMPILE_FBLOCK_FINALLY_END) {
c->c_disable_warning++;
}
return SUCCESS;
}

Expand All @@ -776,6 +780,9 @@ _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label)
u->u_nfblocks--;
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label));
if (t == COMPILE_FBLOCK_FINALLY_END) {
c->c_disable_warning--;
}
}

fblockinfo *
Expand Down Expand Up @@ -1203,6 +1210,9 @@ _PyCompile_Error(compiler *c, location loc, const char *format, ...)
int
_PyCompile_Warn(compiler *c, location loc, const char *format, ...)
{
if (c->c_disable_warning) {
return 0;
}
va_list vargs;
va_start(vargs, format);
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
Expand Down Expand Up @@ -1492,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;
Expand Down
4 changes: 2 additions & 2 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -1962,8 +1962,8 @@ int
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
int end_lineno, int end_col_offset)
{
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
filename, lineno) < 0)
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
filename, lineno, NULL, NULL) < 0)
{
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
/* Replace the SyntaxWarning exception with a SyntaxError
Expand Down
Loading