]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-131927: Prevent emitting optimizer warnings twice in the REPL (#131993)
authorTomas R. <tomas.roun8@gmail.com>
Sat, 12 Apr 2025 10:34:36 +0000 (12:34 +0200)
committerGitHub <noreply@github.com>
Sat, 12 Apr 2025 10:34:36 +0000 (11:34 +0100)
Include/cpython/warnings.h
Lib/test/test_compile.py
Lib/test/test_pyrepl/test_interact.py
Python/_warnings.c
Python/errors.c

index 4e3eb88e8ff4472b37f1903184097757271d6c5b..8731fd2e96b716fd87d988d40320e0ab4b15651f 100644 (file)
@@ -18,3 +18,9 @@ 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 9cc025d85e168a6f3b18555e32c37f630492ba05..0377b3f954f44bda3f5299eb70297a154ba5cdfc 100644 (file)
@@ -1646,6 +1646,24 @@ 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.
+        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)
+
+        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)
+
 class TestBooleanExpression(unittest.TestCase):
     class Value:
         def __init__(self):
index af5d4d0e67632a19ad8dec0387027ea448b5dcb2..a20719033fc9b7b8b030dd981e738c034131ae9a 100644 (file)
@@ -1,6 +1,7 @@
 import contextlib
 import io
 import unittest
+import warnings
 from unittest.mock import patch
 from textwrap import dedent
 
@@ -273,3 +274,28 @@ 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)
index 912468d2a59a95e7c0f02be0f3c92540127553eb..39bf1b225ccb0c69333bf7e688995d66a733d0b7 100644 (file)
@@ -1479,6 +1479,28 @@ 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 14999d6dbaf72ef799250804dd2a9c02fbb3bd33..2b088e2f3888a1ccc766296b38e9107fb6a6088e 100644 (file)
@@ -1906,8 +1906,8 @@ int
 _PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int col_offset,
                          int end_lineno, int end_col_offset)
 {
-    if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, filename,
-                                 lineno, NULL, NULL) < 0)
+    if (_PyErr_WarnExplicitObjectWithContext(PyExc_SyntaxWarning, msg,
+                                             filename, lineno) < 0)
     {
         if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
             /* Replace the SyntaxWarning exception with a SyntaxError