Attachments you submit will be routed for moderation. If you have an account, please log in first.

Ticket #2425: normalized_version.patch

File normalized_version.patch, 5.3 KB (added by erikos, 3 years ago)

New NormalizedVersion class

  • src/sugar/bundle/Makefile.am

    diff --git a/src/sugar/bundle/Makefile.am b/src/sugar/bundle/Makefile.am
    index f1af791..50c93de 100644
    a b  
    33        __init__.py                     \ 
    44        bundle.py                       \ 
    55        activitybundle.py               \ 
     6        bundleversion.py                \ 
    67        contentbundle.py 
  • (a) /dev/null vs. (b) b/src/sugar/bundle/bundleversion.py

    diff --git a/src/sugar/bundle/bundleversion.py b/src/sugar/bundle/bundleversion.py
    new file mode 100644
    index 0000000..e763231
    a b  
     1# Copyright (C) 2010, OLPC 
     2# 
     3# This library is free software; you can redistribute it and/or 
     4# modify it under the terms of the GNU Lesser General Public 
     5# License as published by the Free Software Foundation; either 
     6# version 2 of the License, or (at your option) any later version. 
     7# 
     8# This library is distributed in the hope that it will be useful, 
     9# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     11# Lesser General Public License for more details. 
     12# 
     13# You should have received a copy of the GNU Lesser General Public 
     14# License along with this library; if not, write to the 
     15# Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
     16# Boston, MA 02111-1307, USA. 
     17 
     18# 
     19# Based on the implementation of PEP 386, but adapted to our 
     20# numeration schema. 
     21# 
     22 
     23import re 
     24 
     25 
     26class InvalidVersionError(Exception): 
     27    """The passed activity version can not be normalized.""" 
     28    pass 
     29 
     30VERSION_RE = re.compile(r''' 
     31    ^ 
     32    (?P<version>\d+)               # minimum 'N' 
     33    (?P<extraversion>(?:\.\d+)*)   # any number of extra '.N' segments 
     34    (?: 
     35    (?P<local>\-[a-zA-Z]*)         # ignore any string in the comparison 
     36    )? 
     37    $''', re.VERBOSE) 
     38 
     39 
     40class NormalizedVersion(object): 
     41    """A normalized version. 
     42 
     43    Good: 
     44        1 
     45        1.2 
     46        1.2.3 
     47        1.2.3-peru 
     48 
     49    Bad: 
     50        1.2peru        # must be separated with - 
     51        1.2.           # can't end with '.' 
     52        1.02.5         # can't have a leading zero 
     53 
     54    """ 
     55 
     56    def __init__(self, activity_version): 
     57        """Create a NormalizedVersion instance from a version string. 
     58 
     59        Keyword arguments: 
     60        activity_version -- The version string 
     61 
     62        """ 
     63        self._activity_version = activity_version 
     64        self.parts = [] 
     65        self._local = None 
     66 
     67        if not isinstance(self._activity_version, str): 
     68            raise InvalidVersionError(self._activity_version) 
     69 
     70        match = VERSION_RE.search(self._activity_version) 
     71        if not match: 
     72            raise InvalidVersionError(self._activity_version) 
     73 
     74        groups = match.groupdict() 
     75 
     76        version = self._parse_version(groups['version']) 
     77        self.parts.append(version) 
     78 
     79        if groups['extraversion'] not in ('', None): 
     80            versions = self._parse_extraversions(groups['extraversion'][1:]) 
     81            self.parts.extend(versions) 
     82 
     83        self._local = groups['local'] 
     84 
     85    def _parse_version(self, version_string): 
     86        """Verify that there is no leading zero and convert to integer. 
     87 
     88        Keyword arguments: 
     89        version -- string to be parsed 
     90 
     91        Return: Version 
     92 
     93        """ 
     94        if len(version_string) > 1 and version_string[0] == '0': 
     95            raise InvalidVersionError("Can not have leading zero in segment" 
     96                                      " %s in %r" % (version_string, 
     97                                      self._activity_version)) 
     98 
     99        return int(version_string) 
     100 
     101    def _parse_extraversions(self, extraversion_string): 
     102        """Split into N versions and convert them to integers, verify 
     103        that there are no leading zeros and drop trailing zeros. 
     104 
     105        Keyword arguments: 
     106        extraversion -- 'N.N.N...' sequence to be parsed 
     107 
     108        Return: List of extra versions 
     109 
     110        """ 
     111        nums = [] 
     112        for n in extraversion_string.split("."): 
     113            if len(n) > 1 and n[0] == '0': 
     114                raise InvalidVersionError("Can not have leading zero in " 
     115                                          "segment %s in %r" % (n, 
     116                                          self._activity_version)) 
     117            nums.append(int(n)) 
     118 
     119        while nums and nums[-1] == 0: 
     120            nums.pop() 
     121 
     122        return nums 
     123 
     124    def __str__(self): 
     125        version_string = '.'.join(str(v) for v in self.parts) 
     126        if self._local != None: 
     127            version_string += self._local 
     128        return version_string 
     129 
     130    def __repr__(self): 
     131        return "%s('%s')" % (self.__class__.__name__, self) 
     132 
     133    def _cannot_compare(self, other): 
     134        raise TypeError("Can not compare %s and %s" 
     135                % (type(self).__name__, type(other).__name__)) 
     136 
     137    def __eq__(self, other): 
     138        if not isinstance(other, NormalizedVersion): 
     139            self._cannot_compare(other) 
     140        return self.parts == other.parts 
     141 
     142    def __lt__(self, other): 
     143        if not isinstance(other, NormalizedVersion): 
     144            self._cannot_compare(other) 
     145        return self.parts < other.parts 
     146 
     147    def __ne__(self, other): 
     148        return not self.__eq__(other) 
     149 
     150    def __gt__(self, other): 
     151        return not (self.__lt__(other) or self.__eq__(other)) 
     152 
     153    def __le__(self, other): 
     154        return self.__eq__(other) or self.__lt__(other) 
     155 
     156    def __ge__(self, other): 
     157        return self.__eq__(other) or self.__gt__(other)