Do not report deprecations in subclasses

Upgrading to mock 1.1.0 somehow exposed the logic error in deprecated()
that was causing subclasses of deprecated exceptions to be reported as
deprecated when they should not be. The problem makes sense because we
were indiscriminately reporting the case in the constructor, and if the
subclass initialized the base class the deprecation reporting function
would be called. Change the logic to only report the deprecation if the
class being instantiated matches one of the deprecated classes.

Add a warning, and skip the test, for catching deprecated exceptions
under Python 3, since this feature does not always work there.

Also explicitly declare the versions of mock supported in the unit
tests, since we need different versions for python 2.6 vs. 2.7 and
later.

Change-Id: Ib60413a0e45901902382c3ae547be0e24df4e50c
This commit is contained in:
Doug Hellmann
2015-07-10 13:05:36 +00:00
parent fafa3e40b8
commit ee112220e6
3 changed files with 27 additions and 4 deletions

View File

@@ -15,7 +15,9 @@
import mock
from oslotest import base as test_base
import six
from testtools import matchers
import unittest2
from oslo_log import versionutils
@@ -246,6 +248,9 @@ class DeprecatedTestCase(test_base.BaseTestCase):
as_of='Juno',
remove_in='Kilo')
@unittest2.skipIf(
six.PY3,
'Deprecated exception detection does not work for Python 3')
@mock.patch('oslo_log.versionutils.report_deprecated_feature')
def test_deprecated_exception(self, mock_log):
@versionutils.deprecated(as_of=versionutils.deprecated.ICEHOUSE,

View File

@@ -77,6 +77,12 @@ class deprecated(object):
... remove_in=None)
... def e(): pass
.. warning::
The hook used to detect when a deprecated exception is being
*caught* does not work under Python 3. Deprecated exceptions
are still logged if they are thrown.
"""
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
@@ -155,11 +161,23 @@ class deprecated(object):
# and added to the oslo-incubator requrements
@functools.wraps(orig_init, assigned=('__name__', '__doc__'))
def new_init(self, *args, **kwargs):
report_deprecated_feature(LOG, msg, details)
if self.__class__ in _DEPRECATED_EXCEPTIONS:
report_deprecated_feature(LOG, msg, details)
orig_init(self, *args, **kwargs)
func_or_cls.__init__ = new_init
_DEPRECATED_EXCEPTIONS.add(func_or_cls)
if issubclass(func_or_cls, Exception):
# NOTE(dhellmann): The subclasscheck is called,
# sometimes, to test whether a class matches the type
# being caught in an exception. This lets us warn
# folks that they are trying to catch an exception
# that has been deprecated. However, under Python 3
# the test for whether one class is a subclass of
# another has been optimized so that the abstract
# check is only invoked in some cases. (See
# PyObject_IsSubclass in cpython/Objects/abstract.c
# for the short-cut.)
class ExceptionMeta(type):
def __subclasscheck__(self, subclass):
if self in _DEPRECATED_EXCEPTIONS:
@@ -168,6 +186,7 @@ class deprecated(object):
self).__subclasscheck__(subclass)
func_or_cls = six.add_metaclass(ExceptionMeta)(func_or_cls)
_DEPRECATED_EXCEPTIONS.add(func_or_cls)
return func_or_cls
else:
raise TypeError('deprecated can be used only with functions or '

View File

@@ -10,6 +10,8 @@ python-subunit>=0.0.18
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
mock>=1.1;python_version!='2.6'
mock==1.0.1;python_version=='2.6'
oslotest>=1.5.1 # Apache-2.0
# when we can require tox>= 1.4, this can go into tox.ini:
@@ -20,6 +22,3 @@ coverage>=3.6
# this is required for the docs build jobs
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
oslosphinx>=2.5.0 # Apache-2.0
# mocking framework
mock>=1.0