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

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

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

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 bcbea1b29320275..7fbb1c99823770c 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1536,6 +1536,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 4bb83b214ae6cc7..fcd2c774d0a8f8f 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -595,7 +595,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)
@@ -1100,8 +1102,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.