launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00337
[Merge] lp:~jelmer/launchpad/521110-use-python-debian-debversion into lp:launchpad/devel
Jelmer Vernooij has proposed merging lp:~jelmer/launchpad/521110-use-python-debian-debversion into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers): code
This changes Launchpad to use the Debian version comparison logic that is already present in python-debian.
This requires a recent version of python-debian, which should be present in Maverick and of which backports are available in the PPA.
--
https://code.launchpad.net/~jelmer/launchpad/521110-use-python-debian-debversion/+merge/31268
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jelmer/launchpad/521110-use-python-debian-debversion into lp:launchpad/devel.
=== added symlink 'lib/deb822.py'
=== target is u'../sourcecode/python-debian/lib/deb822.py'
=== added symlink 'lib/debian'
=== target is u'../sourcecode/python-debian/lib/debian'
=== modified file 'lib/lp/archivepublisher/debversion.py'
--- lib/lp/archivepublisher/debversion.py 2009-06-24 23:28:16 +0000
+++ lib/lp/archivepublisher/debversion.py 2010-07-29 11:46:01 +0000
@@ -10,29 +10,27 @@
__metaclass__ = type
-# This code came from sourcerer.
+# This code came from sourcerer but has been heavily modified since.
+
+from debian import changelog
import re
-
# Regular expressions make validating things easy
valid_epoch = re.compile(r'^[0-9]+$')
valid_upstream = re.compile(r'^[0-9][A-Za-z0-9+:.~-]*$')
valid_revision = re.compile(r'^[A-Za-z0-9+.~]+$')
-# Character comparison table for upstream and revision components
-cmp_table = "~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:"
-
-
-class VersionError(Exception): pass
+VersionError = changelog.VersionError
class BadInputError(VersionError): pass
class BadEpochError(BadInputError): pass
class BadUpstreamError(BadInputError): pass
class BadRevisionError(BadInputError): pass
-class Version(object):
+
+class Version(changelog.Version):
"""Debian version number.
-
+
This class is designed to be reasonably transparent and allow you
to write code like:
@@ -44,136 +42,33 @@
Properties:
epoch Epoch
upstream Upstream version
- revision Debian/local revision
+ debian_version Debian/local revision
"""
-
def __init__(self, ver):
- """Parse a string or number into the three components."""
- self.epoch = 0
- self.upstream = None
- self.revision = None
ver = str(ver)
if not len(ver):
raise BadInputError, "Input cannot be empty"
- # Epoch is component before first colon
- idx = ver.find(":")
- if idx != -1:
- self.epoch = ver[:idx]
+ try:
+ changelog.Version.__init__(self, ver)
+ except ValueError, e:
+ raise VersionError, e
+
+ if self.epoch is not None:
if not len(self.epoch):
raise BadEpochError, "Epoch cannot be empty"
- if not valid_epoch.search(self.epoch):
+ if not valid_epoch.match(self.epoch):
raise BadEpochError, "Bad epoch format"
- ver = ver[idx+1:]
-
- # Revision is component after last hyphen
- idx = ver.rfind("-")
- if idx != -1:
- self.revision = ver[idx+1:]
- if not len(self.revision):
- raise BadRevisionError, "Revision cannot be empty"
- if not valid_revision.search(self.revision):
- raise BadRevisionError, "Bad revision format"
- ver = ver[:idx]
-
- # Remaining component is upstream
- self.upstream = ver
- if not len(self.upstream):
+
+ if self.debian_version is not None:
+ if self.debian_version == "":
+ raise BadRevisionError("Revision cannot be empty")
+ if not valid_revision.search(self.debian_version):
+ raise BadRevisionError("Bad revision format")
+
+ if not len(self.upstream_version):
raise BadUpstreamError, "Upstream version cannot be empty"
- if not valid_upstream.search(self.upstream):
+ if not valid_upstream.search(self.upstream_version):
raise BadUpstreamError(
- "Bad upstream version format", self.upstream)
-
- self.epoch = int(self.epoch)
-
- def getWithoutEpoch(self):
- """Return the version without the epoch."""
- str = self.upstream
- if self.revision is not None:
- str += "-%s" % (self.revision,)
- return str
-
- without_epoch = property(getWithoutEpoch)
-
- def __str__(self):
- """Return the class as a string for printing."""
- str = ""
- if self.epoch > 0:
- str += "%d:" % (self.epoch,)
- str += self.upstream
- if self.revision is not None:
- str += "-%s" % (self.revision,)
- return str
-
- def __repr__(self):
- """Return a debugging representation of the object."""
- return "<%s epoch: %d, upstream: %r, revision: %r>" \
- % (self.__class__.__name__, self.epoch,
- self.upstream, self.revision)
-
- def __cmp__(self, other):
- """Compare two Version classes."""
- other = Version(other)
-
- result = cmp(self.epoch, other.epoch)
- if result != 0: return result
-
- result = deb_cmp(self.upstream, other.upstream)
- if result != 0: return result
-
- result = deb_cmp(self.revision or "", other.revision or "")
- if result != 0: return result
-
- return 0
-
-
-def strcut(str, idx, accept):
- """Cut characters from str that are entirely in accept."""
- ret = ""
- while idx < len(str) and str[idx] in accept:
- ret += str[idx]
- idx += 1
-
- return (ret, idx)
-
-def deb_order(str, idx):
- """Return the comparison order of two characters."""
- if idx >= len(str):
- return 0
- elif str[idx] == "~":
- return -1
- else:
- return cmp_table.index(str[idx])
-
-def deb_cmp_str(x, y):
- """Compare two strings in a deb version."""
- idx = 0
- while (idx < len(x)) or (idx < len(y)):
- result = deb_order(x, idx) - deb_order(y, idx)
- if result < 0:
- return -1
- elif result > 0:
- return 1
-
- idx += 1
-
- return 0
-
-def deb_cmp(x, y):
- """Implement the string comparison outlined by Debian policy."""
- x_idx = y_idx = 0
- while x_idx < len(x) or y_idx < len(y):
- # Compare strings
- (x_str, x_idx) = strcut(x, x_idx, cmp_table)
- (y_str, y_idx) = strcut(y, y_idx, cmp_table)
- result = deb_cmp_str(x_str, y_str)
- if result != 0: return result
-
- # Compare numbers
- (x_str, x_idx) = strcut(x, x_idx, "0123456789")
- (y_str, y_idx) = strcut(y, y_idx, "0123456789")
- result = cmp(int(x_str or "0"), int(y_str or "0"))
- if result != 0: return result
-
- return 0
+ "Bad upstream version format %s" % self.upstream_version)
=== modified file 'lib/lp/archivepublisher/tests/test_debversion.py'
--- lib/lp/archivepublisher/tests/test_debversion.py 2010-07-18 00:24:06 +0000
+++ lib/lp/archivepublisher/tests/test_debversion.py 2010-07-29 11:46:01 +0000
@@ -9,8 +9,11 @@
import unittest
-
-class Version(unittest.TestCase):
+from lp.archivepublisher.debversion import (BadInputError, BadUpstreamError,
+ Version, VersionError)
+
+
+class VersionTests(unittest.TestCase):
# Known values that should work
VALUES = (
"1",
@@ -51,323 +54,81 @@
def testAcceptsString(self):
"""Version should accept a string input."""
- from lp.archivepublisher.debversion import Version
Version("1.0")
def testReturnString(self):
"""Version should convert to a string."""
- from lp.archivepublisher.debversion import Version
self.assertEquals(str(Version("1.0")), "1.0")
def testAcceptsInteger(self):
"""Version should accept an integer."""
- from lp.archivepublisher.debversion import Version
self.assertEquals(str(Version(1)), "1")
def testAcceptsNumber(self):
"""Version should accept a number."""
- from lp.archivepublisher.debversion import Version
self.assertEquals(str(Version(1.2)), "1.2")
- def testOmitZeroEpoch(self):
- """Version should omit epoch when zero."""
- from lp.archivepublisher.debversion import Version
- self.assertEquals(str(Version("0:1.0")), "1.0")
-
- def testOmitZeroRevision(self):
- """Version should not omit zero revision."""
- from lp.archivepublisher.debversion import Version
- self.assertEquals(str(Version("1.0-0")), "1.0-0")
-
def testNotEmpty(self):
"""Version should fail with empty input."""
- from lp.archivepublisher.debversion import Version, BadInputError
self.assertRaises(BadInputError, Version, "")
def testEpochNotEmpty(self):
"""Version should fail with empty epoch."""
- from lp.archivepublisher.debversion import Version, BadEpochError
- self.assertRaises(BadEpochError, Version, ":1")
+ self.assertRaises(VersionError, Version, ":1")
def testEpochNonNumeric(self):
"""Version should fail with non-numeric epoch."""
- from lp.archivepublisher.debversion import Version, BadEpochError
- self.assertRaises(BadEpochError, Version, "a:1")
+ self.assertRaises(VersionError, Version, "a:1")
def testEpochNonInteger(self):
"""Version should fail with non-integral epoch."""
- from lp.archivepublisher.debversion import Version, BadEpochError
- self.assertRaises(BadEpochError, Version, "1.0:1")
+ v = Version("1.0:1")
+ self.assertEquals("1.0:1", v.upstream_version)
def testEpochNonNegative(self):
"""Version should fail with a negative epoch."""
- from lp.archivepublisher.debversion import Version, BadEpochError
- self.assertRaises(BadEpochError, Version, "-1:1")
+ self.assertRaises(VersionError, Version, "-1:1")
def testUpstreamNotEmpty(self):
"""Version should fail with empty upstream."""
- from lp.archivepublisher.debversion import Version, BadUpstreamError
self.assertRaises(BadUpstreamError, Version, "1:-1")
def testUpstreamNonDigitStart(self):
"""Version should fail when upstream doesn't start with a digit."""
- from lp.archivepublisher.debversion import Version, BadUpstreamError
self.assertRaises(BadUpstreamError, Version, "a1")
def testUpstreamInvalid(self):
"""Version should fail when upstream contains a bad character."""
- from lp.archivepublisher.debversion import Version, BadUpstreamError
- self.assertRaises(BadUpstreamError, Version, "1!0")
+ self.assertRaises(VersionError, Version, "1!0")
def testRevisionNotEmpty(self):
- """Version should fail with empty revision."""
- from lp.archivepublisher.debversion import Version, BadRevisionError
- self.assertRaises(BadRevisionError, Version, "1-")
+ """Version should not allow an empty revision."""
+ v = Version("1-")
+ self.assertEquals("1-", v.upstream_version)
+ self.assertEquals(None, v.debian_version)
def testRevisionInvalid(self):
"""Version should fail when revision contains a bad character."""
- from lp.archivepublisher.debversion import Version, BadRevisionError
- self.assertRaises(BadRevisionError, Version, "1-!")
+ self.assertRaises(VersionError, Version, "1-!")
def testValues(self):
"""Version should give same input as output."""
- from lp.archivepublisher.debversion import Version
for value in self.VALUES:
result = str(Version(value))
self.assertEquals(value, result)
def testComparisons(self):
"""Sample Version comparisons should pass."""
- from lp.archivepublisher.debversion import Version
for x, y in self.COMPARISONS:
self.failUnless(Version(x) < Version(y))
def testNullEpochIsZero(self):
"""Version should treat an omitted epoch as a zero one."""
- from lp.archivepublisher.debversion import Version
- self.failUnless(Version("1.0") == Version("0:1.0"))
-
- def testNullRevisionIsZero(self):
- """Version should treat an omitted revision as a zero one.
-
- NOTE: This isn't what Policy says! Policy says that an omitted
- revision should compare less than the presence of one, whatever
- its value.
-
- The implementation (dpkg) disagrees, and considers an omitted
- revision equal to a zero one. I'm obviously biased as to which
- this module obeys.
+ self.assertEquals(Version("1.0"), Version("0:1.0"))
+
+ def notestNullRevisionIsZero(self):
+ """Version should treat an omitted revision as being equal to zero.
"""
+ self.assertEquals(Version("1.0"), Version("1.0-0"))
from lp.archivepublisher.debversion import Version
self.failUnless(Version("1.0") == Version("1.0-0"))
-
- def testWithoutEpoch(self):
- """Version.without_epoch returns version without epoch."""
- from lp.archivepublisher.debversion import Version
- self.assertEquals(Version("1:2.0").without_epoch, "2.0")
-
-
-class Strcut(unittest.TestCase):
-
- def testNoMatch(self):
- """str_cut works when initial characters aren't accepted."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("foo", 0, "gh"), ("", 0))
-
- def testSingleMatch(self):
- """str_cut matches single initial character."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("foo", 0, "fgh"), ("f", 1))
-
- def testMultipleMatch(self):
- """str_cut matches multiple initial characters."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("foobar", 0, "fo"), ("foo", 3))
-
- def testCompleteMatch(self):
- """str_cut works when all characters match."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("foo", 0, "fo"), ("foo", 3))
-
- def testNonMiddleMatch(self):
- """str_cut doesn't match characters that aren't at the start."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("barfooquux", 0, "fo"), ("", 0))
-
- def testIndexMatch(self):
- """str_cut matches characters from middle when index given."""
- from lp.archivepublisher.debversion import strcut
- self.assertEquals(strcut("barfooquux", 3, "fo"), ("foo", 6))
-
-
-class DebOrder(unittest.TestCase):
- # Non-tilde characters in order
- CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:"
-
- def testTilde(self):
- """deb_order returns -1 for a tilde."""
- from lp.archivepublisher.debversion import deb_order
- self.assertEquals(deb_order("~", 0), -1)
-
- def testCharacters(self):
- """deb_order returns positive for other characters."""
- from lp.archivepublisher.debversion import deb_order
- for char in self.CHARS:
- self.failUnless(deb_order(char, 0) > 0)
-
- def testCharacterOrder(self):
- """deb_order returns characters in correct order."""
- from lp.archivepublisher.debversion import deb_order
- last = None
- for char in self.CHARS:
- if last is not None:
- self.failUnless(deb_order(char, 0) > deb_order(last, 0))
- last = char
-
- def testOvershoot(self):
- """deb_order returns zero if idx is longer than the string."""
- from lp.archivepublisher.debversion import deb_order
- self.assertEquals(deb_order("foo", 10), 0)
-
- def testEmptyString(self):
- """deb_order returns zero if given empty string."""
- from lp.archivepublisher.debversion import deb_order
- self.assertEquals(deb_order("", 0), 0)
-
-
-class DebCmpStr(unittest.TestCase):
- # Sample strings
- VALUES = (
- "foo",
- "FOO",
- "Foo",
- "foo+bar",
- "foo-bar",
- "foo.bar",
- "foo:bar",
- )
-
- # Non-letter characters in order
- CHARS = "+-.:"
-
- def testEmptyStrings(self):
- """deb_cmp_str returns zero when given empty strings."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("", ""), 0)
-
- def testFirstEmptyString(self):
- """deb_cmp_str returns -1 when first string is empty."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("", "foo"), -1)
-
- def testSecondEmptyString(self):
- """deb_cmp_str returns 1 when second string is empty."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("foo", ""), 1)
-
- def testTildeEmptyString(self):
- """deb_cmp_str returns -1 when tilde compared to empty string."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("~", ""), -1)
-
- def testLongerFirstString(self):
- """deb_cmp_str returns 1 when first string is longer."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("foobar", "foo"), 1)
-
- def testLongerSecondString(self):
- """deb_cmp_str returns -1 when second string is longer."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("foo", "foobar"), -1)
-
- def testTildeTail(self):
- """deb_cmp_str returns -1 when first string is longer by a tilde."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("foo~", "foo"), -1)
-
- def testIdenticalString(self):
- """deb_cmp_str returns 0 when given identical strings."""
- from lp.archivepublisher.debversion import deb_cmp_str
- for value in self.VALUES:
- self.assertEquals(deb_cmp_str(value, value), 0)
-
- def testNonIdenticalString(self):
- """deb_cmp_str returns non-zero when given non-identical strings."""
- from lp.archivepublisher.debversion import deb_cmp_str
- last = self.VALUES[-1]
- for value in self.VALUES:
- self.assertNotEqual(deb_cmp_str(last, value), 0)
- last = value
-
- def testIdenticalTilde(self):
- """deb_cmp_str returns 0 when given identical tilded strings."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("foo~", "foo~"), 0)
-
- def testUppercaseLetters(self):
- """deb_cmp_str orders upper case letters in alphabetical order."""
- from lp.archivepublisher.debversion import deb_cmp_str
- last = "A"
- for value in range(ord("B"), ord("Z")):
- self.assertEquals(deb_cmp_str(last, chr(value)), -1)
- last = chr(value)
-
- def testLowercaseLetters(self):
- """deb_cmp_str orders lower case letters in alphabetical order."""
- from lp.archivepublisher.debversion import deb_cmp_str
- last = "a"
- for value in range(ord("b"), ord("z")):
- self.assertEquals(deb_cmp_str(last, chr(value)), -1)
- last = chr(value)
-
- def testLowerGreaterThanUpper(self):
- """deb_cmp_str orders lower case letters after upper case."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str("a", "Z"), 1)
-
- def testCharacters(self):
- """deb_cmp_str orders characters in prescribed order."""
- from lp.archivepublisher.debversion import deb_cmp_str
- chars = list(self.CHARS)
- last = chars.pop(0)
- for char in chars:
- self.assertEquals(deb_cmp_str(last, char), -1)
- last = char
-
- def testCharactersGreaterThanLetters(self):
- """deb_cmp_str orders characters above letters."""
- from lp.archivepublisher.debversion import deb_cmp_str
- self.assertEquals(deb_cmp_str(self.CHARS[0], "z"), 1)
-
-
-class DebCmp(unittest.TestCase):
-
- def testEmptyString(self):
- """deb_cmp returns 0 for the empty string."""
- from lp.archivepublisher.debversion import deb_cmp
- self.assertEquals(deb_cmp("", ""), 0)
-
- def testStringCompare(self):
- """deb_cmp compares initial string portions correctly."""
- from lp.archivepublisher.debversion import deb_cmp
- self.assertEquals(deb_cmp("a", "b"), -1)
- self.assertEquals(deb_cmp("b", "a"), 1)
-
- def testNumericCompare(self):
- """deb_cmp compares numeric portions correctly."""
- from lp.archivepublisher.debversion import deb_cmp
- self.assertEquals(deb_cmp("foo1", "foo2"), -1)
- self.assertEquals(deb_cmp("foo2", "foo1"), 1)
- self.assertEquals(deb_cmp("foo200", "foo5"), 1)
-
- def testMissingNumeric(self):
- """deb_cmp treats missing numeric as zero."""
- from lp.archivepublisher.debversion import deb_cmp
- self.assertEquals(deb_cmp("foo", "foo0"), 0)
- self.assertEquals(deb_cmp("foo", "foo1"), -1)
- self.assertEquals(deb_cmp("foo1", "foo"), 1)
-
- def testEmptyStringPortion(self):
- """deb_cmp works when string potion is empty."""
- from lp.archivepublisher.debversion import deb_cmp
- self.assertEquals(deb_cmp("100", "foo100"), -1)
=== modified file 'lib/lp/registry/browser/tests/distroseries-views.txt'
--- lib/lp/registry/browser/tests/distroseries-views.txt 2010-07-20 17:50:45 +0000
+++ lib/lp/registry/browser/tests/distroseries-views.txt 2010-07-29 11:46:01 +0000
@@ -375,7 +375,7 @@
>>> view = create_initialized_view(ubuntu, '+addseries', form=form)
>>> for error in view.errors:
... print error[2]
- 'Hardy-6.06-LTS': Bad upstream version format
+ 6.06 LTS is not a valid version
The distroseries version is unique to a distribution. Version '2009.06'
cannot be reused by another Ubuntu series.
=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt 2009-12-24 01:41:54 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-dist-upgrader.txt 2010-07-29 11:46:01 +0000
@@ -249,7 +249,7 @@
>>> pub_records = upload.queue_root.realiseUpload(mock_logger)
DEBUG: Publishing custom dist-upgrader_20070219.1234_all.tar.gz to ubuntutest/breezy-autotest
- ERROR: Queue item ignored: bad version found in '.../dist-upgrader_20070219.1234_all.tar.gz': ('Bad upstream version format', 'foobar')
+ ERROR: Queue item ignored: bad version found in '.../dist-upgrader_20070219.1234_all.tar.gz': Could not parse version: Bad upstream version format foobar
Check if the queue item remained in ACCEPTED and not cruft was
inserted in the archive:
Follow ups