]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-139640: Fix swallowing syntax warnings in different modules (GH-139755...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 14 Oct 2025 15:56:01 +0000 (17:56 +0200)
committerGitHub <noreply@github.com>
Tue, 14 Oct 2025 15:56:01 +0000 (15:56 +0000)
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 279db6bede30be3a1b86803585eb4404d27800da)

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>
Include/cpython/warnings.h
Lib/test/test_compile.py
Lib/test/test_pyrepl/test_interact.py
Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-10-03-37.gh-issue-139640.gY5oTb.rst [new file with mode: 0644]
Python/_warnings.c
Python/compile.c
Python/errors.c

index 8731fd2e96b716fd87d988d40320e0ab4b15651f..4e3eb88e8ff4472b37f1903184097757271d6c5b 100644 (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);
index 7d7409ef93aa5f1f7001bf138dbdf2fa897c7a91..b2beba6e70e2ae53da95bd2ce0ed465214318d0a 100644 (file)
@@ -1662,22 +1662,21 @@ class TestSpecifics(unittest.TestCase):
         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
@@ -1688,16 +1687,47 @@ class TestSpecifics(unittest.TestCase):
             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:
index a20719033fc9b7b8b030dd981e738c034131ae9a..af5d4d0e67632a19ad8dec0387027ea448b5dcb2 100644 (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 @@ class TestMoreLines(unittest.TestCase):
         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 (file)
index 0000000..396e40f
--- /dev/null
@@ -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.
index 39bf1b225ccb0c69333bf7e688995d66a733d0b7..912468d2a59a95e7c0f02be0f3c92540127553eb 100644 (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,
index c04391e682f9ac90f06f0898fd99d3e28285580c..8070d3f03760efcd4f3c1fc349a84220b9435df4 100644 (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);
index 81f267b043afafc2ab4843fbc5cc4399f2f4d799..89ea79f8c914fb88dbbf17e00aaa95c1ce580b3b 100644 (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