diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 00bafd1be4bd0c..9886cacfe1eaad 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -229,6 +229,39 @@ Some examples:: ignore,default:::mymodule # Only report warnings triggered by "mymodule" error:::mymodule # Convert warnings to errors in "mymodule" +.. _warning-filter-examples: + +Warning Filter Examples +~~~~~~~~~~~~~~~~~~~~~~~ + +Here are some complex examples for filtering warnings. + +Note that :func:`filterwarnings` filters have subtle differences +from :option:`-W` and :envvar:`PYTHONWARNINGS` regarding the *message* and *module* +parts of the filter (as described in :ref:`warning-filter`). +Mainly, 'message' and 'module' are regular expressions in the former, +but literal strings in the latter two. + +:: + + filterwarnings("ignore", message=".*generic", module=r"yourmodule\.submodule") + # Ignore warnings in "yourmodule.submodule" which contain "generic". + # Note that the '.' in 'message' marks any character and in 'module' it is escaped, + # in order to match a literal dot character. + filterwarnings("ignore", message="generic", module=r"yourmodule\.submodule") + # Ignore warnings in "yourmodule.submodule" which START with "generic". + filterwarnings("ignore", module="yourmodule.*") + # Ignore all warnings in "yourmodule" and its submodules. + # Note that the '.' in 'module' marks any character so is not escaped. + + -W "ignore:generic::yourmodule.submodule:" + # Ignore warnings in "yourmodule.submodule" which START with "generic" + # (but not those containing it). + # Also note that the '.' in the module part does not need to be escaped + # since it is not a regular expression. + -W "ignore:::yourmodule:" + # Ignore all warnings in "yourmodule", but NOT in its submodules. + .. _default-warning-filter: diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 05710c469348c4..07817f4bc0aa0f 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -318,6 +318,18 @@ def test_message_matching(self): self.module.warn("something completely different") self.assertEqual(w, []) + def test_message_matching_regex(self): + with original_warnings.catch_warnings(record=True, + module=self.module) as w: + self.module.simplefilter("ignore", UserWarning) + self.module.filterwarnings("error", ".*match", UserWarning) + self.assertRaises(UserWarning, self.module.warn, "match") + self.assertRaises(UserWarning, self.module.warn, "match prefix") + self.assertRaises(UserWarning, self.module.warn, "suffix match") + self.assertEqual(w, []) + self.module.warn("not a m4tch") + self.assertEqual(w, []) + def test_mutate_filter_list(self): class X: def match(self, a): @@ -1352,6 +1364,18 @@ def test_single_warning(self): PYTHONDEVMODE="") self.assertEqual(stdout, b"['ignore::DeprecationWarning']") + def test_string_literals(self): + # Ensure message/module are treated as string literals + rc, stdout, stderr = assert_python_ok("-c", + "import sys, warnings; " + "sys.stdout.write(warnings.filters[0][1].pattern); " + "sys.stderr.write(warnings.filters[0][3].pattern)", + PYTHONWARNINGS="ignore:.generic::yourmodule.submodule", + PYTHONDEVMODE="") + self.assertEqual(stdout, rb"\.generic") + # '\Z' is added to the module name, so check start of pattern: + self.assertTrue(stderr.startswith(rb"yourmodule\.submodule")) + def test_comma_separated_warnings(self): rc, stdout, stderr = assert_python_ok("-c", "import sys; sys.stdout.write(str(sys.warnoptions))", diff --git a/Misc/NEWS.d/next/Documentation/2023-07-11-08-46-13.gh-issue-93343.fw_eyw.rst b/Misc/NEWS.d/next/Documentation/2023-07-11-08-46-13.gh-issue-93343.fw_eyw.rst new file mode 100644 index 00000000000000..6dae32598cb5aa --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2023-07-11-08-46-13.gh-issue-93343.fw_eyw.rst @@ -0,0 +1,2 @@ +Add examples of warning filters and the difference between programmatic and +environmental filters.