X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Futils%2Fversions.py;fp=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Futils%2Fversions.py;h=521004c41495525ffe08b2b663bea36aae181fa1;hb=7409dfb144cf2a06210400134d822a1393462b1f;hp=0000000000000000000000000000000000000000;hpb=9e65649dfff8f00dc0a0ef6b10d020ae0e2255ba;p=multicloud%2Fazure.git diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py new file mode 100644 index 0000000..521004c --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py @@ -0,0 +1,163 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +""" +Verion string utilities. +""" + +import re + + +_INF = float('inf') + +_NULL = (), _INF + +_DIGITS_RE = re.compile(r'^\d+$') + +_PREFIXES = { + 'dev': 0.0001, + 'alpha': 0.001, + 'beta': 0.01, + 'rc': 0.1 +} + + +class VersionString(unicode): + """ + Version string that can be compared, sorted, made unique in a set, and used as a unique dict + key. + + The primary part of the string is one or more dot-separated natural numbers. Trailing zeroes + are treated as redundant, e.g. "1.0.0" == "1.0" == "1". + + An optional qualifier can be added after a "-". The qualifier can be a natural number or a + specially treated prefixed natural number, e.g. "1.1-beta1" > "1.1-alpha2". The case of the + prefix is ignored. + + Numeric qualifiers will always be greater than prefixed integer qualifiers, e.g. "1.1-1" > + "1.1-beta1". + + Versions without a qualifier will always be greater than their equivalents with a qualifier, + e.g. e.g. "1.1" > "1.1-1". + + Any value that does not conform to this format will be treated as a zero version, which would + be lesser than any non-zero version. + + For efficient list sorts use the ``key`` property, e.g.:: + + sorted(versions, key=lambda x: x.key) + """ + + NULL = None # initialized below + + def __init__(self, value=None): + if value is not None: + super(VersionString, self).__init__(value) + self.key = parse_version_string(self) + + def __eq__(self, version): + if not isinstance(version, VersionString): + version = VersionString(version) + return self.key == version.key + + def __lt__(self, version): + if not isinstance(version, VersionString): + version = VersionString(version) + return self.key < version.key + + def __hash__(self): + return self.key.__hash__() + + +def parse_version_string(version): # pylint: disable=too-many-branches + """ + Parses a version string. + + :param version: version string + :returns: primary tuple and qualifier float + :rtype: ((:obj:`int`), :obj:`float`) + """ + + if version is None: + return _NULL + version = unicode(version) + + # Split to primary and qualifier on '-' + split = version.split('-', 1) + if len(split) == 2: + primary, qualifier = split + else: + primary = split[0] + qualifier = None + + # Parse primary + split = primary.split('.') + primary = [] + for element in split: + if _DIGITS_RE.match(element) is None: + # Invalid version string + return _NULL + try: + element = int(element) + except ValueError: + # Invalid version string + return _NULL + primary.append(element) + + # Remove redundant zeros + for element in reversed(primary): + if element == 0: + primary.pop() + else: + break + primary = tuple(primary) + + # Parse qualifier + if qualifier is not None: + if _DIGITS_RE.match(qualifier) is not None: + # Integer qualifier + try: + qualifier = float(int(qualifier)) + except ValueError: + # Invalid version string + return _NULL + else: + # Prefixed integer qualifier + value = None + qualifier = qualifier.lower() + for prefix, factor in _PREFIXES.iteritems(): + if qualifier.startswith(prefix): + value = qualifier[len(prefix):] + if _DIGITS_RE.match(value) is None: + # Invalid version string + return _NULL + try: + value = float(int(value)) * factor + except ValueError: + # Invalid version string + return _NULL + break + if value is None: + # Invalid version string + return _NULL + qualifier = value + else: + # Version strings with no qualifiers are higher + qualifier = _INF + + return primary, qualifier + + +VersionString.NULL = VersionString()