Ticket #2425: normalized_version.patch

File normalized_version.patch, 5.3 KB (added by erikos, 14 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 sugar_PYTHON = \ 
    33        __init__.py                     \
    44        bundle.py                       \
    55        activitybundle.py               \
     6        bundleversion.py                \
    67        contentbundle.py
  • new file 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
    - +  
     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)