]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-134716: Support regular expressions in -W and PYTHONWARNINGS (GH-138149)
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 16 Sep 2025 17:06:44 +0000 (20:06 +0300)
committerGitHub <noreply@github.com>
Tue, 16 Sep 2025 17:06:44 +0000 (17:06 +0000)
Doc/library/warnings.rst
Doc/using/cmdline.rst
Doc/whatsnew/3.15.rst
Lib/_py_warnings.py
Lib/test/test_warnings/__init__.py
Misc/NEWS.d/next/Library/2025-08-25-22-38-03.gh-issue-134716.kyYKeX.rst [new file with mode: 0644]

index b6392450a1722f0df84ca588c99c365fd4d5e709..5b17bd009b36e71394106593ea1a4d156da02186 100644 (file)
@@ -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*.
 
index 3f8b52e2345b25ff27b1e3c00c3414040663cbb3..74c18c2a6ede9c6d1cb1e07a297c2f2299262353 100644 (file)
@@ -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
 
index ac09c57441e540b6d3252d5c4497c0d0ee903018..dc16c944886bd0fbf6cb51f7fb26c23fbb206cfb 100644 (file)
@@ -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
 ===========
index 2e7113a637a2ac16ffb59e2ce937e24989a0e5db..576a17ea7b85013997cca3af9f6d636c8bfa1a89 100644 (file)
@@ -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()
index 256713365286c2ca5709e764c3a8678f15fc4b20..260fae8fe243b6f1a36ccee7862cf897d6b2c783 100644 (file)
@@ -755,6 +755,10 @@ class WCmdLineTests(BaseTest):
                 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 @@ class WCmdLineTests(BaseTest):
             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 (file)
index 0000000..8a65080
--- /dev/null
@@ -0,0 +1,2 @@
+Add support of regular expressions in the :option:`-W` option and the
+:envvar:`PYTHONWARNINGS` environment variable.