Skip to content

Commit af28efd

Browse files
[3.14] pythongh-139640: Fix swallowing syntax warnings in different modules (pythonGH-139755) (pythonGH-140117)
Revert pythonGH-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 279db6b) Co-authored-by: Serhiy Storchaka <[email protected]> * Update 2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst --------- Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 99e42ab commit af28efd

File tree

7 files changed

+63
-74
lines changed

7 files changed

+63
-74
lines changed

Include/cpython/warnings.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,3 @@ PyAPI_FUNC(int) PyErr_WarnExplicitFormat(
1818

1919
// DEPRECATED: Use PyErr_WarnEx() instead.
2020
#define PyErr_Warn(category, msg) PyErr_WarnEx((category), (msg), 1)
21-
22-
int _PyErr_WarnExplicitObjectWithContext(
23-
PyObject *category,
24-
PyObject *message,
25-
PyObject *filename,
26-
int lineno);

Lib/test/test_compile.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,22 +1662,21 @@ class WeirdDict(dict):
16621662
self.assertRaises(NameError, ns['foo'])
16631663

16641664
def test_compile_warnings(self):
1665-
# See gh-131927
1666-
# Compile warnings originating from the same file and
1667-
# line are now only emitted once.
1665+
# Each invocation of compile() emits compiler warnings, even if they
1666+
# have the same message and line number.
1667+
source = textwrap.dedent(r"""
1668+
# tokenizer
1669+
1or 0 # line 3
1670+
# code generator
1671+
1 is 1 # line 5
1672+
""")
16681673
with warnings.catch_warnings(record=True) as caught:
16691674
warnings.simplefilter("default")
1670-
compile('1 is 1', '<stdin>', 'eval')
1671-
compile('1 is 1', '<stdin>', 'eval')
1672-
1673-
self.assertEqual(len(caught), 1)
1675+
for i in range(2):
1676+
# Even if compile() is at the same line.
1677+
compile(source, '<stdin>', 'exec')
16741678

1675-
with warnings.catch_warnings(record=True) as caught:
1676-
warnings.simplefilter("always")
1677-
compile('1 is 1', '<stdin>', 'eval')
1678-
compile('1 is 1', '<stdin>', 'eval')
1679-
1680-
self.assertEqual(len(caught), 2)
1679+
self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2)
16811680

16821681
def test_compile_warning_in_finally(self):
16831682
# Ensure that warnings inside finally blocks are
@@ -1688,16 +1687,47 @@ def test_compile_warning_in_finally(self):
16881687
try:
16891688
pass
16901689
finally:
1691-
1 is 1
1690+
1 is 1 # line 5
1691+
try:
1692+
pass
1693+
finally: # nested
1694+
1 is 1 # line 9
16921695
""")
16931696

16941697
with warnings.catch_warnings(record=True) as caught:
1695-
warnings.simplefilter("default")
1698+
warnings.simplefilter("always")
16961699
compile(source, '<stdin>', 'exec')
16971700

1698-
self.assertEqual(len(caught), 1)
1699-
self.assertEqual(caught[0].category, SyntaxWarning)
1700-
self.assertIn("\"is\" with 'int' literal", str(caught[0].message))
1701+
self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9])
1702+
for wm in caught:
1703+
self.assertEqual(wm.category, SyntaxWarning)
1704+
self.assertIn("\"is\" with 'int' literal", str(wm.message))
1705+
1706+
# Other code path is used for "try" with "except*".
1707+
source = textwrap.dedent("""
1708+
try:
1709+
pass
1710+
except *Exception:
1711+
pass
1712+
finally:
1713+
1 is 1 # line 7
1714+
try:
1715+
pass
1716+
except *Exception:
1717+
pass
1718+
finally: # nested
1719+
1 is 1 # line 13
1720+
""")
1721+
1722+
with warnings.catch_warnings(record=True) as caught:
1723+
warnings.simplefilter("always")
1724+
compile(source, '<stdin>', 'exec')
1725+
1726+
self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13])
1727+
for wm in caught:
1728+
self.assertEqual(wm.category, SyntaxWarning)
1729+
self.assertIn("\"is\" with 'int' literal", str(wm.message))
1730+
17011731

17021732
class TestBooleanExpression(unittest.TestCase):
17031733
class Value:

Lib/test/test_pyrepl/test_interact.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import contextlib
22
import io
33
import unittest
4-
import warnings
54
from unittest.mock import patch
65
from textwrap import dedent
76

@@ -274,28 +273,3 @@ def test_incomplete_statement(self):
274273
code = "if foo:"
275274
console = InteractiveColoredConsole(namespace, filename="<stdin>")
276275
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("default")
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+
Fix swallowing some syntax warnings in different modules if they
2+
accidentally have the same message and are emitted from the same line.
3+
Fix duplicated warnings in the ``finally`` block.

Python/_warnings.c

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,28 +1479,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
14791479
return 0;
14801480
}
14811481

1482-
/* Like PyErr_WarnExplicitObject, but automatically sets up context */
1483-
int
1484-
_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message,
1485-
PyObject *filename, int lineno)
1486-
{
1487-
PyObject *unused_filename, *module, *registry;
1488-
int unused_lineno;
1489-
int stack_level = 1;
1490-
1491-
if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno,
1492-
&module, &registry)) {
1493-
return -1;
1494-
}
1495-
1496-
int rc = PyErr_WarnExplicitObject(category, message, filename, lineno,
1497-
module, registry);
1498-
Py_DECREF(unused_filename);
1499-
Py_DECREF(registry);
1500-
Py_DECREF(module);
1501-
return rc;
1502-
}
1503-
15041482
int
15051483
PyErr_WarnExplicit(PyObject *category, const char *text,
15061484
const char *filename_str, int lineno,

Python/compile.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ typedef struct _PyCompiler {
103103
bool c_save_nested_seqs; /* if true, construct recursive instruction sequences
104104
* (including instructions for nested code objects)
105105
*/
106+
int c_disable_warning;
106107
} compiler;
107108

108109
static int
@@ -765,6 +766,9 @@ _PyCompile_PushFBlock(compiler *c, location loc,
765766
f->fb_loc = loc;
766767
f->fb_exit = exit;
767768
f->fb_datum = datum;
769+
if (t == COMPILE_FBLOCK_FINALLY_END) {
770+
c->c_disable_warning++;
771+
}
768772
return SUCCESS;
769773
}
770774

@@ -776,6 +780,9 @@ _PyCompile_PopFBlock(compiler *c, fblocktype t, jump_target_label block_label)
776780
u->u_nfblocks--;
777781
assert(u->u_fblock[u->u_nfblocks].fb_type == t);
778782
assert(SAME_JUMP_TARGET_LABEL(u->u_fblock[u->u_nfblocks].fb_block, block_label));
783+
if (t == COMPILE_FBLOCK_FINALLY_END) {
784+
c->c_disable_warning--;
785+
}
779786
}
780787

781788
fblockinfo *
@@ -1203,6 +1210,9 @@ _PyCompile_Error(compiler *c, location loc, const char *format, ...)
12031210
int
12041211
_PyCompile_Warn(compiler *c, location loc, const char *format, ...)
12051212
{
1213+
if (c->c_disable_warning) {
1214+
return 0;
1215+
}
12061216
va_list vargs;
12071217
va_start(vargs, format);
12081218
PyObject *msg = PyUnicode_FromFormatV(format, vargs);

Python/errors.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,8 +1938,8 @@ int
19381938
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
19391939
int end_lineno, int end_col_offset)
19401940
{
1941-
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
1942-
filename, lineno) < 0)
1941+
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
1942+
filename, lineno, NULL, NULL) < 0)
19431943
{
19441944
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
19451945
/* Replace the SyntaxWarning exception with a SyntaxError

0 commit comments

Comments
 (0)