Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Schampijer <simon@schampijer.de>2010-11-12 10:42:52 (GMT)
committer Simon Schampijer <simon@schampijer.de>2010-11-12 10:42:52 (GMT)
commit3a1117a8fd89d566ef73aebec8f925e069f28d08 (patch)
treed1a3139a89d2dd2ee3408cb37664dd96628f07e4
parentb716665663d4a8caaf91397b60b1160f4cf35c07 (diff)
Add new activity numbering scheme #10379
toolkit: Add class NormalizedVersion to parse and compare the new activity versions, change the bundlebuilder and activitybundle to use the new scheme. backported changes from SL #2425
-rw-r--r--src/sugar/activity/bundlebuilder.py6
-rw-r--r--src/sugar/bundle/Makefile.am1
-rw-r--r--src/sugar/bundle/activitybundle.py13
-rw-r--r--src/sugar/bundle/bundleversion.py157
4 files changed, 170 insertions, 7 deletions
diff --git a/src/sugar/activity/bundlebuilder.py b/src/sugar/activity/bundlebuilder.py
index 555fe98..979becc 100644
--- a/src/sugar/activity/bundlebuilder.py
+++ b/src/sugar/activity/bundlebuilder.py
@@ -82,13 +82,13 @@ class Config(object):
self.bundle_id = bundle.get_bundle_id()
self.bundle_name = reduce(lambda x, y:x+y, self.activity_name.split())
self.bundle_root_dir = self.bundle_name + '.activity'
- self.tar_root_dir = '%s-%d' % (self.bundle_name, self.version)
+ self.tar_root_dir = '%s-%s' % (self.bundle_name, self.version)
if self.dist_name:
self.xo_name = self.tar_name = self.dist_name
else:
- self.xo_name = '%s-%d.xo' % (self.bundle_name, self.version)
- self.tar_name = '%s-%d.tar.bz2' % (self.bundle_name, self.version)
+ self.xo_name = '%s-%s.xo' % (self.bundle_name, self.version)
+ self.tar_name = '%s-%s.tar.bz2' % (self.bundle_name, self.version)
class Builder(object):
def __init__(self, config):
diff --git a/src/sugar/bundle/Makefile.am b/src/sugar/bundle/Makefile.am
index f1af791..50c93de 100644
--- a/src/sugar/bundle/Makefile.am
+++ b/src/sugar/bundle/Makefile.am
@@ -3,4 +3,5 @@ sugar_PYTHON = \
__init__.py \
bundle.py \
activitybundle.py \
+ bundleversion.py \
contentbundle.py
diff --git a/src/sugar/bundle/activitybundle.py b/src/sugar/bundle/activitybundle.py
index 2bec54f..c39c519 100644
--- a/src/sugar/bundle/activitybundle.py
+++ b/src/sugar/bundle/activitybundle.py
@@ -31,6 +31,10 @@ from sugar import util
from sugar.bundle.bundle import Bundle, \
MalformedBundleException, NotInstalledException
+from sugar.bundle.bundleversion import NormalizedVersion
+from sugar.bundle.bundleversion import InvalidVersionError
+
+
class ActivityBundle(Bundle):
"""A Sugar activity bundle
@@ -54,7 +58,7 @@ class ActivityBundle(Bundle):
self._bundle_id = None
self._mime_types = None
self._show_launcher = True
- self._activity_version = 0
+ self._activity_version = '0'
self._installation_time = os.stat(path).st_mtime
self._manifest = None
@@ -181,11 +185,12 @@ class ActivityBundle(Bundle):
if cp.has_option(section, 'activity_version'):
version = cp.get(section, 'activity_version')
try:
- self._activity_version = int(version)
- except ValueError:
+ NormalizedVersion(version)
+ except InvalidVersionError:
raise MalformedBundleException(
- 'Activity bundle %s has invalid version number %s' %
+ 'Activity bundle %s has invalid version number %s' % \
(self._path, version))
+ self._activity_version = version
def _get_linfo_file(self):
lang = locale.getdefaultlocale()[0]
diff --git a/src/sugar/bundle/bundleversion.py b/src/sugar/bundle/bundleversion.py
new file mode 100644
index 0000000..e763231
--- /dev/null
+++ b/src/sugar/bundle/bundleversion.py
@@ -0,0 +1,157 @@
+# Copyright (C) 2010, OLPC
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+#
+# Based on the implementation of PEP 386, but adapted to our
+# numeration schema.
+#
+
+import re
+
+
+class InvalidVersionError(Exception):
+ """The passed activity version can not be normalized."""
+ pass
+
+VERSION_RE = re.compile(r'''
+ ^
+ (?P<version>\d+) # minimum 'N'
+ (?P<extraversion>(?:\.\d+)*) # any number of extra '.N' segments
+ (?:
+ (?P<local>\-[a-zA-Z]*) # ignore any string in the comparison
+ )?
+ $''', re.VERBOSE)
+
+
+class NormalizedVersion(object):
+ """A normalized version.
+
+ Good:
+ 1
+ 1.2
+ 1.2.3
+ 1.2.3-peru
+
+ Bad:
+ 1.2peru # must be separated with -
+ 1.2. # can't end with '.'
+ 1.02.5 # can't have a leading zero
+
+ """
+
+ def __init__(self, activity_version):
+ """Create a NormalizedVersion instance from a version string.
+
+ Keyword arguments:
+ activity_version -- The version string
+
+ """
+ self._activity_version = activity_version
+ self.parts = []
+ self._local = None
+
+ if not isinstance(self._activity_version, str):
+ raise InvalidVersionError(self._activity_version)
+
+ match = VERSION_RE.search(self._activity_version)
+ if not match:
+ raise InvalidVersionError(self._activity_version)
+
+ groups = match.groupdict()
+
+ version = self._parse_version(groups['version'])
+ self.parts.append(version)
+
+ if groups['extraversion'] not in ('', None):
+ versions = self._parse_extraversions(groups['extraversion'][1:])
+ self.parts.extend(versions)
+
+ self._local = groups['local']
+
+ def _parse_version(self, version_string):
+ """Verify that there is no leading zero and convert to integer.
+
+ Keyword arguments:
+ version -- string to be parsed
+
+ Return: Version
+
+ """
+ if len(version_string) > 1 and version_string[0] == '0':
+ raise InvalidVersionError("Can not have leading zero in segment"
+ " %s in %r" % (version_string,
+ self._activity_version))
+
+ return int(version_string)
+
+ def _parse_extraversions(self, extraversion_string):
+ """Split into N versions and convert them to integers, verify
+ that there are no leading zeros and drop trailing zeros.
+
+ Keyword arguments:
+ extraversion -- 'N.N.N...' sequence to be parsed
+
+ Return: List of extra versions
+
+ """
+ nums = []
+ for n in extraversion_string.split("."):
+ if len(n) > 1 and n[0] == '0':
+ raise InvalidVersionError("Can not have leading zero in "
+ "segment %s in %r" % (n,
+ self._activity_version))
+ nums.append(int(n))
+
+ while nums and nums[-1] == 0:
+ nums.pop()
+
+ return nums
+
+ def __str__(self):
+ version_string = '.'.join(str(v) for v in self.parts)
+ if self._local != None:
+ version_string += self._local
+ return version_string
+
+ def __repr__(self):
+ return "%s('%s')" % (self.__class__.__name__, self)
+
+ def _cannot_compare(self, other):
+ raise TypeError("Can not compare %s and %s"
+ % (type(self).__name__, type(other).__name__))
+
+ def __eq__(self, other):
+ if not isinstance(other, NormalizedVersion):
+ self._cannot_compare(other)
+ return self.parts == other.parts
+
+ def __lt__(self, other):
+ if not isinstance(other, NormalizedVersion):
+ self._cannot_compare(other)
+ return self.parts < other.parts
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __gt__(self, other):
+ return not (self.__lt__(other) or self.__eq__(other))
+
+ def __le__(self, other):
+ return self.__eq__(other) or self.__lt__(other)
+
+ def __ge__(self, other):
+ return self.__eq__(other) or self.__gt__(other)