128 lines
3.8 KiB
Python
128 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2015 Mirantis, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
from packetary.objects.base import ComparableObject
|
|
|
|
|
|
class PackageVersion(ComparableObject):
|
|
"""The Package version."""
|
|
|
|
__slots__ = ["epoch", "version", "release"]
|
|
|
|
def __init__(self, epoch, version, release):
|
|
self.epoch = int(epoch)
|
|
self.version = tuple(version)
|
|
self.release = tuple(release)
|
|
|
|
@classmethod
|
|
def from_string(cls, text):
|
|
"""Constructs from string.
|
|
|
|
:param text: the version in format '[{epoch-}]-{version}-{release}'
|
|
"""
|
|
components = text.split("-")
|
|
if len(components) > 2:
|
|
epoch = components[0]
|
|
components = components[1:]
|
|
else:
|
|
epoch = 0
|
|
return cls(epoch, components[0].split("."), components[1].split("."))
|
|
|
|
def cmp(self, other):
|
|
if not isinstance(other, PackageVersion):
|
|
other = PackageVersion.from_string(str(other))
|
|
|
|
if not isinstance(other, PackageVersion):
|
|
raise TypeError
|
|
if self.epoch < other.epoch:
|
|
return -1
|
|
if self.epoch > other.epoch:
|
|
return 1
|
|
|
|
res = self._cmp_version_part(self.version, other.version)
|
|
if res != 0:
|
|
return res
|
|
return self._cmp_version_part(self.release, other.release)
|
|
|
|
def __eq__(self, other):
|
|
if other is self:
|
|
return True
|
|
return self.cmp(other) == 0
|
|
|
|
def __str__(self):
|
|
return "{0}-{1}-{2}".format(
|
|
self.epoch,
|
|
".".join(str(x) for x in self.version),
|
|
".".join(str(x) for x in self.release)
|
|
)
|
|
|
|
@classmethod
|
|
def _order(cls, x):
|
|
"""Return an integer value for character x"""
|
|
if x.isdigit():
|
|
return int(x) + 1
|
|
if x.isalpha():
|
|
return ord(x)
|
|
return ord(x) + 256
|
|
|
|
@classmethod
|
|
def _cmp_version_string(cls, version1, version2):
|
|
"""Compares two versions as string."""
|
|
la = [cls._order(x) for x in version1]
|
|
lb = [cls._order(x) for x in version2]
|
|
while la or lb:
|
|
a = 0
|
|
b = 0
|
|
if la:
|
|
a = la.pop(0)
|
|
if lb:
|
|
b = lb.pop(0)
|
|
if a < b:
|
|
return -1
|
|
elif a > b:
|
|
return 1
|
|
return 0
|
|
|
|
@classmethod
|
|
def _cmp_version_part(cls, version1, version2):
|
|
"""Compares two versions."""
|
|
ver1_it = iter(version1)
|
|
ver2_it = iter(version2)
|
|
while True:
|
|
v1 = next(ver1_it, None)
|
|
v2 = next(ver2_it, None)
|
|
|
|
if v1 is None or v2 is None:
|
|
if v1 is not None:
|
|
return 1
|
|
if v2 is not None:
|
|
return -1
|
|
return 0
|
|
|
|
if v1.isdigit() and v2.isdigit():
|
|
a = int(v1)
|
|
b = int(v2)
|
|
if a < b:
|
|
return -1
|
|
if a > b:
|
|
return 1
|
|
else:
|
|
r = cls._cmp_version_string(v1, v2)
|
|
if r != 0:
|
|
return r
|