Skip to content

Commit 279db6b

Browse files
gh-139640: Fix swallowing syntax warnings in different modules (GH-139755)
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.
1 parent 1ff6d69 commit 279db6b

File tree

7 files changed

+62
-74
lines changed

7 files changed

+62
-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
@@ -1679,22 +1679,21 @@ class WeirdDict(dict):
16791679
self.assertRaises(NameError, ns['foo'])
16801680

16811681
def test_compile_warnings(self):
1682-
# See gh-131927
1683-
# Compile warnings originating from the same file and
1684-
# line are now only emitted once.
1682+
# Each invocation of compile() emits compiler warnings, even if they
1683+
# have the same message and line number.
1684+
source = textwrap.dedent(r"""
1685+
# tokenizer
1686+
1or 0 # line 3
1687+
# code generator
1688+
1 is 1 # line 5
1689+
""")
16851690
with warnings.catch_warnings(record=True) as caught:
16861691
warnings.simplefilter("default")
1687-
compile('1 is 1', '<stdin>', 'eval')
1688-
compile('1 is 1', '<stdin>', 'eval')
1689-
1690-
self.assertEqual(len(caught), 1)
1692+
for i in range(2):
1693+
# Even if compile() is at the same line.
1694+
compile(source, '<stdin>', 'exec')
16911695

1692-
with warnings.catch_warnings(record=True) as caught:
1693-
warnings.simplefilter("always")
1694-
compile('1 is 1', '<stdin>', 'eval')
1695-
compile('1 is 1', '<stdin>', 'eval')
1696-
1697-
self.assertEqual(len(caught), 2)
1696+
self.assertEqual([wm.lineno for wm in caught], [3, 5] * 2)
16981697

16991698
def test_compile_warning_in_finally(self):
17001699
# Ensure that warnings inside finally blocks are
@@ -1705,16 +1704,47 @@ def test_compile_warning_in_finally(self):
17051704
try:
17061705
pass
17071706
finally:
1708-
1 is 1
1707+
1 is 1 # line 5
1708+
try:
1709+
pass
1710+
finally: # nested
1711+
1 is 1 # line 9
17091712
""")
17101713

17111714
with warnings.catch_warnings(record=True) as caught:
1712-
warnings.simplefilter("default")
1715+
warnings.simplefilter("always")
17131716
compile(source, '<stdin>', 'exec')
17141717

1715-
self.assertEqual(len(caught), 1)
1716-
self.assertEqual(caught[0].category, SyntaxWarning)
1717-
self.assertIn("\"is\" with 'int' literal", str(caught[0].message))
1718+
self.assertEqual(sorted(wm.lineno for wm in caught), [5, 9])
1719+
for wm in caught:
1720+
self.assertEqual(wm.category, SyntaxWarning)
1721+
self.assertIn("\"is\" with 'int' literal", str(wm.message))
1722+
1723+
# Other code path is used for "try" with "except*".
1724+
source = textwrap.dedent("""
1725+
try:
1726+
pass
1727+
except *Exception:
1728+
pass
1729+
finally:
1730+
1 is 1 # line 7
1731+
try:
1732+
pass
1733+
except *Exception:
1734+
pass
1735+
finally: # nested
1736+
1 is 1 # line 13
1737+
""")
1738+
1739+
with warnings.catch_warnings(record=True) as caught:
1740+
warnings.simplefilter("always")
1741+
compile(source, '<stdin>', 'exec')
1742+
1743+
self.assertEqual(sorted(wm.lineno for wm in caught), [7, 13])
1744+
for wm in caught:
1745+
self.assertEqual(wm.category, SyntaxWarning)
1746+
self.assertIn("\"is\" with 'int' literal", str(wm.message))
1747+
17181748

17191749
class TestBooleanExpression(unittest.TestCase):
17201750
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix swallowing some syntax warnings in different modules if they
2+
accidentally have the same message and are emitted from the same line.

Python/_warnings.c

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,28 +1473,6 @@ PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
14731473
return 0;
14741474
}
14751475

1476-
/* Like PyErr_WarnExplicitObject, but automatically sets up context */
1477-
int
1478-
_PyErr_WarnExplicitObjectWithContext(PyObject *category, PyObject *message,
1479-
PyObject *filename, int lineno)
1480-
{
1481-
PyObject *unused_filename, *module, *registry;
1482-
int unused_lineno;
1483-
int stack_level = 1;
1484-
1485-
if (!setup_context(stack_level, NULL, &unused_filename, &unused_lineno,
1486-
&module, &registry)) {
1487-
return -1;
1488-
}
1489-
1490-
int rc = PyErr_WarnExplicitObject(category, message, filename, lineno,
1491-
module, registry);
1492-
Py_DECREF(unused_filename);
1493-
Py_DECREF(registry);
1494-
Py_DECREF(module);
1495-
return rc;
1496-
}
1497-
14981476
int
14991477
PyErr_WarnExplicit(PyObject *category, const char *text,
15001478
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
@@ -1962,8 +1962,8 @@ int
19621962
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
19631963
int end_lineno, int end_col_offset)
19641964
{
1965-
if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
1966-
filename, lineno) < 0)
1965+
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
1966+
filename, lineno, NULL, NULL) < 0)
19671967
{
19681968
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
19691969
/* Replace the SyntaxWarning exception with a SyntaxError

0 commit comments

Comments
 (0)