summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/aria/utils/versions.py163
1 files changed, 163 insertions, 0 deletions
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()