https://github.com/python/cpython/commit/cf9ef73121cd2b96dd514e09b3ad253d841d91dd
commit: cf9ef73121cd2b96dd514e09b3ad253d841d91dd
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-09-16T17:06:44Z
summary:

gh-134716: Support regular expressions in -W and PYTHONWARNINGS (GH-138149)

files:
A Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst
M Doc/library/warnings.rst
M Doc/using/cmdline.rst
M Doc/whatsnew/3.15.rst
M Lib/_py_warnings.py
M Lib/test/test_warnings/__init__.py

diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst
index b6392450a1722f..5b17bd009b36e7 100644
--- a/Doc/library/warnings.rst
+++ b/Doc/library/warnings.rst
@@ -159,8 +159,10 @@ the disposition of the match.  Each entry is a tuple of 
the form (*action*,
 
 * *message* is a string containing a regular expression that the start of
   the warning message must match, case-insensitively.  In :option:`-W` and
-  :envvar:`PYTHONWARNINGS`, *message* is a literal string that the start of the
-  warning message must contain (case-insensitively), ignoring any whitespace at
+  :envvar:`PYTHONWARNINGS`, if *message* starts and ends with a forward slash
+  (``/``), it specifies a regular expression as above;
+  otherwise it is a literal string that the start of the
+  warning message must match (case-insensitively), ignoring any whitespace at
   the start or end of *message*.
 
 * *category* is a class (a subclass of :exc:`Warning`) of which the warning
@@ -168,7 +170,9 @@ the disposition of the match.  Each entry is a tuple of the 
form (*action*,
 
 * *module* is a string containing a regular expression that the start of the
   fully qualified module name must match, case-sensitively.  In :option:`-W` 
and
-  :envvar:`PYTHONWARNINGS`, *module* is a literal string that the
+  :envvar:`PYTHONWARNINGS`, if *module* starts and ends with a forward slash
+  (``/``), it specifies a regular expression as above;
+  otherwise it is a literal string that the
   fully qualified module name must be equal to (case-sensitively), ignoring any
   whitespace at the start or end of *module*.
 
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index 3f8b52e2345b25..74c18c2a6ede9c 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -479,8 +479,10 @@ Miscellaneous options
    The *action* field is as explained above but only applies to warnings that
    match the remaining fields.
 
-   The *message* field must match the whole warning message; this match is
-   case-insensitive.
+   The *message* field must match the start of the warning message;
+   this match is case-insensitive.
+   If it starts and ends with a forward slash (``/``), it specifies
+   a regular expression, otherwise it specifies a literal string.
 
    The *category* field matches the warning category
    (ex: ``DeprecationWarning``). This must be a class name; the match test
@@ -489,6 +491,10 @@ Miscellaneous options
 
    The *module* field matches the (fully qualified) module name; this match is
    case-sensitive.
+   If it starts and ends with a forward slash (``/``), it specifies
+   a regular expression that the start of the fully qualified module name
+   must match, otherwise it specifies a literal string that the fully
+   qualified module name must be equal to.
 
    The *lineno* field matches the line number, where zero matches all line
    numbers and is thus equivalent to an omitted line number.
@@ -506,6 +512,9 @@ Miscellaneous options
    See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
    details.
 
+   .. versionchanged:: next
+      Added regular expression support for *message* and *module*.
+
 
 .. option:: -x
 
@@ -980,6 +989,9 @@ conflict.
    See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
    details.
 
+   .. versionchanged:: next
+      Added regular expression support for *message* and *module*.
+
 
 .. envvar:: PYTHONFAULTHANDLER
 
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index ac09c57441e540..dc16c944886bd0 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -273,6 +273,12 @@ Other language changes
   This speeds up class creation, and helps avoid reference cycles.
   (Contributed by Petr Viktorin in :gh:`135228`.)
 
+* The :option:`-W` option and the :envvar:`PYTHONWARNINGS` environment variable
+  can now specify regular expressions instead of literal strings to match
+  the warning message and the module name, if the corresponding field starts
+  and ends with a forward slash (``/``).
+  (Contributed by Serhiy Storchaka in :gh:`134716`.)
+
 
 New modules
 ===========
diff --git a/Lib/_py_warnings.py b/Lib/_py_warnings.py
index 2e7113a637a2ac..576a17ea7b8501 100644
--- a/Lib/_py_warnings.py
+++ b/Lib/_py_warnings.py
@@ -369,9 +369,15 @@ def _setoption(arg):
     if message or module:
         import re
     if message:
-        message = re.escape(message)
+        if len(message) >= 2 and message[0] == message[-1] == '/':
+            message = message[1:-1]
+        else:
+            message = re.escape(message)
     if module:
-        module = re.escape(module) + r'\z'
+        if len(module) >= 2 and module[0] == module[-1] == '/':
+            module = module[1:-1]
+        else:
+            module = re.escape(module) + r'\z'
     if lineno:
         try:
             lineno = int(lineno)
@@ -381,7 +387,23 @@ def _setoption(arg):
             raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None
     else:
         lineno = 0
-    _wm.filterwarnings(action, message, category, module, lineno)
+    try:
+        _wm.filterwarnings(action, message, category, module, lineno)
+    except re.PatternError if message or module else ():
+        if message:
+            try:
+                re.compile(message)
+            except re.PatternError:
+                raise _wm._OptionError(f"invalid regular expression for "
+                                       f"message: {message!r}") from None
+        if module:
+            try:
+                re.compile(module)
+            except re.PatternError:
+                raise _wm._OptionError(f"invalid regular expression for "
+                                       f"module: {module!r}") from None
+        # Should never happen.
+        raise
 
 
 # Helper for _setoption()
diff --git a/Lib/test/test_warnings/__init__.py 
b/Lib/test/test_warnings/__init__.py
index 256713365286c2..260fae8fe243b6 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -755,6 +755,10 @@ def test_improper_input(self):
                 self.module._setoption('ignore::===')
             with self.assertRaisesRegex(self.module._OptionError, 'Wärning'):
                 self.module._setoption('ignore::Wärning')
+            with self.assertRaisesRegex(self.module._OptionError, 'message'):
+                self.module._setoption('ignore:/?/:Warning')
+            with self.assertRaisesRegex(self.module._OptionError, 'module'):
+                self.module._setoption('ignore::Warning:/?/')
             self.module._setoption('error::Warning::0')
             self.assertRaises(UserWarning, self.module.warn, 'convert to 
error')
 
@@ -769,6 +773,31 @@ def test_import_from_module(self):
             with self.assertRaises(TestWarning):
                 self.module.warn('test warning', TestWarning)
 
+    def test_message(self):
+        # Match prefix, case-insensitive.
+        with self.module.catch_warnings():
+            self.module._setoption('error:TEST WARN:UserWarning')
+            with self.assertRaises(UserWarning):
+                self.module.warn('Test Warning')
+        with self.module.catch_warnings():
+            self.module._setoption(r'error:/TE.*WARN/:UserWarning')
+            with self.assertRaises(UserWarning):
+                self.module.warn('Test Warning')
+
+    def test_module(self):
+        with self.module.catch_warnings():
+            self.module._setoption(f'error::UserWarning:{__name__}')
+            with self.assertRaises(UserWarning):
+                self.module.warn('test warning')
+            # Only full match.
+            self.module._setoption(f'ignore::UserWarning:{__name__[:-2]}')
+            with self.assertRaises(UserWarning):
+                self.module.warn('test warning')
+        with self.module.catch_warnings():
+            
self.module._setoption(f'error::UserWarning:/{re.escape(__name__[:-2])}./')
+            with self.assertRaises(UserWarning):
+                self.module.warn('test warning')
+
 
 class CWCmdLineTests(WCmdLineTests, unittest.TestCase):
     module = c_warnings
diff --git 
a/Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst 
b/Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst
new file mode 100644
index 00000000000000..8a65080973a19e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst
@@ -0,0 +1,2 @@
+Add support of regular expressions in the :option:`-W` option and the
+:envvar:`PYTHONWARNINGS` environment variable.

_______________________________________________
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