Version Type for upgrade comparison (Re: [Zope] Was: Zope (X)3 FAQ)

Johan Carlsson [EasyPublisher] johanc@easypublisher.com
Tue, 08 Apr 2003 07:00:38 +0000


At 16:39 2003-04-07 +0200, Lennart Regebro said:
>Johan Carlsson [EasyPublisher] wrote:
>>I think it should use a float instead of a string because it would make it
>>easier to identify the occurrence of an older version than X.X.
>
>Well, that's a good point. Comparing floats is always a pain though, and 
>while > and < works, == and != is unrealiable, and you instead have to use 
>abs(float - comparefloat) < 0.001 and stuff like that, and for most cases 
>all you want to know is if it's the current version or not.

I disagree most cases you want to know if it's never that a specific version.
You may want to reuse the same code to upgrade version 1.6 and 1.7 to 
version 1.8.

>Integers would work nice, though.

Yes, integers works nice.

But Oliver tuple examle got me going.
Tuple of integers would be almost perfect, except for:

(2,) != (2,0)

They should evaluate equal.

Also one interesting aspect would be to allow Version to be
constructed from a string, which made me write one.

I have whipped up a little VersionNumber class.
It supports comparisons, addition and subtraction.
It can be initialized from a string or a tuple.
Strings can have any syntax, integers in the string will be
extracted to form a tuple.

Best Regards,
Johan Carlsson



from types import TupleType, ListType
import re

intListRE=re.compile(r'\d+')

class VersionNumber :

     def __init__(self, *args):
         self.digits=self._parse_args(args)

     def _parse_args(self, args):
         if len(args)==1:
             return tuple(map(lambda x: int(x),intListRE.findall(str(args))))
         elif type(args) in (TupleType, ListType):
             new_args=()
             for arg in args:
                 try: new_args=new_args+(int(arg),)
                 except: pass
             return new_args
         else:
             new_args=()
             try: new_args=new_args+(int(args),)
             except: pass
             return new_args

     def __repr__(self):
         return '.'.join(map(lambda x: str(x), self.digits))

     def _normalize(self, others):
         selfs=self.digits
         if len(selfs)>len(others):
             others=others+((0,)*(len(selfs)-len(others)))
         elif len(selfs)<len(others):
             selfs=selfs+((0,)*(len(others)-len(selfs)))
         return selfs, others

     def __cmp__(self, other):
         if not isinstance(other,self.__class__):
             raise TypeError
         selfs, others=self._normalize(other.digits)
         return cmp(selfs,others)

     def __add__(self, other):
         if isinstance(other,self.__class__):
             others=other.digits
         else:
             others=self._parse_args(args)
         selfs, others=self._normalize(others)
         return tuple(map(lambda x,y: x+y, selfs, others))

     def __sub__(self, other):
         if isinstance(other,self.__class__):
             others=other.digits
         else:
             others=self._parse_args(args)
         selfs, others=self._normalize(others)
         return tuple(map(lambda x,y: x-y, selfs, others))


#Tests

def compare(e1,e2, value):
     result=e1>e2
     if result != value:
         print "%s > %s\t\t" %(e1,e2),value,result

def is_equal(e1,e2, value):
     result=e1==e2
     if result != value:
         print "%s == %s\t\t" %(e1,e2),value,result

def add(e1,e2, value):
     print "%s + %s\t\t" %(e1,e2),e1+e2

def sub(e1,e2, value):
     print "%s - %s\t\t" %(e1,e2),e1-e2


def test():
     ep16=VersionNumber(1,6)
     ep161=VersionNumber(1,6,1)
     ep1_6_123=VersionNumber(1,6,123)
     ep163=VersionNumber(1,6,3)
     ep20=VersionNumber(2,0)
     ep2=VersionNumber(2)
     print
     compare(ep16,ep161, 0)
     compare(ep16,ep1_6_123, 0)
     compare(ep16,ep16, 0)
     compare(ep16,ep20, 0)
     compare(ep16,ep163, 0)
     compare(ep16,ep2, 0)
     compare(ep2,ep20, 0)
     compare(ep20,ep161, 1)
     print
     print "="*25
     compare(ep163,ep161, 1)
     compare(ep163,ep1_6_123, 0)
     compare(ep163,ep16, 1)
     compare(ep163,ep20, 0)
     compare(ep163,ep163, 0)
     compare(ep163,ep2, 0)
     compare(ep163,ep20, 0)
     compare(ep163,ep161, 1)
     print
     print "="*25
     is_equal(ep163,ep161, 0)
     is_equal(ep163,ep1_6_123, 0)
     is_equal(ep163,ep16, 0)
     is_equal(ep163,ep20, 0)
     is_equal(ep163,ep163, 1)
     is_equal(ep163,ep2, 0)
     is_equal(ep163,ep20, 0)
     is_equal(ep163,ep161, 0)

     print "-"*25
     add(ep163,ep161, 0)
     add(ep163,ep1_6_123, 0)
     add(ep163,ep16, 0)
     add(ep163,ep20, 0)
     add(ep163,ep163, 1)
     add(ep163,ep2, 0)
     add(ep163,ep20, 0)
     add(ep163,ep161, 0)

     print "-"*25
     sub(ep163,ep161, 0)
     sub(ep163,ep1_6_123, 0)
     sub(ep163,ep16, 0)
     sub(ep163,ep20, 0)
     sub(ep163,ep163, 1)
     sub(ep163,ep2, 0)
     sub(ep163,ep20, 0)
     sub(ep163,ep161, 0)

     print "-"*25
     compare(VersionNumber("1,6,3"),VersionNumber("1-6-1"), 1)
     compare(VersionNumber("1,6,3"),VersionNumber("1,6,123"), 0)
     compare(VersionNumber("1-6-3"),VersionNumber("1.6"), 1)
     compare(VersionNumber("1,6,3"),VersionNumber("1.6.,3"), 0)
     compare(VersionNumber("1,6,3"),VersionNumber("2"), 0)
     compare(VersionNumber("1,6,3"),VersionNumber("2,0"), 0)
     compare(VersionNumber("2"),VersionNumber("2-2"), 0)
     compare(VersionNumber("ver-2-0"),VersionNumber("1,6,1"), 1)

if __name__ == '__main__':
     test()





-- 
Easy Publisher Developers Team
Johan Carlsson
johanc@easypublisher.com

Mail:
Birkagatan 9
SE-113 36  Stockholm
Sweden

Phone +46-(0)8-31 24 94
Fax +46-(0)8-675 04 44
Mobil +46-(0)70-558 25 24
http://www.easypublisher.com