]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-119049: Fix incorrect display of warning which is constructed by C API (GH-119063)
authorKirill Podoprigora <kirill.bast9@mail.ru>
Thu, 16 May 2024 20:27:59 +0000 (23:27 +0300)
committerGitHub <noreply@github.com>
Thu, 16 May 2024 20:27:59 +0000 (20:27 +0000)
The source line was not displayed if the warnings module had not yet
been imported.

Lib/test/test_capi/test_exceptions.py
Misc/NEWS.d/next/Core and Builtins/2024-05-16-23-02-03.gh-issue-119049.qpd_S-.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Python/_warnings.c

index 1d158e3586e98dc6e5675f6351a86c40f2311ccb..c475b6d78d0c56d0cb20428f0b05944988dc1e67 100644 (file)
@@ -3,11 +3,12 @@ import os
 import re
 import sys
 import unittest
+import textwrap
 
 from test import support
 from test.support import import_helper
 from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE
-from test.support.script_helper import assert_python_failure
+from test.support.script_helper import assert_python_failure, assert_python_ok
 from test.support.testcase import ExceptionIsLikeMixin
 
 from .test_misc import decode_stderr
@@ -68,6 +69,47 @@ class Test_Exceptions(unittest.TestCase):
         else:
             self.assertTrue(False)
 
+    def test_warn_with_stacklevel(self):
+        code = textwrap.dedent('''\
+            import _testcapi
+
+            def foo():
+                _testcapi.function_set_warning()
+
+            foo()  # line 6
+
+
+            foo()  # line 9
+        ''')
+        proc = assert_python_ok("-c", code)
+        warnings = proc.err.splitlines()
+        self.assertEqual(warnings, [
+            b'<string>:6: RuntimeWarning: Testing PyErr_WarnEx',
+            b'  foo()  # line 6',
+            b'<string>:9: RuntimeWarning: Testing PyErr_WarnEx',
+            b'  foo()  # line 9',
+        ])
+
+    def test_warn_during_finalization(self):
+        code = textwrap.dedent('''\
+            import _testcapi
+
+            class Foo:
+                def foo(self):
+                    _testcapi.function_set_warning()
+                def __del__(self):
+                    self.foo()
+
+            ref = Foo()
+        ''')
+        proc = assert_python_ok("-c", code)
+        warnings = proc.err.splitlines()
+        # Due to the finalization of the interpreter, the source will be ommited
+        # because the ``warnings`` module cannot be imported at this time
+        self.assertEqual(warnings, [
+            b'<string>:7: RuntimeWarning: Testing PyErr_WarnEx',
+        ])
+
 
 class Test_FatalError(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-16-23-02-03.gh-issue-119049.qpd_S-.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-16-23-02-03.gh-issue-119049.qpd_S-.rst
new file mode 100644 (file)
index 0000000..1d7aad8
--- /dev/null
@@ -0,0 +1,2 @@
+Fix displaying the source line for warnings created by the C API if the
+:mod:`warnings` module had not yet been imported.
index ff31724c0e9ff9ff1531c81b98f0a7195b295d8b..f99ebf0dde4f9e4a5ccdf0bddc4c412f3911f53e 100644 (file)
@@ -3303,6 +3303,15 @@ failed:
     return NULL;
 }
 
+static PyObject *
+function_set_warning(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
+{
+    if (PyErr_WarnEx(PyExc_RuntimeWarning, "Testing PyErr_WarnEx", 2)) {
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+
 static PyMethodDef TestMethods[] = {
     {"set_errno",               set_errno,                       METH_VARARGS},
     {"test_config",             test_config,                     METH_NOARGS},
@@ -3444,6 +3453,7 @@ static PyMethodDef TestMethods[] = {
     {"function_set_closure", function_set_closure, METH_VARARGS, NULL},
     {"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
     {"test_weakref_capi", test_weakref_capi, METH_NOARGS},
+    {"function_set_warning", function_set_warning, METH_NOARGS},
     {NULL, NULL} /* sentinel */
 };
 
index 793cbc657f318450a4c9ea8d8f3f3af9ec07f4a9..17404d33c1cc9b01fd034f74318d23bd7c895616 100644 (file)
@@ -569,10 +569,9 @@ call_show_warning(PyThreadState *tstate, PyObject *category,
     PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL;
     PyInterpreterState *interp = tstate->interp;
 
-    /* If the source parameter is set, try to get the Python implementation.
-       The Python implementation is able to log the traceback where the source
+    /* The Python implementation is able to log the traceback where the source
        was allocated, whereas the C implementation doesn't. */
-    show_fn = GET_WARNINGS_ATTR(interp, _showwarnmsg, source != NULL);
+    show_fn = GET_WARNINGS_ATTR(interp, _showwarnmsg, 1);
     if (show_fn == NULL) {
         if (PyErr_Occurred())
             return -1;