[3.14] gh-139640: Fix swallowing syntax warnings in different modules (GH-139755) (GH-140117)

Revert GH-131993.

Fix swallowing some syntax warnings in different modules if they accidentally
have the same message and are emitted from the same line.

Fix duplicated warnings in the "finally" block.

(cherry picked from commit 279db6bede)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>

* Update 2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst

---------

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-10-14 17:56:01 +02:00 committed by GitHub
parent 99e42ab49c
commit af28efd8b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 63 additions and 74 deletions

View file

@ -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);

View file

@ -1662,22 +1662,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')
for i in range(2):
# Even if compile() is at the same line.
compile(source, '<stdin>', 'exec')
self.assertEqual(len(caught), 1)
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
@ -1688,16 +1687,47 @@ 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))
class TestBooleanExpression(unittest.TestCase):
class Value:

View file

@ -1,7 +1,6 @@
import contextlib
import io
import unittest
import warnings
from unittest.mock import patch
from textwrap import dedent
@ -274,28 +273,3 @@ 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("default")
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 @@
Fix swallowing some syntax warnings in different modules if they
accidentally have the same message and are emitted from the same line.
Fix duplicated warnings in the ``finally`` block.

View file

@ -1479,28 +1479,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,

View file

@ -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
@ -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;
}
@ -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 *
@ -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);

View file

@ -1938,8 +1938,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