diff --git a/pisi/atomicoperations.py b/pisi/atomicoperations.py index 6de30cad..93569f31 100644 --- a/pisi/atomicoperations.py +++ b/pisi/atomicoperations.py @@ -150,7 +150,7 @@ class Install(AtomicOperation): self.ask_reinstall = ask_reinstall self.check_requirements() - self.check_versioning("%s-%s" % (self.pkginfo.version, self.pkginfo.release)) + self.check_versioning(self.pkginfo.version, self.pkginfo.release) self.check_relations() self.check_operation() self.extract_install() @@ -184,9 +184,12 @@ class Install(AtomicOperation): if self.installdb.has_package(replaced.package): pisi.operations.remove.remove_replaced_packages([replaced.package]) - def check_versioning(self, version): - if not pisi.version.Version.valid(version): - raise Error("%s is not a valid PiSi version format" % version) + def check_versioning(self, version, release): + try: + int(release) + pisi.version.Version(version) + except (ValueError, InvalidVersionError): + raise Error(_("%s-%s is not a valid PiSi version format") % (version, release)) def check_relations(self): # check dependencies diff --git a/pisi/operations/build.py b/pisi/operations/build.py index e68c1ddd..ddbf0874 100644 --- a/pisi/operations/build.py +++ b/pisi/operations/build.py @@ -200,7 +200,7 @@ class Builder: self.specdir = os.path.dirname(self.specuri.get_uri()) # Don't wait until creating .pisi file for complaining about versioning scheme errors - self.check_versioning("%s-%s" % (self.spec.getSourceVersion(), self.spec.getSourceRelease())) + self.check_versioning(self.spec.getSourceVersion(), self.spec.getSourceRelease()) self.read_translations(self.specdir) @@ -586,9 +586,12 @@ class Builder: os.chdir(curDir) return True - def check_versioning(self, version): - if not pisi.version.Version.valid(version): - raise Error("%s is not a valid PiSi version format" % version) + def check_versioning(self, version, release): + try: + int(release) + pisi.version.Version(version) + except (ValueError, InvalidVersionError): + raise Error(_("%s-%s is not a valid PiSi version format") % (version, release)) def check_build_dependencies(self): """check and try to install build dependencies, otherwise fail.""" @@ -813,7 +816,8 @@ class Builder: pkg = os.path.basename(old_package_fn) name, version = pisi.util.parse_package_name(pkg[:-5]) ctx.ui.info(_('(found old version %s)') % old_package_fn) - old_build = int(str(pisi.version.Version(version).build)) + ver, rel, build = pisi.util.split_version(version) + old_build = int(build) if build else 0 found.add( (old_package_fn, old_build) ) except Error: ctx.ui.warning('Package file %s may be corrupt. Skipping.' % old_package_fn) diff --git a/pisi/version.py b/pisi/version.py index 77cd3204..1cfddae3 100644 --- a/pisi/version.py +++ b/pisi/version.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2005 - 2007, TUBITAK/UEKAE +# Copyright (C) 2005 - 2010, TUBITAK/UEKAE # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free @@ -12,32 +12,16 @@ """version structure""" -import re -import string - import gettext __trans = gettext.translation('pisi', fallback=True) _ = __trans.ugettext import pisi -import pisi.util as util - -maxdashes = 2 # Basic rule is: # p > (no suffix) > m > rc > pre > beta > alpha # m: milestone. this was added for OO.o # p: patch-level -keywords = { - "alpha" : 0, - "beta" : 1, - "pre" : 2, - "rc" : 3, - "m" : 4, - "NOKEY" : 5, - "p" : 6, - } - __keywords = ( ("alpha", -5), ("beta", -4), @@ -79,196 +63,60 @@ def make_version(version): except ValueError: raise InvalidVersionError(_("Invalid version string: '%s'") % version) -# helper functions -def has_keyword(versionitem): - if versionitem._keyword != "NOKEY": - return True +class Version(object): - return False - -class VersionException(pisi.Exception): - def __init__(self, str): - pisi.Exception.__init__(self, str) - -class VersionItem: - _keyword = "NOKEY" - _value = 0 - - def __init__(self, itemstring): - - # special-case: 1.5p - # here "p" comes as a separate itemstring and conflicts with - # keyword "p". But keyword should allways contain an integer prefix. - # So, skipping looking for keywords if the length() is 1 makes the - # trick. - if len(itemstring) > 1: - - for keyword in keywords.keys(): - if itemstring.startswith(keyword): - - if self._keyword == "NOKEY": - self._keyword = keyword - else: - # longer match is correct - if len(keyword) > len(self._keyword): - self._keyword = keyword - - if self._keyword == "NOKEY": - if len(itemstring) == 1 and itemstring in string.ascii_letters: - # single letter version item ('a' to 'Z') - self._value = itemstring - elif len(itemstring) > 1 and itemstring[0] in string.ascii_letters: - # Unknown keyword - raise VersionException("") - else: - self._value = int(itemstring) - else: - # rest is the version item's value. And each must have - # one! - self._value = int(itemstring[len(self._keyword):]) - - def __str__(self): - return str(self._value) - - def __lt__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l < r: - return True - elif l == r: - return self._value < rhs._value - else: # l > r - return False - - def __le__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l < r: - return True - elif l == r: - return self._value <= rhs._value - else: # l > r - return False - - def __gt__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l > r: - return True - elif l == r: - return self._value > rhs._value - else: # l < r - return False - - def __ge__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l > r: - return True - elif l == r: - return self._value >= rhs._value - else: # l < r - return False - - def __eq__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l == r and self._value == rhs._value: - return True - return False - -class Version: + __slots__ = ("__version", "__version_string") @staticmethod def valid(version): try: - pisi.version.Version(version) - except pisi.version.VersionException, e: + make_version(version) + except InvalidVersionError: return False return True def __init__(self, verstring): - # PiSi version policy does not allow "-" in version strings. - # They are special and used for build and release no separation. - if verstring.count("-") > maxdashes: - raise VersionException("%s is not a valid PiSi version format" % verstring) - - verchunks = verstring.split("-") - verchunks.extend("0" * (maxdashes - verstring.count("-"))) - (version, release, build) = verchunks - - self.comps = [] - for i in util.multisplit(version,'._'): - # some version strings can contain ascii chars at the - # back. As an example: 2.11a - # We split '11a' as two items like '11' and 'a' - s = re.compile("[a-z-A-Z]$").search(i) - if s: - head = i[:s.start()] - tail = s.group() - self.comps.append(VersionItem(head)) - self.comps.append(VersionItem(tail)) - else: - self.comps.append(VersionItem(i)) - - self.verstring = verstring - self.release = VersionItem(release) - self.build = VersionItem(build) + self.__version_string = verstring + self.__version = make_version(verstring) def string(self): - return self.verstring + return self.__version_string def compare(self, ver): - """this comparison routine is essentially a comparison routine - for two rationals in (0,1) interval. we compare two sequences - of digits one by one. We start with the leftmost digit - in the expansion. If they are equal, we proceed to the next. If - not we use the comparison operator. And we iterate to the left. - The result is, 0 if two are equal, -1 if self < rhs, and +1 - if self>rhs""" - if isinstance(ver, basestring): - ver = pisi.version.Version(ver) + return cmp(self.__version, make_version(ver)) - lhs = self.comps - rhs = ver.comps - # pad the short version string with zeros - if len(lhs) < len(rhs): - lhs.extend( [VersionItem('0')] * (len(rhs) - len(lhs)) ) - elif len(lhs) > len(rhs): - rhs.extend( [VersionItem('0')] * (len(lhs) - len(rhs)) ) - # now let's iterate from left to right in version items - for (litem, ritem) in zip(lhs, rhs): - if litem < ritem: - return -1 - elif litem > ritem: - return +1 - # now let's compare release and build - lhs = (self.release, self.build) - rhs = (ver.release, ver.build) - for (litem, ritem) in zip(lhs, rhs): - if litem < ritem: - return -1 - elif litem > ritem: - return +1 - return 0 + return cmp(self.__version, ver.__version) - # premature optimization is the root of all evil + def __lt__(self, rhs): + if isinstance(rhs, basestring): + return self.__version < make_version(rhs) - def __lt__(self,rhs): - return self.compare(rhs) < 0 + return self.__version < rhs.__version - def __le__(self,rhs): - return self.compare(rhs) <= 0 + def __le__(self, rhs): + if isinstance(rhs, basestring): + return self.__version <= make_version(rhs) - def __gt__(self,rhs): - return self.compare(rhs) > 0 + return self.__version <= rhs.__version - def __ge__(self,rhs): - return self.compare(rhs) >= 0 + def __gt__(self, rhs): + if isinstance(rhs, basestring): + return self.__version > make_version(rhs) - def __eq__(self,rhs): - return self.compare(rhs) == 0 + return self.__version > rhs.__version + + def __ge__(self, rhs): + if isinstance(rhs, basestring): + return self.__version >= make_version(rhs) + + return self.__version >= rhs.__version + + def __eq__(self, rhs): + if isinstance(rhs, basestring): + return self.__version_string == rhs + + return self.__version_string == rhs.__version_string def __str__(self): - return self.verstring + return self.__version_string