Bug#562257: [PATCH] debian_support: Add a native Python Version class
John Wright
john.wright at hp.com
Thu Mar 11 01:36:19 UTC 2010
Based on the DpkgVersion class by Raphael Hertzog in
svn://svn.debian.org/qa/trunk/pts/www/bin/common.py r2361
Closes: #562257, #573009
---
debian_bundle/debian_support.py | 176 ++++++++++++++++++++++++++++++++-------
1 files changed, 146 insertions(+), 30 deletions(-)
diff --git a/debian_bundle/debian_support.py b/debian_bundle/debian_support.py
index 6543206..e13a251 100644
--- a/debian_bundle/debian_support.py
+++ b/debian_bundle/debian_support.py
@@ -23,8 +23,13 @@ import hashlib
import types
from deprecation import function_deprecated_by
-import apt_pkg
-apt_pkg.init()
+
+try:
+ import apt_pkg
+ apt_pkg.init()
+ __have_apt_pkg = True
+except ImportError:
+ __have_apt_pkg = False
class ParseError(Exception):
"""An exception which is used to signal a parse failure.
@@ -58,24 +63,24 @@ class ParseError(Exception):
printOut = function_deprecated_by(print_out)
-class Version:
- """Version class which uses the original APT comparison algorithm."""
+class AptPkgVersion(object):
+ """Represents a Debian package version, using apt_pkg.VersionCompare"""
def __init__(self, version):
"""Creates a new Version object."""
- t = type(version)
- if t == types.UnicodeType:
- version = version.encode('UTF-8')
+ if not isinstance(version, (str, unicode)):
+ raise ValueError, "version must be a string or unicode object"
+
+ if isinstance(version, unicode):
+ self.__as_string = version.encode("utf-8")
else:
- assert t == types.StringType, `version`
- assert version <> ""
- self.__asString = version
+ self.__as_string = version
def __str__(self):
- return self.__asString
+ return self.__as_string
def __repr__(self):
- return 'Version(%s)' % `self.__asString`
+ return "%s('%s')" % (self.__class__.__name__, self)
def __cmp__(self, other):
return apt_pkg.VersionCompare(str(self), str(other))
@@ -83,8 +88,118 @@ class Version:
def __hash__(self):
return hash(str(self))
+# NativeVersion based on the DpkgVersion class by Raphael Hertzog in
+# svn://svn.debian.org/qa/trunk/pts/www/bin/common.py r2361
+class NativeVersion(object):
+ """Represents a Debian package version, with native Python comparison"""
+
+ re_all_digits_or_not = re.compile("\d+|\D+")
+ re_digits = re.compile("\d+")
+ re_digit = re.compile("\d")
+ re_alpha = re.compile("[A-Za-z]")
+ re_epoch_and_version = re.compile("(\d*):(.+)")
+ re_version_and_revision = re.compile("(.+)-(.*)")
+
+ def __init__(self, version):
+ result = self.re_epoch_and_version.match(version)
+ if result is not None:
+ self.epoch = int(result.group(1))
+ version = result.group(2)
+ self.no_epoch = False
+ else:
+ self.epoch = 0
+ self.no_epoch = True
+ result = self.re_version_and_revision.match(version)
+ if result is not None:
+ self.version = result.group(1)
+ self.revision = result.group(2)
+ self.no_revision = False
+ else:
+ self.version = version
+ self.revision = "0"
+ self.no_revision = True
-version_compare = apt_pkg.VersionCompare
+ def __str__(self):
+ version = ""
+ if not self.no_epoch:
+ version += self.epoch + ":"
+ version += self.version
+ if not self.no_revision:
+ version += "-" + self.revision
+ return version
+
+ def __repr__(self):
+ return "%s('%s')" % (self.__class__.__name__, self)
+
+ def __cmp__(self, other):
+ res = cmp(self.epoch, other.epoch)
+ if res != 0:
+ return res
+ res = self._version_cmp_part(self.version, other.version)
+ if res != 0:
+ return res
+ return self._version_cmp_part(self.revision, other.revision)
+
+ @classmethod
+ def _order(cls, x):
+ """Return an integer value for character x"""
+ if x == '~':
+ return -1
+ elif cls.re_digit.match(x):
+ return int(x) + 1
+ elif cls.re_alpha.match(x):
+ return ord(x)
+ else:
+ return ord(x) + 256
+
+ @classmethod
+ def _version_cmp_string(cls, va, vb):
+ la = [cls._order(x) for x in va]
+ lb = [cls._order(x) for x in vb]
+ while la or lb:
+ a = 0
+ b = 0
+ if la:
+ a = la.pop(0)
+ if lb:
+ b = lb.pop(0)
+ res = cmp(a, b)
+ if res != 0:
+ return res
+ return 0
+
+ @classmethod
+ def _version_cmp_part(cls, va, vb):
+ la = cls.re_all_digits_or_not.findall(va)
+ lb = cls.re_all_digits_or_not.findall(vb)
+ while la or lb:
+ a = "0"
+ b = "0"
+ if la:
+ a = la.pop(0)
+ if lb:
+ b = lb.pop(0)
+ if cls.re_digits.match(a) and cls.re_digits.match(b):
+ a = int(a)
+ b = int(b)
+ res = cmp(a, b)
+ if res != 0:
+ return res
+ else:
+ res = cls._version_cmp_string(a, b)
+ if res != 0:
+ return res
+ return 0
+
+if __have_apt_pkg:
+ class Version(AptPkgVersion):
+ pass
+else:
+ class Version(NativeVersion):
+ pass
+
+def version_compare(a, b):
+ return cmp(Version(a), Version(b))
class PackageFile:
"""A Debian package file.
@@ -423,23 +538,24 @@ mergeAsSets = function_deprecated_by(merge_as_sets)
def test():
# Version
- assert Version('0') < Version('a')
- assert Version('1.0') < Version('1.1')
- assert Version('1.2') < Version('1.11')
- assert Version('1.0-0.1') < Version('1.1')
- assert Version('1.0-0.1') < Version('1.0-1')
- assert Version('1.0-0.1') == Version('1.0-0.1')
- assert Version('1.0-0.1') < Version('1.0-1')
- assert Version('1.0final-5sarge1') > Version('1.0final-5') \
- > Version('1.0a7-2')
- assert Version('0.9.2-5') < Version('0.9.2+cvs.1.0.dev.2004.07.28-1.5')
- assert Version('1:500') < Version('1:5000')
- assert Version('100:500') > Version('11:5000')
- assert Version('1.0.4-2') > Version('1.0pre7-2')
- assert Version('1.5~rc1') < Version('1.5')
- assert Version('1.5~rc1') < Version('1.5+b1')
- assert Version('1.5~rc1') < Version('1.5~rc2')
- assert Version('1.5~rc1') > Version('1.5~dev0')
+ for cls in (AptPkgVersion, NativeVersion):
+ assert cls('0') < cls('a')
+ assert cls('1.0') < cls('1.1')
+ assert cls('1.2') < cls('1.11')
+ assert cls('1.0-0.1') < cls('1.1')
+ assert cls('1.0-0.1') < cls('1.0-1')
+ assert cls('1.0-0.1') == cls('1.0-0.1')
+ assert cls('1.0-0.1') < cls('1.0-1')
+ assert cls('1.0final-5sarge1') > cls('1.0final-5') \
+ > cls('1.0a7-2')
+ assert cls('0.9.2-5') < cls('0.9.2+cvs.1.0.dev.2004.07.28-1.5')
+ assert cls('1:500') < cls('1:5000')
+ assert cls('100:500') > cls('11:5000')
+ assert cls('1.0.4-2') > cls('1.0pre7-2')
+ assert cls('1.5~rc1') < cls('1.5')
+ assert cls('1.5~rc1') < cls('1.5+b1')
+ assert cls('1.5~rc1') < cls('1.5~rc2')
+ assert cls('1.5~rc1') > cls('1.5~dev0')
# Release
assert intern_release('sarge') < intern_release('etch')
--
1.6.6
More information about the pkg-python-debian-maint
mailing list