Skip to content

Commit dcb9cfd

Browse files
gh-134716: Support regular expressions in -W and PYTHONWARNINGS
1 parent 9ee0214 commit dcb9cfd

File tree

6 files changed

+93
-8
lines changed

6 files changed

+93
-8
lines changed

Doc/library/warnings.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,20 @@ the disposition of the match. Each entry is a tuple of the form (*action*,
157157

158158
* *message* is a string containing a regular expression that the start of
159159
the warning message must match, case-insensitively. In :option:`-W` and
160-
:envvar:`PYTHONWARNINGS`, *message* is a literal string that the start of the
161-
warning message must contain (case-insensitively), ignoring any whitespace at
160+
:envvar:`PYTHONWARNINGS`, if *message* starts and ends with a forward slash
161+
(``/``), it specifies a regular expression as above;
162+
otherwise it is a literal string that the start of the
163+
warning message must match (case-insensitively), ignoring any whitespace at
162164
the start or end of *message*.
163165

164166
* *category* is a class (a subclass of :exc:`Warning`) of which the warning
165167
category must be a subclass in order to match.
166168

167169
* *module* is a string containing a regular expression that the start of the
168170
fully qualified module name must match, case-sensitively. In :option:`-W` and
169-
:envvar:`PYTHONWARNINGS`, *module* is a literal string that the
171+
:envvar:`PYTHONWARNINGS`, if *module* starts and ends with a forward slash
172+
(``/``), it specifies a regular expression as above;
173+
otherwise it is a literal string that the
170174
fully qualified module name must be equal to (case-sensitively), ignoring any
171175
whitespace at the start or end of *module*.
172176

Doc/using/cmdline.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,8 +470,10 @@ Miscellaneous options
470470
The *action* field is as explained above but only applies to warnings that
471471
match the remaining fields.
472472

473-
The *message* field must match the whole warning message; this match is
474-
case-insensitive.
473+
The *message* field must match the start of the warning message;
474+
this match is case-insensitive.
475+
If it starts and ends with a forward slash (``/``), it specifies
476+
a regular expression, otherwise it specifies a literal string.
475477

476478
The *category* field matches the warning category
477479
(ex: ``DeprecationWarning``). This must be a class name; the match test
@@ -480,6 +482,10 @@ Miscellaneous options
480482

481483
The *module* field matches the (fully qualified) module name; this match is
482484
case-sensitive.
485+
If it starts and ends with a forward slash (``/``), it specifies
486+
a regular expression that the start of the fully qualified module name
487+
must match, otherwise it specifies a literal string that the fully
488+
qualified module name must be equal to.
483489

484490
The *lineno* field matches the line number, where zero matches all line
485491
numbers and is thus equivalent to an omitted line number.
@@ -497,6 +503,9 @@ Miscellaneous options
497503
See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
498504
details.
499505

506+
.. versionchanged:: next
507+
Added regular expression support for *message* and *module*.
508+
500509

501510
.. option:: -x
502511

@@ -971,6 +980,9 @@ conflict.
971980
See :ref:`warning-filter` and :ref:`describing-warning-filters` for more
972981
details.
973982

983+
.. versionchanged:: next
984+
Added regular expression support for *message* and *module*.
985+
974986

975987
.. envvar:: PYTHONFAULTHANDLER
976988

Doc/whatsnew/3.15.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,22 @@ Other language changes
273273
This speeds up class creation, and helps avoid reference cycles.
274274
(Contributed by Petr Viktorin in :gh:`135228`.)
275275

276+
* Warning filters specified by the :option:`-W` option and the
277+
:envvar:`PYTHONWARNINGS` environment variable can now use regular
278+
expressions for message and module.
279+
The corresponding field must be surrounded by slashes (``/``).
280+
281+
The :option:`-W` option and the :envvar:`PYTHONWARNINGS` environment
282+
variable can now specify use regular expressions for matching warning
283+
message and module if the corresponding field starts and ends by slashes
284+
(``/``).
285+
286+
The :option:`-W` option and the :envvar:`PYTHONWARNINGS` environment variable
287+
can now specify regular expressions instead of literal strings to match
288+
the warning message and the module name, if the corresponding field starts
289+
and ends with a forward slash (``/``).
290+
(Contributed by Serhiy Storchaka in :gh:`134716`.)
291+
276292

277293
New modules
278294
===========

Lib/_py_warnings.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,15 @@ def _setoption(arg):
369369
if message or module:
370370
import re
371371
if message:
372-
message = re.escape(message)
372+
if len(message) >= 2 and message[0] == message[-1] == '/':
373+
message = message[1:-1]
374+
else:
375+
message = re.escape(message)
373376
if module:
374-
module = re.escape(module) + r'\z'
377+
if len(module) >= 2 and module[0] == module[-1] == '/':
378+
module = module[1:-1]
379+
else:
380+
module = re.escape(module) + r'\z'
375381
if lineno:
376382
try:
377383
lineno = int(lineno)
@@ -381,7 +387,23 @@ def _setoption(arg):
381387
raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None
382388
else:
383389
lineno = 0
384-
_wm.filterwarnings(action, message, category, module, lineno)
390+
try:
391+
_wm.filterwarnings(action, message, category, module, lineno)
392+
except re.PatternError if message or module else ():
393+
if message:
394+
try:
395+
re.compile(message)
396+
except re.PatternError:
397+
raise _wm._OptionError(f"invalid regular expression for "
398+
f"message: {message!r}") from None
399+
if module:
400+
try:
401+
re.compile(module)
402+
except re.PatternError:
403+
raise _wm._OptionError(f"invalid regular expression for "
404+
f"module: {module!r}") from None
405+
# Should never happen.
406+
raise
385407

386408

387409
# Helper for _setoption()

Lib/test/test_warnings/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,10 @@ def test_improper_input(self):
755755
self.module._setoption('ignore::===')
756756
with self.assertRaisesRegex(self.module._OptionError, 'Wärning'):
757757
self.module._setoption('ignore::Wärning')
758+
with self.assertRaisesRegex(self.module._OptionError, 'message'):
759+
self.module._setoption('ignore:/?/:Warning')
760+
with self.assertRaisesRegex(self.module._OptionError, 'module'):
761+
self.module._setoption('ignore::Warning:/?/')
758762
self.module._setoption('error::Warning::0')
759763
self.assertRaises(UserWarning, self.module.warn, 'convert to error')
760764

@@ -769,6 +773,31 @@ def test_import_from_module(self):
769773
with self.assertRaises(TestWarning):
770774
self.module.warn('test warning', TestWarning)
771775

776+
def test_message(self):
777+
# Match prefix, case-insensitive.
778+
with self.module.catch_warnings():
779+
self.module._setoption('error:TEST WARN:UserWarning')
780+
with self.assertRaises(UserWarning):
781+
self.module.warn('Test Warning')
782+
with self.module.catch_warnings():
783+
self.module._setoption(r'error:/TE.*WARN/:UserWarning')
784+
with self.assertRaises(UserWarning):
785+
self.module.warn('Test Warning')
786+
787+
def test_module(self):
788+
with self.module.catch_warnings():
789+
self.module._setoption(f'error::UserWarning:{__name__}')
790+
with self.assertRaises(UserWarning):
791+
self.module.warn('test warning')
792+
# Only full match.
793+
self.module._setoption(f'ignore::UserWarning:{__name__[:-2]}')
794+
with self.assertRaises(UserWarning):
795+
self.module.warn('test warning')
796+
with self.module.catch_warnings():
797+
self.module._setoption(f'error::UserWarning:/{re.escape(__name__[:-2])}./')
798+
with self.assertRaises(UserWarning):
799+
self.module.warn('test warning')
800+
772801

773802
class CWCmdLineTests(WCmdLineTests, unittest.TestCase):
774803
module = c_warnings
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support of regular expressions in the :option:`-W` option and the
2+
:envvar:`PYTHONWARNINGS` environment variable.

0 commit comments

Comments
 (0)