[3.14] gh-155319: Fix the source line of a warning issued with module_globals (GH-155320) (GH-155825)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/94e5346c8459bc656070117dfd456f4476d0c045
commit: 94e5346c8459bc656070117dfd456f4476d0c045
branch: 3.14
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-15T07:39:27Z
summary:

[3.14] gh-155319: Fix the source line of a warning issued with module_globals (GH-155320) (GH-155825)

warn_explicit() computed the source line from the loader of the module
whose globals were passed as module_globals, but did not pass it to
WarningMessage.  It also raised IndexError if lineno was out of the range
of the module source.

(cherry picked from commit c92e2fd8f9c8af3ea383dec6222cfcca47a405ff)

files:
A Misc/NEWS.d/next/Library/2026-08-07-02-20-00.gh-issue-155319.srcline.rst
M Lib/test/test_warnings/__init__.py
M Python/_warnings.c

diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index 799ea4939ddab59..b757b5a534583e0 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1601,6 +1601,37 @@ def test_RuntimeError(self):
                                             _version=version)
 
 
+class WarnExplicitSourceTests(BaseTest):
+    # gh-155319: the source line is taken from the loader of the module
+    # whose globals are passed as module_globals, if the file which name
+    # is passed as filename cannot be read.
+
+    def warn_explicit(self, lineno):
+        with support.captured_stderr() as stderr:
+            with self.module.catch_warnings():
+                self.module.simplefilter("always")
+                self.module.warn_explicit(
+                    'eggs', UserWarning, 'nonexistent', lineno,
+                    module_globals=warning_tests.__dict__)
+        return stderr.getvalue()
+
+    def test_source_line(self):
+        source = warning_tests.__loader__.get_source(warning_tests.__name__)
+        expected = source.splitlines()[0].strip()
+        self.assertEqual(self.warn_explicit(1),
+                         f'nonexistent:1: UserWarning: eggs\n  {expected}\n')
+
+    def test_source_line_out_of_range(self):
+        self.assertEqual(self.warn_explicit(1000),
+                         'nonexistent:1000: UserWarning: eggs\n')
+
+class CWarnExplicitSourceTests(WarnExplicitSourceTests, unittest.TestCase):
+    module = c_warnings
+
+class PyWarnExplicitSourceTests(WarnExplicitSourceTests, unittest.TestCase):
+    module = py_warnings
+
+
 class BootstrapTest(unittest.TestCase):
 
     def test_issue_8766(self):
diff --git a/Misc/NEWS.d/next/Library/2026-08-07-02-20-00.gh-issue-155319.srcline.rst b/Misc/NEWS.d/next/Library/2026-08-07-02-20-00.gh-issue-155319.srcline.rst
new file mode 100644
index 000000000000000..34a7ff83a73c0b8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-07-02-20-00.gh-issue-155319.srcline.rst
@@ -0,0 +1,4 @@
+:func:`warnings.warn_explicit` now displays the source line taken from the
+loader of the module whose globals are passed as *module_globals*.  It also
+no longer raises :exc:`IndexError` if *lineno* is out of the range of the
+module source.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 912468d2a59a95e..3c8726a48058cf0 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -768,7 +768,9 @@ call_show_warning(PyThreadState *tstate, PyObject *category,
     }
 
     msg = PyObject_CallFunctionObjArgs(warnmsg_cls, message, category,
-            filename, lineno_obj, Py_None, Py_None, source,
+            filename, lineno_obj, Py_None,
+            sourceline ? sourceline : Py_None,
+            source,
             NULL);
     Py_DECREF(warnmsg_cls);
     if (msg == NULL)
@@ -1269,8 +1271,11 @@ get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno
     }
 
     /* Get the source line. */
-    source_line = PyList_GetItem(source_list, lineno-1);
-    Py_XINCREF(source_line);
+    if (lineno < 1 || lineno > PyList_GET_SIZE(source_list)) {
+        Py_DECREF(source_list);
+        return NULL;
+    }
+    source_line = Py_NewRef(PyList_GET_ITEM(source_list, lineno-1));
     Py_DECREF(source_list);
     return source_line;
 }

_______________________________________________
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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.