https://github.com/python/cpython/commit/00c4a946f48ccbef865deaa78d7578b9fecf1121
commit: 00c4a946f48ccbef865deaa78d7578b9fecf1121
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-05-03T09:35:47Z
summary:

gh-143231: Add the module attribute to warnings.WarningMessage (GH-149298)

files:
A Misc/NEWS.d/next/Library/2026-05-02-18-23-50.gh-issue-143231.oBbQb5.rst
M Lib/_py_warnings.py
M Lib/test/test_warnings/__init__.py
M Python/_warnings.c

diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py
index 81a386c4487d95..ab09913de6812d 100644
--- a/Lib/_py_warnings.py
+++ b/Lib/_py_warnings.py
@@ -620,17 +620,18 @@ def warn_explicit(message, category, filename, lineno,
     linecache.getlines(filename, module_globals)
 
     # Print message and context
-    msg = _wm.WarningMessage(message, category, filename, lineno, 
source=source)
+    msg = _wm.WarningMessage(message, category, filename, lineno,
+                             module=module, source=source)
     _wm._showwarnmsg(msg)
 
 
 class WarningMessage(object):
 
     _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
-                        "line", "source")
+                        "line", "source", "module")
 
     def __init__(self, message, category, filename, lineno, file=None,
-                 line=None, source=None):
+                 line=None, source=None, module=None):
         self.message = message
         self.category = category
         self.filename = filename
@@ -638,12 +639,14 @@ def __init__(self, message, category, filename, lineno, 
file=None,
         self.file = file
         self.line = line
         self.source = source
+        self.module = module
         self._category_name = category.__name__ if category else None
 
     def __str__(self):
-        return ("{message : %r, category : %r, filename : %r, lineno : %s, "
-                    "line : %r}" % (self.message, self._category_name,
-                                    self.filename, self.lineno, self.line))
+        return ("{message : %r, category : %r, module : %r, "
+                "filename : %r, lineno : %s, line : %r}" % (
+                    self.message, self._category_name, self.module,
+                    self.filename, self.lineno, self.line))
 
     def __repr__(self):
         return f'<{type(self).__qualname__} {self}>'
diff --git a/Lib/test/test_warnings/__init__.py 
b/Lib/test/test_warnings/__init__.py
index d86844c1a29a9a..bf1bcf8e6ed5d9 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -582,15 +582,19 @@ def test_warn_nonstandard_types(self):
                 # ``Warning() != Warning()``.
                 self.assertEqual(str(w[-1].message), str(UserWarning(ob)))
 
-    def test_filename(self):
+    def test_filename_module(self):
         with warnings_state(self.module):
             with self.module.catch_warnings(record=True) as w:
                 warning_tests.inner("spam1")
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "stacklevel.py")
+                self.assertEqual(w[-1].module,
+                                 "test.test_warnings.data.stacklevel")
                 warning_tests.outer("spam2")
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "stacklevel.py")
+                self.assertEqual(w[-1].module,
+                                 "test.test_warnings.data.stacklevel")
 
     def test_stacklevel(self):
         # Test stacklevel argument
@@ -600,23 +604,32 @@ def test_stacklevel(self):
                 warning_tests.inner("spam3", stacklevel=1)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "stacklevel.py")
+                self.assertEqual(w[-1].module,
+                                 "test.test_warnings.data.stacklevel")
                 warning_tests.outer("spam4", stacklevel=1)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "stacklevel.py")
+                self.assertEqual(w[-1].module,
+                                 "test.test_warnings.data.stacklevel")
 
                 warning_tests.inner("spam5", stacklevel=2)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "__init__.py")
+                self.assertEqual(w[-1].module, __name__)
                 warning_tests.outer("spam6", stacklevel=2)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "stacklevel.py")
+                self.assertEqual(w[-1].module,
+                                 "test.test_warnings.data.stacklevel")
                 warning_tests.outer("spam6.5", stacklevel=3)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "__init__.py")
+                self.assertEqual(w[-1].module, __name__)
 
                 warning_tests.inner("spam7", stacklevel=9999)
                 self.assertEqual(os.path.basename(w[-1].filename),
                                     "<sys>")
+                self.assertEqual(w[-1].module, "sys")
 
     def test_stacklevel_import(self):
         # Issue #24305: With stacklevel=2, module-level warnings should work.
@@ -627,6 +640,7 @@ def test_stacklevel_import(self):
                 import test.test_warnings.data.import_warning  # noqa: F401
                 self.assertEqual(len(w), 1)
                 self.assertEqual(w[0].filename, __file__)
+                self.assertEqual(w[0].module, __name__)
 
     def test_skip_file_prefixes(self):
         with warnings_state(self.module):
@@ -638,20 +652,27 @@ def test_skip_file_prefixes(self):
                         "inner_api", stacklevel=2,
                         warnings_module=warning_tests.warnings)
                 self.assertEqual(w[-1].filename, __file__)
+                self.assertEqual(w[-1].module, __name__)
                 warning_tests.package("package api", stacklevel=2)
                 self.assertEqual(w[-1].filename, __file__)
+                self.assertEqual(w[-1].module, __name__)
                 self.assertEqual(w[-2].filename, w[-1].filename)
+                self.assertEqual(w[-2].module, w[-1].module)
                 # Low stacklevels are overridden to 2 behavior.
                 warning_tests.package("package api 1", stacklevel=1)
                 self.assertEqual(w[-1].filename, __file__)
+                self.assertEqual(w[-1].module, __name__)
                 warning_tests.package("package api 0", stacklevel=0)
                 self.assertEqual(w[-1].filename, __file__)
+                self.assertEqual(w[-1].module, __name__)
                 warning_tests.package("package api -99", stacklevel=-99)
                 self.assertEqual(w[-1].filename, __file__)
+                self.assertEqual(w[-1].module, __name__)
 
                 # The stacklevel still goes up out of the package.
                 warning_tests.package("prefix02", stacklevel=3)
                 self.assertIn("unittest", w[-1].filename)
+                self.assertStartsWith(w[-1].module, "unittest")
 
     def test_skip_file_prefixes_file_path(self):
         # see: gh-126209
@@ -662,6 +683,8 @@ def test_skip_file_prefixes_file_path(self):
 
             self.assertEqual(len(w), 1)
             self.assertNotEqual(w[-1].filename, skipped)
+            self.assertEqual(w[-1].filename, __file__)
+            self.assertEqual(w[-1].module, __name__)
 
     def test_skip_file_prefixes_type_errors(self):
         with warnings_state(self.module):
@@ -673,7 +696,7 @@ def test_skip_file_prefixes_type_errors(self):
             with self.assertRaises(TypeError):
                 warn("msg", skip_file_prefixes="a sequence of strs")
 
-    def test_exec_filename(self):
+    def test_exec_filename_module(self):
         filename = "<warnings-test>"
         codeobj = compile(("import warnings\n"
                            "warnings.warn('hello', UserWarning)"),
@@ -682,6 +705,12 @@ def test_exec_filename(self):
             self.module.simplefilter("always", category=UserWarning)
             exec(codeobj)
         self.assertEqual(w[0].filename, filename)
+        self.assertEqual(w[0].module, __name__)
+        with self.module.catch_warnings(record=True) as w:
+            self.module.simplefilter("always", category=UserWarning)
+            exec(codeobj, {})
+        self.assertEqual(w[0].filename, filename)
+        self.assertEqual(w[0].module, '<string>')
 
     def test_warn_explicit_non_ascii_filename(self):
         with self.module.catch_warnings(record=True) as w:
diff --git 
a/Misc/NEWS.d/next/Library/2026-05-02-18-23-50.gh-issue-143231.oBbQb5.rst 
b/Misc/NEWS.d/next/Library/2026-05-02-18-23-50.gh-issue-143231.oBbQb5.rst
new file mode 100644
index 00000000000000..e4769866c2045d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-05-02-18-23-50.gh-issue-143231.oBbQb5.rst
@@ -0,0 +1 @@
+A *module* attribute has been added to :class:`!warnings.WarningMessage`.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 6b8fa19ff3f606..4f6de50efa14a8 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -716,7 +716,7 @@ static int
 call_show_warning(PyThreadState *tstate, PyObject *category,
                   PyObject *text, PyObject *message,
                   PyObject *filename, int lineno, PyObject *lineno_obj,
-                  PyObject *sourceline, PyObject *source)
+                  PyObject *sourceline, PyObject *source, PyObject *module)
 {
     PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL;
     PyInterpreterState *interp = tstate->interp;
@@ -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, source,
+            filename, lineno_obj, Py_None, Py_None,
+            source ? source : Py_None, module,
             NULL);
     Py_DECREF(warnmsg_cls);
     if (msg == NULL)
@@ -878,7 +879,7 @@ warn_explicit(PyThreadState *tstate, PyObject *category, 
PyObject *message,
         goto return_none;
     if (rc == 0) {
         if (call_show_warning(tstate, category, text, message, filename,
-                              lineno, lineno_obj, sourceline, source) < 0)
+                              lineno, lineno_obj, sourceline, source, module) 
< 0)
             goto cleanup;
     }
     else /* if (rc == -1) */

_______________________________________________
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