[RPM] Use yum library for packages comparison
Closes-bug: #1587032 Change-Id: I8c857edb839a441b71864d915ef4a814463ed81b
This commit is contained in:
parent
2bd9a9415d
commit
07079a0b55
|
@ -19,6 +19,7 @@
|
||||||
import copy
|
import copy
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
|
import rpmUtils
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
@ -30,10 +31,10 @@ from packetary.drivers.base import RepositoryDriverBase
|
||||||
from packetary.library.checksum import composite as checksum_composite
|
from packetary.library.checksum import composite as checksum_composite
|
||||||
from packetary.library.streams import GzipDecompress
|
from packetary.library.streams import GzipDecompress
|
||||||
from packetary.library import utils
|
from packetary.library import utils
|
||||||
|
from packetary.objects.base import ComparableObject
|
||||||
from packetary.objects import FileChecksum
|
from packetary.objects import FileChecksum
|
||||||
from packetary.objects import Package
|
from packetary.objects import Package
|
||||||
from packetary.objects import PackageRelation
|
from packetary.objects import PackageRelation
|
||||||
from packetary.objects import PackageVersion
|
|
||||||
from packetary.objects import Repository
|
from packetary.objects import Repository
|
||||||
from packetary.objects import VersionRange
|
from packetary.objects import VersionRange
|
||||||
from packetary.schemas import RPM_REPO_SCHEMA
|
from packetary.schemas import RPM_REPO_SCHEMA
|
||||||
|
@ -86,6 +87,53 @@ class CreaterepoCallBack(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RpmPackageVersion(ComparableObject):
|
||||||
|
"""The Package version."""
|
||||||
|
|
||||||
|
__slots__ = ["epoch", "version", "release"]
|
||||||
|
|
||||||
|
def __init__(self, epoch, version, release=None):
|
||||||
|
self.epoch = epoch
|
||||||
|
self.version = version
|
||||||
|
self.release = release or ''
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, text):
|
||||||
|
"""Constructs from string.
|
||||||
|
|
||||||
|
:param text: the version in format '[{epoch-}]-{version}-{release}'
|
||||||
|
"""
|
||||||
|
(epoch, version, release) = rpmUtils.miscutils.stringToVersion(text)
|
||||||
|
return cls(epoch, version, release)
|
||||||
|
|
||||||
|
def cmp(self, other):
|
||||||
|
if not isinstance(other, RpmPackageVersion):
|
||||||
|
other = RpmPackageVersion.from_string(str(other))
|
||||||
|
|
||||||
|
return rpmUtils.miscutils.compareEVR(
|
||||||
|
(self.epoch, self.version, self.release),
|
||||||
|
(other.epoch, other.version, other.release)
|
||||||
|
)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if other is self:
|
||||||
|
return True
|
||||||
|
return self.cmp(other) == 0
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.release:
|
||||||
|
return "{0}:{1}-{2}".format(
|
||||||
|
self.epoch,
|
||||||
|
self.version,
|
||||||
|
self.release,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return "{0}:{1}".format(
|
||||||
|
self.epoch,
|
||||||
|
self.version,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RpmRepositoryDriver(RepositoryDriverBase):
|
class RpmRepositoryDriver(RepositoryDriverBase):
|
||||||
def get_repository_data_schema(self):
|
def get_repository_data_schema(self):
|
||||||
return RPM_REPO_SCHEMA
|
return RPM_REPO_SCHEMA
|
||||||
|
@ -194,7 +242,7 @@ class RpmRepositoryDriver(RepositoryDriverBase):
|
||||||
return Package(
|
return Package(
|
||||||
repository=repository,
|
repository=repository,
|
||||||
name=hdr["name"],
|
name=hdr["name"],
|
||||||
version=PackageVersion(
|
version=RpmPackageVersion(
|
||||||
hdr['epoch'], hdr['version'], hdr['release']
|
hdr['epoch'], hdr['version'], hdr['release']
|
||||||
),
|
),
|
||||||
filesize=int(hdr['size']),
|
filesize=int(hdr['size']),
|
||||||
|
@ -381,7 +429,7 @@ class RpmRepositoryDriver(RepositoryDriverBase):
|
||||||
return [
|
return [
|
||||||
PackageRelation(
|
PackageRelation(
|
||||||
x[0], VersionRange(
|
x[0], VersionRange(
|
||||||
_OPERATORS_MAPPING[x[1]], x[1] and PackageVersion(*x[2])
|
_OPERATORS_MAPPING[x[1]], x[1] and RpmPackageVersion(*x[2])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for x in relations
|
for x in relations
|
||||||
|
@ -416,11 +464,11 @@ class RpmRepositoryDriver(RepositoryDriverBase):
|
||||||
"""Gets the package version from attributes.
|
"""Gets the package version from attributes.
|
||||||
|
|
||||||
:param attrs: the relation tag attributes
|
:param attrs: the relation tag attributes
|
||||||
:return: the PackageVersion object
|
:return: the RpmPackageVersion object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return PackageVersion(
|
return RpmPackageVersion(
|
||||||
attrs.get("epoch", 0),
|
attrs.get("epoch", "0"),
|
||||||
attrs.get("ver", "0.0"),
|
attrs.get("ver", "0.0"),
|
||||||
attrs.get("rel")
|
attrs.get("rel")
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,6 @@ from packetary.objects.package import FileChecksum
|
||||||
from packetary.objects.package import Package
|
from packetary.objects.package import Package
|
||||||
from packetary.objects.package_relation import PackageRelation
|
from packetary.objects.package_relation import PackageRelation
|
||||||
from packetary.objects.package_relation import VersionRange
|
from packetary.objects.package_relation import VersionRange
|
||||||
from packetary.objects.package_version import PackageVersion
|
|
||||||
from packetary.objects.packages_forest import PackagesForest
|
from packetary.objects.packages_forest import PackagesForest
|
||||||
from packetary.objects.packages_tree import PackagesTree
|
from packetary.objects.packages_tree import PackagesTree
|
||||||
from packetary.objects.repository import Repository
|
from packetary.objects.repository import Repository
|
||||||
|
@ -34,7 +33,6 @@ __all__ = [
|
||||||
"PackageRelation",
|
"PackageRelation",
|
||||||
"PackagesForest",
|
"PackagesForest",
|
||||||
"PackagesTree",
|
"PackagesTree",
|
||||||
"PackageVersion",
|
|
||||||
"Repository",
|
"Repository",
|
||||||
"VersionRange",
|
"VersionRange",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
# -*- 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=None):
|
|
||||||
self.epoch = int(epoch or 0)
|
|
||||||
self.version = tuple(version.split('.'))
|
|
||||||
if release:
|
|
||||||
self.release = tuple(release.split('.'))
|
|
||||||
else:
|
|
||||||
self.release = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_string(cls, text):
|
|
||||||
"""Constructs from string.
|
|
||||||
|
|
||||||
:param text: the version in format '[{epoch-}]-{version}-{release}'
|
|
||||||
"""
|
|
||||||
pos1 = text.find(':')
|
|
||||||
if pos1 != -1:
|
|
||||||
epoch = text[0:pos1]
|
|
||||||
else:
|
|
||||||
epoch = 0
|
|
||||||
pos1 += 1
|
|
||||||
pos2 = text.find('-', pos1)
|
|
||||||
if pos2 != -1:
|
|
||||||
version = text[pos1: pos2]
|
|
||||||
release = text[pos2 + 1:]
|
|
||||||
else:
|
|
||||||
version = text[pos1:]
|
|
||||||
release = None
|
|
||||||
return cls(epoch, version, release)
|
|
||||||
|
|
||||||
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
|
|
||||||
if self.release and other.release:
|
|
||||||
return self._cmp_version_part(self.release, other.release)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if other is self:
|
|
||||||
return True
|
|
||||||
return self.cmp(other) == 0
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.release:
|
|
||||||
return "{0}:{1}-{2}".format(
|
|
||||||
self.epoch,
|
|
||||||
".".join(str(x) for x in self.version),
|
|
||||||
".".join(str(x) for x in self.release)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return "{0}:{1}".format(
|
|
||||||
self.epoch,
|
|
||||||
".".join(str(x) for x in self.version),
|
|
||||||
)
|
|
||||||
|
|
||||||
@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)
|
|
||||||
# handle the tilde separator, which is ~ = (int) 382
|
|
||||||
# _order("~") == 382
|
|
||||||
# if both versions have tilde then let's continue
|
|
||||||
if a == b == 382:
|
|
||||||
continue
|
|
||||||
if a == 382:
|
|
||||||
return -1
|
|
||||||
if b == 382:
|
|
||||||
return 1
|
|
||||||
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
|
|
|
@ -20,7 +20,6 @@ import copy
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from packetary.objects import PackageRelation
|
from packetary.objects import PackageRelation
|
||||||
from packetary.objects import PackageVersion
|
|
||||||
from packetary.objects import VersionRange
|
from packetary.objects import VersionRange
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,44 +214,3 @@ class TestVersionRange(TestObjectBase):
|
||||||
def test_intersection_is_typesafe(self):
|
def test_intersection_is_typesafe(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
VersionRange("=", 1).has_intersection(("=", 1))
|
VersionRange("=", 1).has_intersection(("=", 1))
|
||||||
|
|
||||||
|
|
||||||
class TestPackageVersion(base.TestCase):
|
|
||||||
def test_get_from_string(self):
|
|
||||||
ver = PackageVersion.from_string("1.0-22")
|
|
||||||
self.assertEqual(0, ver.epoch)
|
|
||||||
self.assertEqual(('1', '0'), ver.version)
|
|
||||||
self.assertEqual(('22',), ver.release)
|
|
||||||
|
|
||||||
ver2 = PackageVersion.from_string("1:11.0-2")
|
|
||||||
self.assertEqual(1, ver2.epoch)
|
|
||||||
self.assertEqual(('11', '0'), ver2.version)
|
|
||||||
self.assertEqual(('2',), ver2.release)
|
|
||||||
|
|
||||||
ver3 = PackageVersion.from_string("11.0")
|
|
||||||
self.assertEqual(0, ver3.epoch)
|
|
||||||
self.assertEqual(('11', '0'), ver3.version)
|
|
||||||
self.assertIsNone(ver3.release)
|
|
||||||
|
|
||||||
def test_compare(self):
|
|
||||||
ver1 = PackageVersion.from_string("6.3-31.5")
|
|
||||||
ver2 = PackageVersion.from_string("13.9-16.12")
|
|
||||||
ver3 = PackageVersion.from_string("13.9")
|
|
||||||
ver4 = PackageVersion.from_string("1:13.9")
|
|
||||||
ver5 = PackageVersion.from_string("1:8.0.0~b3")
|
|
||||||
ver6 = PackageVersion.from_string("1:8.0.0")
|
|
||||||
ver7 = PackageVersion.from_string("1:8.0.0~a1")
|
|
||||||
self.assertLess(ver1, ver2)
|
|
||||||
self.assertGreater(ver2, ver1)
|
|
||||||
self.assertEqual(ver1, ver1)
|
|
||||||
self.assertLess(ver1, "6.3-40")
|
|
||||||
self.assertGreater(ver1, "6.3-31.4a")
|
|
||||||
self.assertEqual(ver2, ver3)
|
|
||||||
self.assertGreater(ver4, ver3)
|
|
||||||
self.assertGreater(ver4, ver2)
|
|
||||||
# test tilda in versioning
|
|
||||||
# rpmdev-vercmp 1:8.0.0~b3 1:8.0.0
|
|
||||||
# 1:8.0.0~b3 < 1:8.0.0
|
|
||||||
# 1:8.0.0~b3 > 1:8.0.0~a1
|
|
||||||
self.assertGreater(ver6, ver5)
|
|
||||||
self.assertGreater(ver5, ver7)
|
|
||||||
|
|
|
@ -43,8 +43,10 @@ class TestRpmDriver(base.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
sys.modules["createrepo"] = mock.MagicMock()
|
sys.modules["createrepo"] = mock.MagicMock()
|
||||||
|
sys.modules["rpmUtils"] = mock.MagicMock()
|
||||||
from packetary.drivers import rpm_driver
|
from packetary.drivers import rpm_driver
|
||||||
cls.createrepo = rpm_driver.createrepo = mock.MagicMock()
|
cls.createrepo = rpm_driver.createrepo = mock.MagicMock()
|
||||||
|
cls.rpmUtils = rpm_driver.rpmUtils = mock.MagicMock()
|
||||||
|
|
||||||
super(TestRpmDriver, cls).setUpClass()
|
super(TestRpmDriver, cls).setUpClass()
|
||||||
cls.driver = rpm_driver.RpmRepositoryDriver()
|
cls.driver = rpm_driver.RpmRepositoryDriver()
|
||||||
|
@ -52,6 +54,7 @@ class TestRpmDriver(base.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.createrepo.reset_mock()
|
self.createrepo.reset_mock()
|
||||||
|
self.rpmUtils.reset_mock()
|
||||||
self.connection = mock.MagicMock()
|
self.connection = mock.MagicMock()
|
||||||
|
|
||||||
def configure_streams(self, groups_gzipped=True):
|
def configure_streams(self, groups_gzipped=True):
|
||||||
|
@ -127,6 +130,10 @@ class TestRpmDriver(base.TestCase):
|
||||||
self.assertEqual(2, len(packages))
|
self.assertEqual(2, len(packages))
|
||||||
package = packages[0]
|
package = packages[0]
|
||||||
self.assertEqual("test1", package.name)
|
self.assertEqual("test1", package.name)
|
||||||
|
self.rpmUtils.miscutils.stringToVersion.return_value = (
|
||||||
|
"0", "1.1.1.1", "1.el7"
|
||||||
|
)
|
||||||
|
self.rpmUtils.miscutils.compareEVR.return_value = 0
|
||||||
self.assertEqual("1.1.1.1-1.el7", package.version)
|
self.assertEqual("1.1.1.1-1.el7", package.version)
|
||||||
self.assertEqual(100, package.filesize)
|
self.assertEqual(100, package.filesize)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -366,7 +373,7 @@ class TestRpmDriver(base.TestCase):
|
||||||
rpm_mock.returnFileEntries.return_value = ["/usr/bin/test"]
|
rpm_mock.returnFileEntries.return_value = ["/usr/bin/test"]
|
||||||
self.createrepo.yumbased.YumLocalPackage.return_value = rpm_mock
|
self.createrepo.yumbased.YumLocalPackage.return_value = rpm_mock
|
||||||
rpm_mock.returnLocalHeader.return_value = {
|
rpm_mock.returnLocalHeader.return_value = {
|
||||||
"name": "Test", "epoch": 1, "version": "1.2.3", "release": "1",
|
"name": "Test", "epoch": "1", "version": "1.2.3", "release": "1",
|
||||||
"size": "10", "group": "Group"
|
"size": "10", "group": "Group"
|
||||||
}
|
}
|
||||||
repo = gen_repository("Test", url="file:///repo/os/x86_64/")
|
repo = gen_repository("Test", url="file:///repo/os/x86_64/")
|
||||||
|
|
Loading…
Reference in New Issue