Move versionutils into place and remove deprecation tools
We only want the is_compatible() function in oslo.utils, so remove everything else from the module as we move it into place. Change-Id: Id63c7584bb3b6951897ca58dd0289b09b5d12f09
This commit is contained in:
parent
a18b051144
commit
528ba92551
@ -1,262 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Helpers for comparing version strings.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import copy
|
|
||||||
import functools
|
|
||||||
import inspect
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
import pkg_resources
|
|
||||||
import six
|
|
||||||
|
|
||||||
from openstack.common._i18n import _
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
CONF = cfg.CONF
|
|
||||||
|
|
||||||
|
|
||||||
deprecated_opts = [
|
|
||||||
cfg.BoolOpt('fatal_deprecations',
|
|
||||||
default=False,
|
|
||||||
help='Enables or disables fatal status of deprecations.'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
|
||||||
"""Entry point for oslo.config-generator.
|
|
||||||
"""
|
|
||||||
return [(None, copy.deepcopy(deprecated_opts))]
|
|
||||||
|
|
||||||
|
|
||||||
class deprecated(object):
|
|
||||||
"""A decorator to mark callables as deprecated.
|
|
||||||
|
|
||||||
This decorator logs a deprecation message when the callable it decorates is
|
|
||||||
used. The message will include the release where the callable was
|
|
||||||
deprecated, the release where it may be removed and possibly an optional
|
|
||||||
replacement.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
1. Specifying the required deprecated release
|
|
||||||
|
|
||||||
>>> @deprecated(as_of=deprecated.ICEHOUSE)
|
|
||||||
... def a(): pass
|
|
||||||
|
|
||||||
2. Specifying a replacement:
|
|
||||||
|
|
||||||
>>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()')
|
|
||||||
... def b(): pass
|
|
||||||
|
|
||||||
3. Specifying the release where the functionality may be removed:
|
|
||||||
|
|
||||||
>>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=+1)
|
|
||||||
... def c(): pass
|
|
||||||
|
|
||||||
4. Specifying the deprecated functionality will not be removed:
|
|
||||||
>>> @deprecated(as_of=deprecated.ICEHOUSE, remove_in=0)
|
|
||||||
... def d(): pass
|
|
||||||
|
|
||||||
5. Specifying a replacement, deprecated functionality will not be removed:
|
|
||||||
>>> @deprecated(as_of=deprecated.ICEHOUSE, in_favor_of='f()', remove_in=0)
|
|
||||||
... def e(): pass
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# NOTE(morganfainberg): Bexar is used for unit test purposes, it is
|
|
||||||
# expected we maintain a gap between Bexar and Folsom in this list.
|
|
||||||
BEXAR = 'B'
|
|
||||||
FOLSOM = 'F'
|
|
||||||
GRIZZLY = 'G'
|
|
||||||
HAVANA = 'H'
|
|
||||||
ICEHOUSE = 'I'
|
|
||||||
JUNO = 'J'
|
|
||||||
KILO = 'K'
|
|
||||||
LIBERTY = 'L'
|
|
||||||
|
|
||||||
_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',
|
|
||||||
}
|
|
||||||
|
|
||||||
_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
|
|
||||||
|
|
||||||
:param as_of: the release deprecating the callable. Constants
|
|
||||||
are define in this class for convenience.
|
|
||||||
: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 what: name of the thing being deprecated (default: the
|
|
||||||
callable's name)
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.as_of = as_of
|
|
||||||
self.in_favor_of = in_favor_of
|
|
||||||
self.remove_in = remove_in
|
|
||||||
self.what = what
|
|
||||||
|
|
||||||
def __call__(self, func_or_cls):
|
|
||||||
if not self.what:
|
|
||||||
self.what = func_or_cls.__name__ + '()'
|
|
||||||
msg, details = self._build_message()
|
|
||||||
|
|
||||||
if inspect.isfunction(func_or_cls):
|
|
||||||
|
|
||||||
@six.wraps(func_or_cls)
|
|
||||||
def wrapped(*args, **kwargs):
|
|
||||||
report_deprecated_feature(LOG, msg, details)
|
|
||||||
return func_or_cls(*args, **kwargs)
|
|
||||||
return wrapped
|
|
||||||
elif inspect.isclass(func_or_cls):
|
|
||||||
orig_init = func_or_cls.__init__
|
|
||||||
|
|
||||||
# TODO(tsufiev): change `functools` module to `six` as
|
|
||||||
# soon as six 1.7.4 (with fix for passing `assigned`
|
|
||||||
# argument to underlying `functools.wraps`) is released
|
|
||||||
# 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)
|
|
||||||
orig_init(self, *args, **kwargs)
|
|
||||||
func_or_cls.__init__ = new_init
|
|
||||||
return func_or_cls
|
|
||||||
else:
|
|
||||||
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?
|
|
||||||
new_release = chr(ord(release) + self.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))
|
|
||||||
|
|
||||||
if self.in_favor_of:
|
|
||||||
details['in_favor_of'] = self.in_favor_of
|
|
||||||
if 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
|
|
||||||
else:
|
|
||||||
if 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
|
|
||||||
|
|
||||||
|
|
||||||
def is_compatible(requested_version, current_version, same_major=True):
|
|
||||||
"""Determine whether `requested_version` is satisfied by
|
|
||||||
`current_version`; in other words, `current_version` is >=
|
|
||||||
`requested_version`.
|
|
||||||
|
|
||||||
:param requested_version: version to check for compatibility
|
|
||||||
:param current_version: version to check against
|
|
||||||
:param same_major: if True, the major version must be identical between
|
|
||||||
`requested_version` and `current_version`. This is used when a
|
|
||||||
major-version difference indicates incompatibility between the two
|
|
||||||
versions. Since this is the common-case in practice, the default is
|
|
||||||
True.
|
|
||||||
:returns: True if compatible, False if not
|
|
||||||
"""
|
|
||||||
requested_parts = pkg_resources.parse_version(requested_version)
|
|
||||||
current_parts = pkg_resources.parse_version(current_version)
|
|
||||||
|
|
||||||
if same_major and (requested_parts[0] != current_parts[0]):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return current_parts >= requested_parts
|
|
||||||
|
|
||||||
|
|
||||||
# Track the messages we have sent already. See
|
|
||||||
# report_deprecated_feature().
|
|
||||||
_deprecated_messages_sent = {}
|
|
||||||
|
|
||||||
|
|
||||||
def report_deprecated_feature(logger, msg, *args, **kwargs):
|
|
||||||
"""Call this function when a deprecated feature is used.
|
|
||||||
|
|
||||||
If the system is configured for fatal deprecations then the message
|
|
||||||
is logged at the 'critical' level and :class:`DeprecatedConfig` will
|
|
||||||
be raised.
|
|
||||||
|
|
||||||
Otherwise, the message will be logged (once) at the 'warn' level.
|
|
||||||
|
|
||||||
:raises: :class:`DeprecatedConfig` if the system is configured for
|
|
||||||
fatal deprecations.
|
|
||||||
"""
|
|
||||||
stdmsg = _("Deprecated: %s") % msg
|
|
||||||
CONF.register_opts(deprecated_opts)
|
|
||||||
if CONF.fatal_deprecations:
|
|
||||||
logger.critical(stdmsg, *args, **kwargs)
|
|
||||||
raise DeprecatedConfig(msg=stdmsg)
|
|
||||||
|
|
||||||
# Using a list because a tuple with dict can't be stored in a set.
|
|
||||||
sent_args = _deprecated_messages_sent.setdefault(msg, list())
|
|
||||||
|
|
||||||
if args in sent_args:
|
|
||||||
# Already logged this message, so don't log it again.
|
|
||||||
return
|
|
||||||
|
|
||||||
sent_args.append(args)
|
|
||||||
logger.warn(stdmsg, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class DeprecatedConfig(Exception):
|
|
||||||
message = _("Fatal call to deprecated config: %(msg)s")
|
|
||||||
|
|
||||||
def __init__(self, msg):
|
|
||||||
super(Exception, self).__init__(self.message % dict(msg=msg))
|
|
69
oslo_utils/tests/test_versionutils.py
Normal file
69
oslo_utils/tests/test_versionutils.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Copyright (c) 2013 OpenStack Foundation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 oslotest import base as test_base
|
||||||
|
|
||||||
|
from oslo_utils import versionutils
|
||||||
|
|
||||||
|
|
||||||
|
class IsCompatibleTestCase(test_base.BaseTestCase):
|
||||||
|
def test_same_version(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1', '1'))
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.0'))
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.0'))
|
||||||
|
|
||||||
|
def test_requested_minor_greater(self):
|
||||||
|
self.assertFalse(versionutils.is_compatible('1.1', '1.0'))
|
||||||
|
|
||||||
|
def test_requested_minor_less_than(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.1'))
|
||||||
|
|
||||||
|
def test_requested_patch_greater(self):
|
||||||
|
self.assertFalse(versionutils.is_compatible('1.0.1', '1.0.0'))
|
||||||
|
|
||||||
|
def test_requested_patch_less_than(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.1'))
|
||||||
|
|
||||||
|
def test_requested_patch_not_present_same(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.0.0'))
|
||||||
|
|
||||||
|
def test_requested_patch_not_present_less_than(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.0.1'))
|
||||||
|
|
||||||
|
def test_current_patch_not_present_same(self):
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0'))
|
||||||
|
|
||||||
|
def test_current_patch_not_present_less_than(self):
|
||||||
|
self.assertFalse(versionutils.is_compatible('1.0.1', '1.0'))
|
||||||
|
|
||||||
|
def test_same_major_true(self):
|
||||||
|
"""Even though the current version is 2.0, since `same_major` defaults
|
||||||
|
to `True`, 1.0 is deemed incompatible.
|
||||||
|
"""
|
||||||
|
self.assertFalse(versionutils.is_compatible('2.0', '1.0'))
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.0'))
|
||||||
|
self.assertFalse(versionutils.is_compatible('1.0', '2.0'))
|
||||||
|
|
||||||
|
def test_same_major_false(self):
|
||||||
|
"""With `same_major` set to False, then major version compatibiity
|
||||||
|
rule is not enforced, so a current version of 2.0 is deemed to satisfy
|
||||||
|
a requirement of 1.0.
|
||||||
|
"""
|
||||||
|
self.assertFalse(versionutils.is_compatible('2.0', '1.0',
|
||||||
|
same_major=False))
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '1.0',
|
||||||
|
same_major=False))
|
||||||
|
self.assertTrue(versionutils.is_compatible('1.0', '2.0',
|
||||||
|
same_major=False))
|
48
oslo_utils/versionutils.py
Normal file
48
oslo_utils/versionutils.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Copyright (c) 2013 OpenStack Foundation
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Helpers for comparing version strings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_compatible(requested_version, current_version, same_major=True):
|
||||||
|
"""Determine whether `requested_version` is satisfied by
|
||||||
|
`current_version`; in other words, `current_version` is >=
|
||||||
|
`requested_version`.
|
||||||
|
|
||||||
|
:param requested_version: version to check for compatibility
|
||||||
|
:param current_version: version to check against
|
||||||
|
:param same_major: if True, the major version must be identical between
|
||||||
|
`requested_version` and `current_version`. This is used when a
|
||||||
|
major-version difference indicates incompatibility between the two
|
||||||
|
versions. Since this is the common-case in practice, the default is
|
||||||
|
True.
|
||||||
|
:returns: True if compatible, False if not
|
||||||
|
"""
|
||||||
|
requested_parts = pkg_resources.parse_version(requested_version)
|
||||||
|
current_parts = pkg_resources.parse_version(current_version)
|
||||||
|
|
||||||
|
if same_major and (requested_parts[0] != current_parts[0]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return current_parts >= requested_parts
|
@ -1,284 +0,0 @@
|
|||||||
# Copyright (c) 2013 OpenStack Foundation
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# 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 mock
|
|
||||||
from oslotest import base as test_base
|
|
||||||
from testtools import matchers
|
|
||||||
|
|
||||||
from openstack.common 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
|
|
||||||
else:
|
|
||||||
expected_msg = getattr(
|
|
||||||
decorator,
|
|
||||||
'_deprecated_msg_with_alternative_no_removal')
|
|
||||||
else:
|
|
||||||
if no_removal is False:
|
|
||||||
expected_msg = decorator._deprecated_msg_no_alternative
|
|
||||||
else:
|
|
||||||
expected_msg = getattr(
|
|
||||||
decorator,
|
|
||||||
'_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.
|
|
||||||
mock_reporter.assert_called_with(mock.ANY,
|
|
||||||
expected_msg,
|
|
||||||
expected_details)
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecating_a_function_returns_correct_value(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.ICEHOUSE)
|
|
||||||
def do_outdated_stuff(data):
|
|
||||||
return data
|
|
||||||
|
|
||||||
expected_rv = 'expected return value'
|
|
||||||
retval = do_outdated_stuff(expected_rv)
|
|
||||||
|
|
||||||
self.assertThat(retval, matchers.Equals(expected_rv))
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecating_a_method_returns_correct_value(self, mock_reporter):
|
|
||||||
|
|
||||||
class C(object):
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.ICEHOUSE)
|
|
||||||
def outdated_method(self, *args):
|
|
||||||
return args
|
|
||||||
|
|
||||||
retval = C().outdated_method(1, 'of anything')
|
|
||||||
|
|
||||||
self.assertThat(retval, matchers.Equals((1, 'of anything')))
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_unknown_future_release(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.BEXAR,
|
|
||||||
in_favor_of='different_stuff()')
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
in_favor_of='different_stuff()',
|
|
||||||
as_of='Bexar',
|
|
||||||
remove_in='D')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_known_future_release(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
in_favor_of='different_stuff()')
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
in_favor_of='different_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Icehouse')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_without_replacement(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY)
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Icehouse')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_custom_what(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
what='v2.0 API',
|
|
||||||
in_favor_of='v3 API')
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='v2.0 API',
|
|
||||||
in_favor_of='v3 API',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Icehouse')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_removed_next_release(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
remove_in=1)
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Havana')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_removed_plus_3(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
remove_in=+3)
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Juno')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_removed_zero(self, mock_reporter):
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
remove_in=0)
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
no_removal=True,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
remove_in='Grizzly')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_with_removed_zero_and_alternative(self, mock_reporter):
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.GRIZZLY,
|
|
||||||
in_favor_of='different_stuff()',
|
|
||||||
remove_in=0)
|
|
||||||
def do_outdated_stuff():
|
|
||||||
return
|
|
||||||
|
|
||||||
do_outdated_stuff()
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
no_removal=True,
|
|
||||||
what='do_outdated_stuff()',
|
|
||||||
as_of='Grizzly',
|
|
||||||
in_favor_of='different_stuff()',
|
|
||||||
remove_in='Grizzly')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_class_without_init(self, mock_reporter):
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
|
||||||
remove_in=+1)
|
|
||||||
class OutdatedClass(object):
|
|
||||||
pass
|
|
||||||
obj = OutdatedClass()
|
|
||||||
|
|
||||||
self.assertIsInstance(obj, OutdatedClass)
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='OutdatedClass()',
|
|
||||||
as_of='Juno',
|
|
||||||
remove_in='Kilo')
|
|
||||||
|
|
||||||
@mock.patch('openstack.common.versionutils.report_deprecated_feature')
|
|
||||||
def test_deprecated_class_with_init(self, mock_reporter):
|
|
||||||
mock_arguments = mock.MagicMock()
|
|
||||||
args = (1, 5, 7)
|
|
||||||
kwargs = {'first': 10, 'second': 20}
|
|
||||||
|
|
||||||
@versionutils.deprecated(as_of=versionutils.deprecated.JUNO,
|
|
||||||
remove_in=+1)
|
|
||||||
class OutdatedClass(object):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""It is __init__ method."""
|
|
||||||
mock_arguments.args = args
|
|
||||||
mock_arguments.kwargs = kwargs
|
|
||||||
super(OutdatedClass, self).__init__()
|
|
||||||
obj = OutdatedClass(*args, **kwargs)
|
|
||||||
|
|
||||||
self.assertIsInstance(obj, OutdatedClass)
|
|
||||||
self.assertEqual('__init__', obj.__init__.__name__)
|
|
||||||
self.assertEqual('It is __init__ method.', obj.__init__.__doc__)
|
|
||||||
self.assertEqual(args, mock_arguments.args)
|
|
||||||
self.assertEqual(kwargs, mock_arguments.kwargs)
|
|
||||||
self.assert_deprecated(mock_reporter,
|
|
||||||
what='OutdatedClass()',
|
|
||||||
as_of='Juno',
|
|
||||||
remove_in='Kilo')
|
|
||||||
|
|
||||||
|
|
||||||
class IsCompatibleTestCase(test_base.BaseTestCase):
|
|
||||||
def test_same_version(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1', '1'))
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.0'))
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.0'))
|
|
||||||
|
|
||||||
def test_requested_minor_greater(self):
|
|
||||||
self.assertFalse(versionutils.is_compatible('1.1', '1.0'))
|
|
||||||
|
|
||||||
def test_requested_minor_less_than(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.1'))
|
|
||||||
|
|
||||||
def test_requested_patch_greater(self):
|
|
||||||
self.assertFalse(versionutils.is_compatible('1.0.1', '1.0.0'))
|
|
||||||
|
|
||||||
def test_requested_patch_less_than(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0.1'))
|
|
||||||
|
|
||||||
def test_requested_patch_not_present_same(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.0.0'))
|
|
||||||
|
|
||||||
def test_requested_patch_not_present_less_than(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.0.1'))
|
|
||||||
|
|
||||||
def test_current_patch_not_present_same(self):
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0.0', '1.0'))
|
|
||||||
|
|
||||||
def test_current_patch_not_present_less_than(self):
|
|
||||||
self.assertFalse(versionutils.is_compatible('1.0.1', '1.0'))
|
|
||||||
|
|
||||||
def test_same_major_true(self):
|
|
||||||
"""Even though the current version is 2.0, since `same_major` defaults
|
|
||||||
to `True`, 1.0 is deemed incompatible.
|
|
||||||
"""
|
|
||||||
self.assertFalse(versionutils.is_compatible('2.0', '1.0'))
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.0'))
|
|
||||||
self.assertFalse(versionutils.is_compatible('1.0', '2.0'))
|
|
||||||
|
|
||||||
def test_same_major_false(self):
|
|
||||||
"""With `same_major` set to False, then major version compatibiity
|
|
||||||
rule is not enforced, so a current version of 2.0 is deemed to satisfy
|
|
||||||
a requirement of 1.0.
|
|
||||||
"""
|
|
||||||
self.assertFalse(versionutils.is_compatible('2.0', '1.0',
|
|
||||||
same_major=False))
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '1.0',
|
|
||||||
same_major=False))
|
|
||||||
self.assertTrue(versionutils.is_compatible('1.0', '2.0',
|
|
||||||
same_major=False))
|
|
Loading…
x
Reference in New Issue
Block a user