From 771c943ad2116193e7bb118c74993c829d93bd71 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 24 Dec 2021 11:24:08 +0000 Subject: [PATCH] Add 'WarningsFixture' This duplicates what exists in nova and various other projects. The important difference between this and what we're doing currently is that it *restores*, rather than reset, the warning filters. There are more various warning filters pre-configured in a typical Python environment, including a few from third-party libraries such as requests [1][2] and urllib3 [3] as well as stdlib [4]. By calling 'warnings.resetwarnings', we *reset* all the warning filters [5]. This is clearly not something we want to do, and resulted in tests puking warnings after the initial test run. [1] https://github.com/psf/requests/blob/v2.26.0/requests/__init__.py#L127 [2] https://github.com/psf/requests/blob/v2.26.0/requests/__init__.py#L152 [3] https://github.com/urllib3/urllib3/blob/1.26.7/src/urllib3/__init__.py#L68-L78 [4] https://docs.python.org/3.8/library/warnings.html#default-warning-filter [5] https://docs.python.org/3.8/library/warnings.html#warnings.resetwarnings Change-Id: Ia2046dc32e3ac270b1dbcf2fe540104c1a8d95d8 Signed-off-by: Stephen Finucane --- keystone/tests/unit/core.py | 14 +----- keystone/tests/unit/ksfixtures/__init__.py | 1 + keystone/tests/unit/ksfixtures/warnings.py | 54 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 keystone/tests/unit/ksfixtures/warnings.py diff --git a/keystone/tests/unit/core.py b/keystone/tests/unit/core.py index 5e93b842f4..3e873dbbaa 100644 --- a/keystone/tests/unit/core.py +++ b/keystone/tests/unit/core.py @@ -11,6 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + import atexit import base64 import contextlib @@ -24,7 +25,6 @@ import shutil import socket import sys import uuid -import warnings import fixtures import flask @@ -36,7 +36,6 @@ from oslo_context import fixture as oslo_ctx_fixture from oslo_log import fixture as log_fixture from oslo_log import log from oslo_utils import timeutils -from sqlalchemy import exc import testtools from testtools import testcase @@ -678,17 +677,8 @@ class BaseTestCase(testtools.TestCase): self.useFixture(fixtures.MockPatchObject(sys, 'exit', side_effect=UnexpectedExit)) self.useFixture(log_fixture.get_logging_handle_error_fixture()) + self.useFixture(ksfixtures.WarningsFixture()) - warnings.filterwarnings('error', category=DeprecationWarning, - module='^keystone\\.') - warnings.filterwarnings( - 'ignore', category=DeprecationWarning, - message=r"Using function/method 'db_version\(\)' is deprecated") - warnings.simplefilter('error', exc.SAWarning) - if hasattr(exc, "RemovedIn20Warning"): - warnings.simplefilter('ignore', exc.RemovedIn20Warning) - - self.addCleanup(warnings.resetwarnings) # Ensure we have an empty threadlocal context at the start of each # test. self.assertIsNone(oslo_context.get_current()) diff --git a/keystone/tests/unit/ksfixtures/__init__.py b/keystone/tests/unit/ksfixtures/__init__.py index 7a92c42cd0..a76e5e5a9c 100644 --- a/keystone/tests/unit/ksfixtures/__init__.py +++ b/keystone/tests/unit/ksfixtures/__init__.py @@ -17,3 +17,4 @@ from keystone.tests.unit.ksfixtures.cache import Cache # noqa from keystone.tests.unit.ksfixtures.jws_key_repository import JWSKeyRepository # noqa from keystone.tests.unit.ksfixtures.key_repository import KeyRepository # noqa from keystone.tests.unit.ksfixtures.policy import Policy # noqa +from keystone.tests.unit.ksfixtures.warnings import WarningsFixture # noqa diff --git a/keystone/tests/unit/ksfixtures/warnings.py b/keystone/tests/unit/ksfixtures/warnings.py new file mode 100644 index 0000000000..11d69567c8 --- /dev/null +++ b/keystone/tests/unit/ksfixtures/warnings.py @@ -0,0 +1,54 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import warnings + +import fixtures +from sqlalchemy import exc as sqla_exc + + +class WarningsFixture(fixtures.Fixture): + """Filters out warnings during test runs.""" + + def setUp(self): + super().setUp() + + self._original_warning_filters = warnings.filters[:] + + # NOTE(stephenfin): Make deprecation warnings only happen once. + # Otherwise this gets kind of crazy given the way that upstream python + # libs use this. + warnings.simplefilter('once', DeprecationWarning) + + warnings.filterwarnings( + 'error', + category=DeprecationWarning, + module='^keystone\\.', + ) + + # TODO(stephenfin): This will be fixed once we drop sqlalchemy-migrate + warnings.filterwarnings( + 'ignore', + category=DeprecationWarning, + message=r"Using function/method 'db_version\(\)' is deprecated", + ) + + # TODO(stephenfin): We should filter on the specific RemovedIn20Warning + # warnings that affect us, so that we can slowly start addressing them + warnings.simplefilter('error', sqla_exc.SAWarning) + if hasattr(sqla_exc, 'RemovedIn20Warning'): + warnings.simplefilter('ignore', sqla_exc.RemovedIn20Warning) + + self.addCleanup(self._reset_warning_filters) + + def _reset_warning_filters(self): + warnings.filters[:] = self._original_warning_filters