Enhance Bzr.version_cmp to handle non-numeric versions
authorMichel Alexandre Salim <salimma@fedoraproject.org>
Thu, 4 Aug 2011 15:50:32 +0000 (17:50 +0200)
committerW. Trevor King <wking@drexel.edu>
Thu, 8 Sep 2011 02:02:37 +0000 (22:02 -0400)
bzr uses non-numeric tags to indicate prereleases; previously, this
triggers an exception in be's Bzr module as version comparison is only
supported between version strings that only contain numbers and dots.

This patch extends version_cmp to support a single non-numeric
pre-release string of arbitrary length (e.g. 'a', 'b', 'pre', 'rc'), and
extends the docstring tests to cover this extension.

Signed-off-by: Michel Alexandre Salim <salimma@fedoraproject.org>
libbe/storage/vcs/bzr.py

index 0a4275be3e19a302fe562f144fb1d9d2fcb8b3ed..00435cb6a51f3c1596a85ee1b9417e84d3d0413b 100644 (file)
@@ -87,8 +87,14 @@ class Bzr(base.VCS):
         0
         >>> b.version_cmp(2,3,2)
         -1
+        >>> b.version_cmp(2,3,'a',5)
+        1
         >>> b.version_cmp(2,3,0)
         1
+        >>> b.version_cmp(2,3,1,'a',5)
+        1
+        >>> b.version_cmp(2,3,1,1)
+        -1
         >>> b.version_cmp(3)
         -1
         >>> b._version = '2.0.0pre2'
@@ -96,9 +102,17 @@ class Bzr(base.VCS):
         >>> b.version_cmp(3)
         -1
         >>> b.version_cmp(2,0,1)
-        Traceback (most recent call last):
-          ...
-        NotImplementedError: Cannot parse non-integer portion "0pre2" of Bzr version "2.0.0pre2"
+        -1
+        >>> b.version_cmp(2,0,0,'pre',1)
+        1
+        >>> b.version_cmp(2,0,0,'pre',2)
+        0
+        >>> b.version_cmp(2,0,0,'pre',3)
+        -1
+        >>> b.version_cmp(2,0,0,'a',3)
+        1
+        >>> b.version_cmp(2,0,0,'rc',1)
+        -1
         """
         if not hasattr(self, '_parsed_version') \
                 or self._parsed_version == None:
@@ -108,16 +122,43 @@ class Bzr(base.VCS):
                 try:
                     self._parsed_version.append(int(num))
                 except ValueError, e:
-                    self._parsed_version.append(num)
+                    # bzr version number might contain non-numerical tags
+                    import re
+                    splitter = re.compile(r'[\D]') # Match non-digits
+                    splits = splitter.split(num)
+                    # if len(tag) > 1 some splits will be empty; remove
+                    splits = filter(lambda s: s != '', splits)
+                    tag_starti = len(splits[0])
+                    num_starti = num.find(splits[1], tag_starti)
+                    tag = num[tag_starti:num_starti]
+                    self._parsed_version.append(int(splits[0]))
+                    self._parsed_version.append(tag)
+                    self._parsed_version.append(int(splits[1]))
         for current,other in zip(self._parsed_version, args):
-            if type(current) != types.IntType:
-                raise NotImplementedError(
-                    'Cannot parse non-integer portion "%s" of Bzr version "%s"'
-                    % (current, self.version()))
+            if type(current) != type (other):
+                # one of them is a pre-release string
+                if type(current) != types.IntType:
+                    return -1
+                else:
+                    return 1
             c = cmp(current,other)
             if c != 0:
                 return c
-        return 0
+        # see if one is longer than the other
+        verlen = len(self._parsed_version)
+        arglen = len(args)
+        if verlen == arglen:
+            return 0
+        elif verlen > arglen:
+            if type(self._parsed_version[arglen]) != types.IntType:
+                return -1 # self is a prerelease
+            else:
+                return 1
+        else:
+            if type(args[verlen]) != types.IntType:
+                return 1 # args is a prerelease
+            else:
+                return -1
 
     def _vcs_get_user_id(self):
         # excerpted from bzrlib.builtins.cmd_whoami.run()