summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Finucane <stephenfin@redhat.com>2021-12-24 11:21:24 +0000
committerStephen Finucane <stephen@that.guru>2022-10-19 11:51:24 +0100
commit4cd367fb4bb9038c3b236532173ea68ba8c6d5c3 (patch)
tree696ce9e09f38ed3a19df4e619c683f9a80cd3ad6
parentf0376a18496f2707f41a028882a500b83192b8d0 (diff)
downloadfixtures-git-4cd367fb4bb9038c3b236532173ea68ba8c6d5c3.tar.gz
Add WarningsFilter fixture
This has enough users around OpenStack to justify adding it to 'fixtures' proper. It's intentionally dumb, since the main purpose of this is to avoid people calling `resetwarnings` in their variant of the fixture, as that clears *all* filters including those we don't control. Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
-rw-r--r--NEWS4
-rw-r--r--fixtures/__init__.py2
-rw-r--r--fixtures/_fixtures/__init__.py4
-rw-r--r--fixtures/_fixtures/warnings.py51
-rw-r--r--fixtures/tests/_fixtures/test_warnings.py59
5 files changed, 116 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 499d047..1d4b23a 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,10 @@ NEXT
* Drop support for Python 3.6 (EOL)
(Stephen Finucane)
+* Add a new ``WarningsFilter`` filter, allowing users to filter warnings as
+ part of their tests, before restoring said filters.
+ (Stephen Finucane)
+
4.0.1
~~~~~
diff --git a/fixtures/__init__.py b/fixtures/__init__.py
index a47f9e8..a4b9808 100644
--- a/fixtures/__init__.py
+++ b/fixtures/__init__.py
@@ -73,6 +73,7 @@ __all__ = [
'Timeout',
'TimeoutException',
'WarningsCapture',
+ 'WarningsFilter',
'__version__',
'version',
]
@@ -110,6 +111,7 @@ from fixtures._fixtures import (
Timeout,
TimeoutException,
WarningsCapture,
+ WarningsFilter,
)
from fixtures.testcase import TestWithFixtures
diff --git a/fixtures/_fixtures/__init__.py b/fixtures/_fixtures/__init__.py
index b60a6fb..a1bca03 100644
--- a/fixtures/_fixtures/__init__.py
+++ b/fixtures/_fixtures/__init__.py
@@ -40,7 +40,8 @@ __all__ = [
'Timeout',
'TimeoutException',
'WarningsCapture',
- ]
+ 'WarningsFilter',
+]
from fixtures._fixtures.environ import (
@@ -83,4 +84,5 @@ from fixtures._fixtures.timeout import (
)
from fixtures._fixtures.warnings import (
WarningsCapture,
+ WarningsFilter,
)
diff --git a/fixtures/_fixtures/warnings.py b/fixtures/_fixtures/warnings.py
index 8184403..da13c25 100644
--- a/fixtures/_fixtures/warnings.py
+++ b/fixtures/_fixtures/warnings.py
@@ -11,10 +11,9 @@
# license you chose for the specific language governing permissions and
# limitations under that license.
-from __future__ import absolute_import
-
__all__ = [
'WarningsCapture',
+ 'WarningsFilter',
]
import warnings
@@ -38,3 +37,51 @@ class WarningsCapture(fixtures.Fixture):
patch = fixtures.MonkeyPatch("warnings.showwarning", self._showwarning)
self.useFixture(patch)
self.captures = []
+
+
+class WarningsFilter(fixtures.Fixture):
+ """Configure warnings filters.
+
+ While ``WarningsFilter`` is active, warnings will be filtered per
+ configuration.
+ """
+
+ def __init__(self, filters=None):
+ """Create a WarningsFilter fixture.
+
+ :param filters: An optional list of dictionaries with arguments
+ corresponding to the arguments to
+ :py:func:`warnings.filterwarnings`. For example::
+
+ [
+ {
+ 'action': 'ignore',
+ 'message': 'foo',
+ 'category': DeprecationWarning,
+ },
+ ]
+
+ Order is important: entries closer to the front of the list
+ override entries later in the list, if both match a particular
+ warning.
+
+ Alternatively, you can configure warnings within the context of the
+ fixture.
+
+ See `the Python documentation`__ for more information.
+
+ __: https://docs.python.org/3/library/warnings.html#the-warnings-filter
+ """
+ super().__init__()
+ self.filters = filters or []
+
+ def _setUp(self):
+ self._original_warning_filters = warnings.filters[:]
+
+ for filt in self.filters:
+ warnings.filterwarnings(**filt)
+
+ self.addCleanup(self._reset_warning_filters)
+
+ def _reset_warning_filters(self):
+ warnings.filters[:] = self._original_warning_filters
diff --git a/fixtures/tests/_fixtures/test_warnings.py b/fixtures/tests/_fixtures/test_warnings.py
index c9e68bf..4b037be 100644
--- a/fixtures/tests/_fixtures/test_warnings.py
+++ b/fixtures/tests/_fixtures/test_warnings.py
@@ -18,11 +18,12 @@ import testtools
import fixtures
-class TestWarnings(testtools.TestCase, fixtures.TestWithFixtures):
+class TestWarningsCapture(testtools.TestCase, fixtures.TestWithFixtures):
def test_capture_reuse(self):
# DeprecationWarnings are hidden by default in Python 3.2+, enable them
# https://docs.python.org/3/library/warnings.html#default-warning-filter
+ self.useFixture(fixtures.WarningsFilter())
warnings.simplefilter("always")
w = fixtures.WarningsCapture()
@@ -35,6 +36,7 @@ class TestWarnings(testtools.TestCase, fixtures.TestWithFixtures):
def test_capture_message(self):
# DeprecationWarnings are hidden by default in Python 3.2+, enable them
# https://docs.python.org/3/library/warnings.html#default-warning-filter
+ self.useFixture(fixtures.WarningsFilter())
warnings.simplefilter("always")
w = self.useFixture(fixtures.WarningsCapture())
@@ -45,6 +47,7 @@ class TestWarnings(testtools.TestCase, fixtures.TestWithFixtures):
def test_capture_category(self):
# DeprecationWarnings are hidden by default in Python 3.2+, enable them
# https://docs.python.org/3/library/warnings.html#default-warning-filter
+ self.useFixture(fixtures.WarningsFilter())
warnings.simplefilter("always")
w = self.useFixture(fixtures.WarningsCapture())
@@ -59,3 +62,57 @@ class TestWarnings(testtools.TestCase, fixtures.TestWithFixtures):
for i, category in enumerate(categories):
c = w.captures[i]
self.assertEqual(category, c.category)
+
+
+class TestWarningsFilter(testtools.TestCase, fixtures.TestWithFixtures):
+
+ def test_filter(self):
+ fixture = fixtures.WarningsFilter(
+ [
+ {
+ 'action': 'ignore',
+ 'category': DeprecationWarning,
+ },
+ {
+ 'action': 'once',
+ 'category': UserWarning,
+ },
+ ],
+ )
+ self.useFixture(fixture)
+ with warnings.catch_warnings(record=True) as w:
+ warnings.warn('deprecated', DeprecationWarning)
+ warnings.warn('user', UserWarning)
+
+ # only the user warning should be present, and it should only have been
+ # raised once
+ self.assertEqual(1, len(w))
+
+ def test_filters_restored(self):
+
+ class CustomWarning(Warning):
+ pass
+
+ fixture = fixtures.WarningsFilter(
+ [
+ {
+ 'action': 'once',
+ 'category': CustomWarning,
+ },
+ ],
+ )
+
+ # we copy the filter values rather than a reference to the containing
+ # list since that can change
+ old_filters = warnings.filters[:]
+
+ # NOTE: we intentionally do not use 'self.useFixture' since we want to
+ # teardown the fixture manually here before we exit this test method
+ with fixture:
+ new_filters = warnings.filters[:]
+ self.assertEqual(len(old_filters) + 1, len(new_filters))
+ self.assertNotEqual(old_filters, new_filters)
+
+ new_filters = warnings.filters[:]
+ self.assertEqual(len(old_filters), len(new_filters))
+ self.assertEqual(old_filters, new_filters)