Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecations fixture #19

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ AUTHORS
ChangeLog
.eggs
build
.tox
12 changes: 11 additions & 1 deletion README
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,16 @@ in test failure descriptions. Very useful in combination with MonkeyPatch.
... pass
>>> fixture.cleanUp()

Deprecations
++++++++++++

Prevent code under test from calling deprecated functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to import warnings here.

You can make this a little cleaner by writing the test using with (we no longer support 2.5)

>>> with fixture:
...    warnings.warn('stop using me', DeprecationWarning)
Exception...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imported warnings and now local tox -e py27 works. Also switched to using context for the setUp/cleanUp.

>>> fixture = fixtures.Deprecations('my_module')
>>> fixture.setUp()
>>> warnings.warn('stop using me', DeprecationWarning) # will raise
>>> fixture.cleanUp()

EnvironmentVariable
+++++++++++++++++++

Expand Down Expand Up @@ -385,7 +395,7 @@ MockPatchMultiple

Adapts ``mock.patch.multiple`` to be used as a Fixture.

>>> fixture = fixtures.MockPatch('subprocess.Popen', returncode=3)
>>> fixture = fixtures.MockPatchMultiple('subprocess.Popen', returncode=3)

MonkeyPatch
+++++++++++
Expand Down
2 changes: 2 additions & 0 deletions fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

__all__ = [
'ByteStream',
'Deprecations',
'DetailStream',
'EnvironmentVariable',
'EnvironmentVariableFixture',
Expand Down Expand Up @@ -86,6 +87,7 @@
)
from fixtures._fixtures import (
ByteStream,
Deprecations,
DetailStream,
EnvironmentVariable,
EnvironmentVariableFixture,
Expand Down
4 changes: 4 additions & 0 deletions fixtures/_fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

__all__ = [
'ByteStream',
'Deprecations',
'DetailStream',
'EnvironmentVariable',
'EnvironmentVariableFixture',
Expand All @@ -43,6 +44,9 @@
]


from fixtures._fixtures.deprecations import (
Deprecations,
)
from fixtures._fixtures.environ import (
EnvironmentVariable,
EnvironmentVariableFixture,
Expand Down
84 changes: 84 additions & 0 deletions fixtures/_fixtures/deprecations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright 2015 IBM Corp.
#
# 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.

from __future__ import absolute_import

import contextlib
import re
import warnings # conflicts with the local warnings module so absolute_import

import fixtures


class Deprecations(fixtures.Fixture):
"""Prevent calls to deprecated function.

This fixture can be added to a testcase to ensure that the code under test
isn't calling deprecated function. It sets Python's `warnings` module for
the module under test to "error" so that DeprecationWarning will be
raised.

You might want your application to not use any deprecated function.
Deprecated function is going to be removed and sometimes is being removed
because it's buggy and you shouldn't be using it.

It can be difficult to tell just through code reviews that new code is
calling deprecated function. This fixture can be used to protect you from
developers proposing changes that use deprecated function.

It can also be useful to be able to test if your application is still using
some function that's been newly deprecated.

:param str module: The name of the module. Deprecated function called from
this module will be errors.

"""

def __init__(self, module):
super(Deprecations, self).__init__()
self._module = module

def _setUp(self):
module_regex = '^%s' % re.escape(self._module + '.')
warnings.filterwarnings('error', category=DeprecationWarning,
module=module_regex)
self.addCleanup(warnings.resetwarnings)

def expect_deprecations(self):
"""Indicate that this test expects to call deprecated function.

If you've got a test that you expect to call deprecated function and
you don't want it to fail call this at the start of the test.

"""
warnings.resetwarnings()

@contextlib.contextmanager
def expect_deprecations_here(self):
"""This section of code expects to call deprecated function.

If you've got a test that part of it is testing deprecated function
then wrap the part in this context manager::

with self.deprecations.expect_deprecations_here():
call_deprecated_function()

"""
warnings.resetwarnings()

yield

module_regex = '^%s' % re.escape(self._module + '.')
warnings.filterwarnings('error', category=DeprecationWarning,
module=module_regex)
1 change: 1 addition & 0 deletions fixtures/tests/_fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

def load_tests(loader, standard_tests, pattern):
test_modules = [
'deprecations',
'environ',
'logger',
'mockpatch',
Expand Down
59 changes: 59 additions & 0 deletions fixtures/tests/_fixtures/test_deprecations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2015 IBM Corp.
#
# 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 testtools

from fixtures import Deprecations


MODULE = 'fixtures'


class TestDeprecations(testtools.TestCase):
def test_enabled_raises(self):
# When the Deprecations fixture is active, calling deprecated function
# is an error.
self.useFixture(Deprecations(MODULE))
self.assertRaises(
DeprecationWarning,
lambda: warnings.warn('message ignored', DeprecationWarning))

def test_expect_deprecations(self):
# When expect_deprecations in a test, deprecations are not an error.
deprecations = self.useFixture(Deprecations(MODULE))
deprecations.expect_deprecations()
warnings.warn('message ignored', DeprecationWarning)

def test_expect_deprecations_here(self):
# While in the expect_deprecations_here() context, deprecations are not
# errors, and afterwards deprecations are errors.
deprecations = self.useFixture(Deprecations(MODULE))
with deprecations.expect_deprecations_here():
warnings.warn('message ignored', DeprecationWarning)
self.assertRaises(
DeprecationWarning,
lambda: warnings.warn('message ignored', DeprecationWarning))

def test_other_module(self):
# When the Deprecations fixture is active, deprecations from other
# modules are ignored.
self.useFixture(Deprecations('different_module'))
warnings.warn('message ignored', DeprecationWarning)

def test_null_case(self):
# When the Deprecations fixture isn't used then deprecations are not
# errors. This shows that python works as expected.
warnings.warn('message ignored', DeprecationWarning)