Provide a normal method for deprecation warnings.
The deprecated decorator is very useful in situations where you have a complete function that you want to deprecate. However it doesn't let me simply trigger a deprecation warning in a code path that i know is deprecated but doesn't exist in a function. Create a standard deprecated warning function that has almost the same signature (what becomes required) and produces the same warning and fatal_deprecations behaviour for situations where a deprecator is not useful. For example: if CONF.deprecated_value: versionutils.deprecated_warning('deprecated_value', as_of=versionutils.OCATA) # do stuff Change-Id: Ifd83cd74ac397e7bc6def11a38b46de5dff2acfa Closes-Bug: #1590223
This commit is contained in:
parent
070cc7ae8d
commit
c27cbd8dd8
@ -25,20 +25,19 @@ from oslo_log import versionutils
|
||||
class DeprecatedTestCase(test_base.BaseTestCase):
|
||||
def assert_deprecated(self, mock_reporter, no_removal=False,
|
||||
**expected_details):
|
||||
decorator = versionutils.deprecated
|
||||
if 'in_favor_of' in expected_details:
|
||||
if no_removal is False:
|
||||
expected_msg = decorator._deprecated_msg_with_alternative
|
||||
expected_msg = versionutils._deprecated_msg_with_alternative
|
||||
else:
|
||||
expected_msg = getattr(
|
||||
decorator,
|
||||
versionutils,
|
||||
'_deprecated_msg_with_alternative_no_removal')
|
||||
else:
|
||||
if no_removal is False:
|
||||
expected_msg = decorator._deprecated_msg_no_alternative
|
||||
expected_msg = versionutils._deprecated_msg_no_alternative
|
||||
else:
|
||||
expected_msg = getattr(
|
||||
decorator,
|
||||
versionutils,
|
||||
'_deprecated_msg_with_no_alternative_no_removal')
|
||||
# The first argument is the logger, and we don't care about
|
||||
# that, so ignore it with ANY.
|
||||
@ -356,3 +355,17 @@ class DeprecatedTestCase(test_base.BaseTestCase):
|
||||
what='OutdatedClass()',
|
||||
as_of='Newton',
|
||||
remove_in='P')
|
||||
|
||||
@mock.patch('oslo_log.versionutils.report_deprecated_feature')
|
||||
def test_deprecated_message(self, mock_reporter):
|
||||
|
||||
versionutils.deprecation_warning('outdated_stuff',
|
||||
as_of=versionutils.deprecated.KILO,
|
||||
in_favor_of='different_stuff',
|
||||
remove_in=+2)
|
||||
|
||||
self.assert_deprecated(mock_reporter,
|
||||
what='outdated_stuff',
|
||||
in_favor_of='different_stuff',
|
||||
as_of='Kilo',
|
||||
remove_in='Mitaka')
|
||||
|
@ -39,6 +39,38 @@ deprecated_opts = [
|
||||
]
|
||||
|
||||
|
||||
_deprecated_msg_with_alternative = _(
|
||||
'%(what)s is deprecated as of %(as_of)s in favor of '
|
||||
'%(in_favor_of)s and may be removed in %(remove_in)s.')
|
||||
|
||||
_deprecated_msg_no_alternative = _(
|
||||
'%(what)s is deprecated as of %(as_of)s and may be '
|
||||
'removed in %(remove_in)s. It will not be superseded.')
|
||||
|
||||
_deprecated_msg_with_alternative_no_removal = _(
|
||||
'%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.')
|
||||
|
||||
_deprecated_msg_with_no_alternative_no_removal = _(
|
||||
'%(what)s is deprecated as of %(as_of)s. It will not be superseded.')
|
||||
|
||||
|
||||
_RELEASES = {
|
||||
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
|
||||
# expected we maintain a gap between Bexar and Folsom in this list.
|
||||
'B': 'Bexar',
|
||||
'F': 'Folsom',
|
||||
'G': 'Grizzly',
|
||||
'H': 'Havana',
|
||||
'I': 'Icehouse',
|
||||
'J': 'Juno',
|
||||
'K': 'Kilo',
|
||||
'L': 'Liberty',
|
||||
'M': 'Mitaka',
|
||||
'N': 'Newton',
|
||||
'O': 'Ocata',
|
||||
}
|
||||
|
||||
|
||||
def register_options():
|
||||
"""Register configuration options used by this library.
|
||||
|
||||
@ -109,36 +141,6 @@ class deprecated(object):
|
||||
NEWTON = 'N'
|
||||
OCATA = 'O'
|
||||
|
||||
_RELEASES = {
|
||||
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
|
||||
# expected we maintain a gap between Bexar and Folsom in this list.
|
||||
'B': 'Bexar',
|
||||
'F': 'Folsom',
|
||||
'G': 'Grizzly',
|
||||
'H': 'Havana',
|
||||
'I': 'Icehouse',
|
||||
'J': 'Juno',
|
||||
'K': 'Kilo',
|
||||
'L': 'Liberty',
|
||||
'M': 'Mitaka',
|
||||
'N': 'Newton',
|
||||
'O': 'Ocata',
|
||||
}
|
||||
|
||||
_deprecated_msg_with_alternative = _(
|
||||
'%(what)s is deprecated as of %(as_of)s in favor of '
|
||||
'%(in_favor_of)s and may be removed in %(remove_in)s.')
|
||||
|
||||
_deprecated_msg_no_alternative = _(
|
||||
'%(what)s is deprecated as of %(as_of)s and may be '
|
||||
'removed in %(remove_in)s. It will not be superseded.')
|
||||
|
||||
_deprecated_msg_with_alternative_no_removal = _(
|
||||
'%(what)s is deprecated as of %(as_of)s in favor of %(in_favor_of)s.')
|
||||
|
||||
_deprecated_msg_with_no_alternative_no_removal = _(
|
||||
'%(what)s is deprecated as of %(as_of)s. It will not be superseded.')
|
||||
|
||||
def __init__(self, as_of, in_favor_of=None, remove_in=2, what=None):
|
||||
"""Initialize decorator
|
||||
|
||||
@ -157,15 +159,18 @@ class deprecated(object):
|
||||
self.what = what
|
||||
|
||||
def __call__(self, func_or_cls):
|
||||
if not self.what:
|
||||
self.what = func_or_cls.__name__ + '()'
|
||||
msg, details = self._build_message()
|
||||
report_deprecated = functools.partial(
|
||||
deprecation_warning,
|
||||
what=self.what or func_or_cls.__name__ + '()',
|
||||
as_of=self.as_of,
|
||||
in_favor_of=self.in_favor_of,
|
||||
remove_in=self.remove_in)
|
||||
|
||||
if inspect.isfunction(func_or_cls):
|
||||
|
||||
@six.wraps(func_or_cls)
|
||||
def wrapped(*args, **kwargs):
|
||||
report_deprecated_feature(LOG, msg, details)
|
||||
report_deprecated()
|
||||
return func_or_cls(*args, **kwargs)
|
||||
return wrapped
|
||||
elif inspect.isclass(func_or_cls):
|
||||
@ -178,7 +183,7 @@ class deprecated(object):
|
||||
@functools.wraps(orig_init, assigned=('__name__', '__doc__'))
|
||||
def new_init(self, *args, **kwargs):
|
||||
if self.__class__ in _DEPRECATED_EXCEPTIONS:
|
||||
report_deprecated_feature(LOG, msg, details)
|
||||
report_deprecated()
|
||||
orig_init(self, *args, **kwargs)
|
||||
func_or_cls.__init__ = new_init
|
||||
_DEPRECATED_EXCEPTIONS.add(func_or_cls)
|
||||
@ -197,7 +202,7 @@ class deprecated(object):
|
||||
class ExceptionMeta(type):
|
||||
def __subclasscheck__(self, subclass):
|
||||
if self in _DEPRECATED_EXCEPTIONS:
|
||||
report_deprecated_feature(LOG, msg, details)
|
||||
report_deprecated()
|
||||
return super(ExceptionMeta,
|
||||
self).__subclasscheck__(subclass)
|
||||
func_or_cls = six.add_metaclass(ExceptionMeta)(func_or_cls)
|
||||
@ -208,40 +213,52 @@ class deprecated(object):
|
||||
raise TypeError('deprecated can be used only with functions or '
|
||||
'classes')
|
||||
|
||||
def _get_safe_to_remove_release(self, release):
|
||||
# TODO(dstanek): this method will have to be reimplemented once
|
||||
# when we get to the X release because once we get to the Y
|
||||
# release, what is Y+2?
|
||||
remove_in = self.remove_in
|
||||
if remove_in is None:
|
||||
remove_in = 0
|
||||
new_release = chr(ord(release) + remove_in)
|
||||
if new_release in self._RELEASES:
|
||||
return self._RELEASES[new_release]
|
||||
else:
|
||||
return new_release
|
||||
|
||||
def _build_message(self):
|
||||
details = dict(what=self.what,
|
||||
as_of=self._RELEASES[self.as_of],
|
||||
remove_in=self._get_safe_to_remove_release(self.as_of))
|
||||
def _get_safe_to_remove_release(release, remove_in):
|
||||
# TODO(dstanek): this method will have to be reimplemented once
|
||||
# when we get to the X release because once we get to the Y
|
||||
# release, what is Y+2?
|
||||
if remove_in is None:
|
||||
remove_in = 0
|
||||
new_release = chr(ord(release) + remove_in)
|
||||
if new_release in _RELEASES:
|
||||
return _RELEASES[new_release]
|
||||
else:
|
||||
return new_release
|
||||
|
||||
if self.in_favor_of:
|
||||
details['in_favor_of'] = self.in_favor_of
|
||||
if self.remove_in is not None and self.remove_in > 0:
|
||||
msg = self._deprecated_msg_with_alternative
|
||||
else:
|
||||
# There are no plans to remove this function, but it is
|
||||
# now deprecated.
|
||||
msg = self._deprecated_msg_with_alternative_no_removal
|
||||
|
||||
def deprecation_warning(what, as_of, in_favor_of=None,
|
||||
remove_in=2, logger=LOG):
|
||||
"""Warn about the deprecation of a feature.
|
||||
|
||||
:param what: name of the thing being deprecated.
|
||||
:param as_of: the release deprecating the callable.
|
||||
:param in_favor_of: the replacement for the callable (optional)
|
||||
:param remove_in: an integer specifying how many releases to wait
|
||||
before removing (default: 2)
|
||||
:param logger: the logging object to use for reporting (optional).
|
||||
"""
|
||||
details = dict(what=what,
|
||||
as_of=_RELEASES[as_of],
|
||||
remove_in=_get_safe_to_remove_release(as_of, remove_in))
|
||||
|
||||
if in_favor_of:
|
||||
details['in_favor_of'] = in_favor_of
|
||||
if remove_in is not None and remove_in > 0:
|
||||
msg = _deprecated_msg_with_alternative
|
||||
else:
|
||||
if self.remove_in is not None and self.remove_in > 0:
|
||||
msg = self._deprecated_msg_no_alternative
|
||||
else:
|
||||
# There are no plans to remove this function, but it is
|
||||
# now deprecated.
|
||||
msg = self._deprecated_msg_with_no_alternative_no_removal
|
||||
return msg, details
|
||||
# There are no plans to remove this function, but it is
|
||||
# now deprecated.
|
||||
msg = _deprecated_msg_with_alternative_no_removal
|
||||
else:
|
||||
if remove_in is not None and remove_in > 0:
|
||||
msg = _deprecated_msg_no_alternative
|
||||
else:
|
||||
# There are no plans to remove this function, but it is
|
||||
# now deprecated.
|
||||
msg = _deprecated_msg_with_no_alternative_no_removal
|
||||
|
||||
report_deprecated_feature(logger, msg, details)
|
||||
|
||||
|
||||
# Track the messages we have sent already. See
|
||||
|
Loading…
x
Reference in New Issue
Block a user