Skip to content

Commit 6cb99f0

Browse files
gh-139640: Fix swallowing syntax warnings in different modules
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 162997b commit 6cb99f0

File tree

7 files changed

+62
-73
lines changed

7 files changed

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

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

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

16841683
def test_compile_warning_in_finally(self):
16851684
# Ensure that warnings inside finally blocks are
@@ -1690,16 +1689,47 @@ def test_compile_warning_in_finally(self):
16901689
try:
16911690
pass
16921691
finally:
1693-
1 is 1
1692+
1 is 1 # line 5
1693+
try:
1694+
pass
1695+
finally: # nested
1696+
1 is 1 # line 9
16941697
""")
16951698

16961699
with warnings.catch_warnings(record=True) as caught:
1697-
warnings.simplefilter("default")
1700+
warnings.simplefilter("always")
16981701
compile(source, '<stdin>', 'exec')
16991702

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

17041734
class TestBooleanExpression(unittest.TestCase):
17051735
class Value:

Lib/test/test_pyrepl/test_interact.py

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -274,28 +274,3 @@ def test_incomplete_statement(self):
274274
code = "if foo:"
275275
console = InteractiveColoredConsole(namespace, filename="<stdin>")
276276
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)