https://github.com/python/cpython/commit/279db6bede30be3a1b86803585eb4404d27800da
commit: 279db6bede30be3a1b86803585eb4404d27800da
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-10-14T17:48:09+03:00
summary:
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.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst
M Include/cpython/warnings.h
M Lib/test/test_compile.py
M Lib/test/test_pyrepl/test_interact.py
M Python/_warnings.c
M Python/compile.c
M Python/errors.c
diff --git a/Include/cpython/warnings.h b/Include/cpython/warnings.h
index 8731fd2e96b716..4e3eb88e8ff447 100644
--- a/Include/cpython/warnings.h
+++ b/Include/cpython/warnings.h
@@ -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);
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 1660dabe6815aa..bc8ef93cb8f9de 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1679,22 +1679,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
@@ -1705,16 +1704,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:
diff --git a/Lib/test/test_pyrepl/test_interact.py
b/Lib/test/test_pyrepl/test_interact.py
index 8c0eeab6dcae96..1a3146da8eadc8 100644
--- a/Lib/test/test_pyrepl/test_interact.py
+++ b/Lib/test/test_pyrepl/test_interact.py
@@ -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)
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst
new file mode 100644
index 00000000000000..f1344aedb58286
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst
@@ -0,0 +1,2 @@
+Fix swallowing some syntax warnings in different modules if they
+accidentally have the same message and are emitted from the same line.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 243a5e88e9dbbc..9989b623dbce3a 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -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, ®istry)) {
- 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,
diff --git a/Python/compile.c b/Python/compile.c
index c04391e682f9ac..8070d3f03760ef 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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);
diff --git a/Python/errors.c b/Python/errors.c
index 2688396004e98b..9fe95cec0ab794 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -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
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]