Sync versionutils from oslo-incubator
This is needed for comparing client versions from the command line to required versions for CLI tests. Tempest already has the immediate oslo dependencies of gettextutils and log and those are up-to-date enough for what's needed in versionutils, so this is a --nodeps sync. Part of blueprint minversion-check-for-cli-tests Change-Id: I19784499a9bb1135a9c8dc8b26fc56c53489ed22
This commit is contained in:
parent
3f3d845e83
commit
8c5be17cbb
@ -7,6 +7,7 @@ module=lockutils
|
|||||||
module=log
|
module=log
|
||||||
module=importlib
|
module=importlib
|
||||||
module=fixture
|
module=fixture
|
||||||
|
module=versionutils
|
||||||
|
|
||||||
# The base module to hold the copy of openstack.common
|
# The base module to hold the copy of openstack.common
|
||||||
base=tempest
|
base=tempest
|
||||||
|
148
tempest/openstack/common/versionutils.py
Normal file
148
tempest/openstack/common/versionutils.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# 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 functools
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
from tempest.openstack.common.gettextutils import _
|
||||||
|
from tempest.openstack.common import log as logging
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
FOLSOM = 'F'
|
||||||
|
GRIZZLY = 'G'
|
||||||
|
HAVANA = 'H'
|
||||||
|
ICEHOUSE = 'I'
|
||||||
|
|
||||||
|
_RELEASES = {
|
||||||
|
'F': 'Folsom',
|
||||||
|
'G': 'Grizzly',
|
||||||
|
'H': 'Havana',
|
||||||
|
'I': 'Icehouse',
|
||||||
|
}
|
||||||
|
|
||||||
|
_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.')
|
||||||
|
|
||||||
|
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):
|
||||||
|
if not self.what:
|
||||||
|
self.what = func.__name__ + '()'
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
msg, details = self._build_message()
|
||||||
|
LOG.deprecated(msg, details)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
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
|
||||||
|
msg = self._deprecated_msg_with_alternative
|
||||||
|
else:
|
||||||
|
msg = self._deprecated_msg_no_alternative
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user