From b4e048411f4c62ad7343bca32c307f0bf5ef74b4 Mon Sep 17 00:00:00 2001 From: Zac Hatfield-Dodds Date: Sat, 23 Apr 2022 17:55:22 -0700 Subject: [PATCH] gh-91230: Concise catch_warnings with simplefilter (#91435) --- Doc/library/warnings.rst | 10 +++++++++- Doc/whatsnew/3.11.rst | 7 +++++++ Lib/test/test_warnings/__init__.py | 19 +++++++++++++++++++ Lib/warnings.py | 12 +++++++++++- ...2-04-10-17-12-23.gh-issue-91230.T1d_fG.rst | 3 +++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-04-10-17-12-23.gh-issue-91230.T1d_fG.rst diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index 289b28229e1..f7a1f70833b 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -491,7 +491,7 @@ Available Functions Available Context Managers -------------------------- -.. class:: catch_warnings(*, record=False, module=None) +.. class:: catch_warnings(*, record=False, module=None, action=None, category=Warning, lineno=0, append=False) A context manager that copies and, upon exit, restores the warnings filter and the :func:`showwarning` function. @@ -507,6 +507,10 @@ Available Context Managers protected. This argument exists primarily for testing the :mod:`warnings` module itself. + If the *action* argument is not ``None``, the remaining arguments are + passed to :func:`simplefilter` as if it were called immediately on + entering the context. + .. note:: The :class:`catch_warnings` manager works by replacing and @@ -514,3 +518,7 @@ Available Context Managers :func:`showwarning` function and internal list of filter specifications. This means the context manager is modifying global state and therefore is not thread-safe. + + .. versionchanged:: 3.11 + + Added the *action*, *category*, *lineno*, and *append* parameters. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 34742515168..43d8e6b1cbf 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -529,6 +529,13 @@ venv Third party code that also creates new virtual environments should do the same. (Contributed by Miro HronĨok in :issue:`45413`.) +warnings +-------- + +* :func:`warnings.catch_warnings` now accepts arguments for :func:`warnings.simplefilter`, + providing a more concise way to locally ignore warnings or convert them to errors. + (Contributed by Zac Hatfield-Dodds in :issue:`47074`.) + zipfile ------- diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index f7f93113071..0f960b82bfa 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -373,6 +373,25 @@ def test_append_duplicate(self): "appended duplicate changed order of filters" ) + def test_catchwarnings_with_simplefilter_ignore(self): + with original_warnings.catch_warnings(module=self.module): + self.module.resetwarnings() + self.module.simplefilter("error") + with self.module.catch_warnings( + module=self.module, action="ignore" + ): + self.module.warn("This will be ignored") + + def test_catchwarnings_with_simplefilter_error(self): + with original_warnings.catch_warnings(module=self.module): + self.module.resetwarnings() + with self.module.catch_warnings( + module=self.module, action="error", category=FutureWarning + ): + self.module.warn("Other types of warnings are not errors") + self.assertRaises(FutureWarning, + self.module.warn, FutureWarning("msg")) + class CFilterTests(FilterTests, unittest.TestCase): module = c_warnings diff --git a/Lib/warnings.py b/Lib/warnings.py index 887ca6ecd1a..7d8c4400127 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -432,9 +432,13 @@ class catch_warnings(object): named 'warnings' and imported under that name. This argument is only useful when testing the warnings module itself. + If the 'action' argument is not None, the remaining arguments are passed + to warnings.simplefilter() as if it were called immediately on entering the + context. """ - def __init__(self, *, record=False, module=None): + def __init__(self, *, record=False, module=None, + action=None, category=Warning, lineno=0, append=False): """Specify whether to record warnings and if an alternative module should be used other than sys.modules['warnings']. @@ -445,6 +449,10 @@ def __init__(self, *, record=False, module=None): self._record = record self._module = sys.modules['warnings'] if module is None else module self._entered = False + if action is None: + self._filter = None + else: + self._filter = (action, category, lineno, append) def __repr__(self): args = [] @@ -464,6 +472,8 @@ def __enter__(self): self._module._filters_mutated() self._showwarning = self._module.showwarning self._showwarnmsg_impl = self._module._showwarnmsg_impl + if self._filter is not None: + simplefilter(*self._filter) if self._record: log = [] self._module._showwarnmsg_impl = log.append diff --git a/Misc/NEWS.d/next/Library/2022-04-10-17-12-23.gh-issue-91230.T1d_fG.rst b/Misc/NEWS.d/next/Library/2022-04-10-17-12-23.gh-issue-91230.T1d_fG.rst new file mode 100644 index 00000000000..1efc7afca4e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-10-17-12-23.gh-issue-91230.T1d_fG.rst @@ -0,0 +1,3 @@ +:func:`warnings.catch_warnings` now accepts arguments for +:func:`warnings.simplefilter`, providing a more concise way to +locally ignore warnings or convert them to errors.