https://github.com/python/cpython/commit/c92e2fd8f9c8af3ea383dec6222cfcca47a405ff
commit: c92e2fd8f9c8af3ea383dec6222cfcca47a405ff
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-15T09:54:58+03:00
summary:

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

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.

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 bf1bcf8e6ed5d9a..cde6076dd2f44b7 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1661,6 +1661,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 4f6de50efa14a8e..992c3e50f2a9f67 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -747,7 +747,8 @@ call_show_warning(PyThreadState *tstate, PyObject *category,
     }
 
     msg = PyObject_CallFunctionObjArgs(warnmsg_cls, message, category,
-            filename, lineno_obj, Py_None, Py_None,
+            filename, lineno_obj, Py_None,
+            sourceline ? sourceline : Py_None,
             source ? source : Py_None, module,
             NULL);
     Py_DECREF(warnmsg_cls);
@@ -1234,8 +1235,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]

Reply via email to