Rename the module to sugar3

The old gtk-2 based module will be present in
the 0.94 branch in the sugar-toolkit.

Signed-off-by: Simon Schampijer <simon@laptop.org>
Acked-by: Sascha Silbe <silbe@activitycentral.com>
This commit is contained in:
Simon Schampijer
2011-10-29 10:19:34 +02:00
parent 516d7fc700
commit 000ed75cbe
103 changed files with 25 additions and 25 deletions
+7
View File
@@ -0,0 +1,7 @@
sugardir = $(pythondir)/sugar3/bundle
sugar_PYTHON = \
__init__.py \
bundle.py \
activitybundle.py \
bundleversion.py \
contentbundle.py
+16
View File
@@ -0,0 +1,16 @@
# Copyright (C) 2006-2007, Red Hat, Inc.
#
# 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.
+341
View File
@@ -0,0 +1,341 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# 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.
"""Sugar activity bundles
UNSTABLE.
"""
from ConfigParser import ConfigParser
import locale
import os
import shutil
import tempfile
import logging
import warnings
from sugar import env
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
See http://wiki.laptop.org/go/Activity_bundles for details
"""
MIME_TYPE = 'application/vnd.olpc-sugar'
DEPRECATED_MIME_TYPE = 'application/vnd.olpc-x-sugar'
_zipped_extension = '.xo'
_unzipped_extension = '.activity'
_infodir = 'activity'
def __init__(self, path):
Bundle.__init__(self, path)
self.activity_class = None
self.bundle_exec = None
self._name = None
self._local_name = None
self._icon = None
self._bundle_id = None
self._mime_types = None
self._show_launcher = True
self._tags = None
self._activity_version = '0'
self._installation_time = os.stat(path).st_mtime
info_file = self.get_file('activity/activity.info')
if info_file is None:
raise MalformedBundleException('No activity.info file')
self._parse_info(info_file)
linfo_file = self._get_linfo_file()
if linfo_file:
self._parse_linfo(linfo_file)
if self._local_name == None:
self._local_name = self._name
def _parse_info(self, info_file):
cp = ConfigParser()
cp.readfp(info_file)
section = 'Activity'
if cp.has_option(section, 'bundle_id'):
self._bundle_id = cp.get(section, 'bundle_id')
# FIXME deprecated
elif cp.has_option(section, 'service_name'):
warnings.warn('use bundle_id instead of service_name ' \
'in your activity.info', DeprecationWarning)
self._bundle_id = cp.get(section, 'service_name')
else:
raise MalformedBundleException(
'Activity bundle %s does not specify a bundle id' %
self._path)
if cp.has_option(section, 'name'):
self._name = cp.get(section, 'name')
else:
raise MalformedBundleException(
'Activity bundle %s does not specify a name' % self._path)
# FIXME class is deprecated
if cp.has_option(section, 'class'):
warnings.warn('use exec instead of class ' \
'in your activity.info', DeprecationWarning)
self.activity_class = cp.get(section, 'class')
elif cp.has_option(section, 'exec'):
self.bundle_exec = cp.get(section, 'exec')
else:
raise MalformedBundleException(
'Activity bundle %s must specify either class or exec' %
self._path)
if cp.has_option(section, 'mime_types'):
mime_list = cp.get(section, 'mime_types').strip(';')
self._mime_types = [mime.strip() for mime in mime_list.split(';')]
if cp.has_option(section, 'show_launcher'):
if cp.get(section, 'show_launcher') == 'no':
self._show_launcher = False
if cp.has_option(section, 'tags'):
tag_list = cp.get(section, 'tags').strip(';')
self._tags = [tag.strip() for tag in tag_list.split(';')]
if cp.has_option(section, 'icon'):
self._icon = cp.get(section, 'icon')
if cp.has_option(section, 'activity_version'):
version = cp.get(section, 'activity_version')
try:
NormalizedVersion(version)
except InvalidVersionError:
raise MalformedBundleException(
'Activity bundle %s has invalid version number %s' %
(self._path, version))
self._activity_version = version
def _get_linfo_file(self):
lang = locale.getdefaultlocale()[0]
if not lang:
return None
linfo_path = os.path.join('locale', lang, 'activity.linfo')
linfo_file = self.get_file(linfo_path)
if linfo_file is not None:
return linfo_file
linfo_path = os.path.join('locale', lang[:2], 'activity.linfo')
linfo_file = self.get_file(linfo_path)
if linfo_file is not None:
return linfo_file
return None
def _parse_linfo(self, linfo_file):
cp = ConfigParser()
cp.readfp(linfo_file)
section = 'Activity'
if cp.has_option(section, 'name'):
self._local_name = cp.get(section, 'name')
if cp.has_option(section, 'tags'):
tag_list = cp.get(section, 'tags').strip(';')
self._tags = [tag.strip() for tag in tag_list.split(';')]
def get_locale_path(self):
"""Get the locale path inside the (installed) activity bundle."""
if self._zip_file is not None:
raise NotInstalledException
return os.path.join(self._path, 'locale')
def get_icons_path(self):
"""Get the icons path inside the (installed) activity bundle."""
if self._zip_file is not None:
raise NotInstalledException
return os.path.join(self._path, 'icons')
def get_path(self):
"""Get the activity bundle path."""
return self._path
def get_name(self):
"""Get the activity user-visible name."""
return self._local_name
def get_bundle_name(self):
"""Get the activity bundle name."""
return self._name
def get_installation_time(self):
"""Get a timestamp representing the time at which this activity was
installed."""
return self._installation_time
def get_bundle_id(self):
"""Get the activity bundle id"""
return self._bundle_id
def get_icon(self):
"""Get the activity icon name"""
# FIXME: this should return the icon data, not a filename, so that
# we don't need to create a temp file in the zip case
icon_path = os.path.join('activity', self._icon + '.svg')
if self._zip_file is None:
return os.path.join(self._path, icon_path)
else:
icon_data = self.get_file(icon_path).read()
temp_file, temp_file_path = tempfile.mkstemp(prefix=self._icon,
suffix='.svg')
os.write(temp_file, icon_data)
os.close(temp_file)
return util.TempFilePath(temp_file_path)
def get_activity_version(self):
"""Get the activity version"""
return self._activity_version
def get_command(self):
"""Get the command to execute to launch the activity factory"""
if self.bundle_exec:
command = os.path.expandvars(self.bundle_exec)
else:
command = 'sugar-activity ' + self.activity_class
return command
def get_mime_types(self):
"""Get the MIME types supported by the activity"""
return self._mime_types
def get_tags(self):
"""Get the tags that describe the activity"""
return self._tags
def get_show_launcher(self):
"""Get whether there should be a visible launcher for the activity"""
return self._show_launcher
def install(self, install_dir=None):
if install_dir is None:
install_dir = env.get_user_activities_path()
self._unzip(install_dir)
install_path = os.path.join(install_dir, self._zip_root_dir)
self.install_mime_type(install_path)
return install_path
def install_mime_type(self, install_path):
""" Update the mime type database and install the mime type icon
"""
xdg_data_home = os.getenv('XDG_DATA_HOME',
os.path.expanduser('~/.local/share'))
mime_path = os.path.join(install_path, 'activity', 'mimetypes.xml')
if os.path.isfile(mime_path):
mime_dir = os.path.join(xdg_data_home, 'mime')
mime_pkg_dir = os.path.join(mime_dir, 'packages')
if not os.path.isdir(mime_pkg_dir):
os.makedirs(mime_pkg_dir)
installed_mime_path = os.path.join(mime_pkg_dir,
'%s.xml' % self._bundle_id)
self._symlink(mime_path, installed_mime_path)
os.spawnlp(os.P_WAIT, 'update-mime-database',
'update-mime-database', mime_dir)
mime_types = self.get_mime_types()
if mime_types is not None:
installed_icons_dir = os.path.join(xdg_data_home,
'icons/sugar/scalable/mimetypes')
if not os.path.isdir(installed_icons_dir):
os.makedirs(installed_icons_dir)
for mime_type in mime_types:
mime_icon_base = os.path.join(install_path, 'activity',
mime_type.replace('/', '-'))
svg_file = mime_icon_base + '.svg'
info_file = mime_icon_base + '.icon'
self._symlink(svg_file,
os.path.join(installed_icons_dir,
os.path.basename(svg_file)))
self._symlink(info_file,
os.path.join(installed_icons_dir,
os.path.basename(info_file)))
def _symlink(self, src, dst):
if not os.path.isfile(src):
return
if not os.path.islink(dst) and os.path.exists(dst):
raise RuntimeError('Do not remove %s if it was not '
'installed by sugar', dst)
logging.debug('Link resource %s to %s', src, dst)
if os.path.lexists(dst):
logging.debug('Relink %s', dst)
os.unlink(dst)
os.symlink(src, dst)
def uninstall(self, install_path, force=False, delete_profile=False):
if os.path.islink(install_path):
# Don't remove the actual activity dir if it's a symbolic link
# because we may be removing user data.
os.unlink(install_path)
return
xdg_data_home = os.getenv('XDG_DATA_HOME',
os.path.expanduser('~/.local/share'))
mime_dir = os.path.join(xdg_data_home, 'mime')
installed_mime_path = os.path.join(mime_dir, 'packages',
'%s.xml' % self._bundle_id)
if os.path.exists(installed_mime_path):
os.remove(installed_mime_path)
os.spawnlp(os.P_WAIT, 'update-mime-database',
'update-mime-database', mime_dir)
mime_types = self.get_mime_types()
if mime_types is not None:
installed_icons_dir = os.path.join(xdg_data_home,
'icons/sugar/scalable/mimetypes')
if os.path.isdir(installed_icons_dir):
for f in os.listdir(installed_icons_dir):
path = os.path.join(installed_icons_dir, f)
if os.path.islink(path) and \
os.readlink(path).startswith(install_path):
os.remove(path)
if delete_profile:
bundle_profile_path = env.get_profile_path(self._bundle_id)
if os.path.exists(bundle_profile_path):
os.chmod(bundle_profile_path, 0775)
shutil.rmtree(bundle_profile_path, ignore_errors=True)
self._uninstall(install_path)
def is_user_activity(self):
return self.get_path().startswith(env.get_user_activities_path())
+200
View File
@@ -0,0 +1,200 @@
# Copyright (C) 2007, Red Hat, Inc.
#
# 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.
"""Sugar bundle file handler
UNSTABLE.
"""
import os
import logging
import shutil
import StringIO
import zipfile
class AlreadyInstalledException(Exception):
pass
class NotInstalledException(Exception):
pass
class InvalidPathException(Exception):
pass
class ZipExtractException(Exception):
pass
class RegistrationException(Exception):
pass
class MalformedBundleException(Exception):
pass
class Bundle(object):
"""A Sugar activity, content module, etc.
The bundle itself may be either a zip file or a directory
hierarchy, with metadata about the bundle stored various files
inside it.
This is an abstract base class. See ActivityBundle and
ContentBundle for more details on those bundle types.
"""
_zipped_extension = None
_unzipped_extension = None
def __init__(self, path):
self._path = path
self._zip_root_dir = None
self._zip_file = None
if not os.path.isdir(self._path):
try:
self._zip_file = zipfile.ZipFile(self._path)
except zipfile.error, exception:
raise MalformedBundleException('Error accessing zip file %r: '
'%s' % (self._path, exception))
self._check_zip_bundle()
def __del__(self):
if self._zip_file is not None:
self._zip_file.close()
def _check_zip_bundle(self):
file_names = self._zip_file.namelist()
if len(file_names) == 0:
raise MalformedBundleException('Empty zip file')
if file_names[0] == 'mimetype':
del file_names[0]
self._zip_root_dir = file_names[0].split('/')[0]
if self._zip_root_dir.startswith('.'):
raise MalformedBundleException(
'root directory starts with .')
if self._unzipped_extension is not None:
(name_, ext) = os.path.splitext(self._zip_root_dir)
if ext != self._unzipped_extension:
raise MalformedBundleException(
'All files in the bundle must be inside a single ' +
'directory whose name ends with %r' %
self._unzipped_extension)
for file_name in file_names:
if not file_name.startswith(self._zip_root_dir):
raise MalformedBundleException(
'All files in the bundle must be inside a single ' +
'top-level directory')
def get_file(self, filename):
f = None
if self._zip_file is None:
path = os.path.join(self._path, filename)
try:
f = open(path, 'rb')
except IOError:
return None
else:
path = os.path.join(self._zip_root_dir, filename)
try:
data = self._zip_file.read(path)
f = StringIO.StringIO(data)
except KeyError:
logging.debug('%s not found.', filename)
return f
def is_file(self, filename):
if self._zip_file is None:
path = os.path.join(self._path, filename)
return os.path.isfile(path)
else:
path = os.path.join(self._zip_root_dir, filename)
try:
self._zip_file.getinfo(path)
except KeyError:
return False
return True
def is_dir(self, filename):
if self._zip_file is None:
path = os.path.join(self._path, filename)
return os.path.isdir(path)
else:
path = os.path.join(self._zip_root_dir, filename, "")
for f in self._zip_file.namelist():
if f.startswith(path):
return True
return False
def get_path(self):
"""Get the bundle path."""
return self._path
def _unzip(self, install_dir):
if self._zip_file is None:
raise AlreadyInstalledException
if not os.path.isdir(install_dir):
os.mkdir(install_dir, 0775)
# zipfile provides API that in theory would let us do this
# correctly by hand, but handling all the oddities of
# Windows/UNIX mappings, extension attributes, deprecated
# features, etc makes it impractical.
if os.spawnlp(os.P_WAIT, 'unzip', 'unzip', '-o', self._path,
'-x', 'mimetype', '-d', install_dir):
# clean up install dir after failure
shutil.rmtree(os.path.join(install_dir, self._zip_root_dir),
ignore_errors=True)
# indicate failure.
raise ZipExtractException
def _zip(self, bundle_path):
if self._zip_file is not None:
raise NotInstalledException
raise NotImplementedError
def _uninstall(self, install_path):
if not os.path.isdir(install_path):
raise InvalidPathException
if self._unzipped_extension is not None:
(name_, ext) = os.path.splitext(install_path)
if ext != self._unzipped_extension:
raise InvalidPathException
for root, dirs, files in os.walk(install_path, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
path = os.path.join(root, name)
if os.path.islink(path):
os.remove(path)
else:
os.rmdir(path)
os.rmdir(install_path)
+157
View File
@@ -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)
+243
View File
@@ -0,0 +1,243 @@
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2009 Aleksey Lim
#
# 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.
"""Sugar content bundles
UNSTABLE.
"""
from ConfigParser import ConfigParser
import os
import urllib
from sugar import env
from sugar.bundle.bundle import Bundle, NotInstalledException, \
MalformedBundleException
from sugar.bundle.bundleversion import NormalizedVersion
from sugar.bundle.bundleversion import InvalidVersionError
class ContentBundle(Bundle):
"""A Sugar content bundle
See http://wiki.laptop.org/go/Content_bundles for details
"""
MIME_TYPE = 'application/vnd.olpc-content'
_zipped_extension = '.xol'
_unzipped_extension = None
_infodir = 'library'
def __init__(self, path):
Bundle.__init__(self, path)
self._locale = None
self._l10n = None
self._category = None
self._name = None
self._subcategory = None
self._category_class = None
self._category_icon = None
self._library_version = '0'
self._bundle_class = None
self._activity_start = None
self._global_name = None
info_file = self.get_file('library/library.info')
if info_file is None:
raise MalformedBundleException('No library.info file')
self._parse_info(info_file)
if (self.get_file('index.html') is None and
self.get_file('library/library.xml') is None):
raise MalformedBundleException(
'Content bundle %s has neither index.html nor library.xml' %
self._path)
def _parse_info(self, info_file):
cp = ConfigParser()
cp.readfp(info_file)
section = 'Library'
if cp.has_option(section, 'name'):
self._name = cp.get(section, 'name')
else:
raise MalformedBundleException(
'Content bundle %s does not specify a name' % self._path)
if cp.has_option(section, 'library_version'):
version = cp.get(section, 'library_version')
try:
NormalizedVersion(version)
except InvalidVersionError:
raise MalformedBundleException(
'Content bundle %s has invalid version number %s' %
(self._path, version))
self._library_version = version
if cp.has_option(section, 'l10n'):
l10n = cp.get(section, 'l10n')
if l10n == 'true':
self._l10n = True
elif l10n == 'false':
self._l10n = False
else:
raise MalformedBundleException(
'Content bundle %s has invalid l10n key %r' %
(self._path, l10n))
else:
raise MalformedBundleException(
'Content bundle %s does not specify if it is localized' %
self._path)
if cp.has_option(section, 'locale'):
self._locale = cp.get(section, 'locale')
else:
raise MalformedBundleException(
'Content bundle %s does not specify a locale' % self._path)
if cp.has_option(section, 'category'):
self._category = cp.get(section, 'category')
else:
raise MalformedBundleException(
'Content bundle %s does not specify a category' % self._path)
if cp.has_option(section, 'global_name'):
self._global_name = cp.get(section, 'global_name')
else:
self._global_name = None
if cp.has_option(section, 'category_icon'):
self._category_icon = cp.get(section, 'category_icon')
else:
self._category_icon = None
if cp.has_option(section, 'category_class'):
self._category_class = cp.get(section, 'category_class')
else:
self._category_class = None
if cp.has_option(section, 'subcategory'):
self._subcategory = cp.get(section, 'subcategory')
else:
self._subcategory = None
if cp.has_option(section, 'bundle_class'):
self._bundle_class = cp.get(section, 'bundle_class')
else:
self._bundle_class = None
if cp.has_option(section, 'activity_start'):
self._activity_start = cp.get(section, 'activity_start')
else:
self._activity_start = 'index.html'
if self._bundle_class is None and self._global_name is None:
raise MalformedBundleException(
'Content bundle %s must specify either global_name or '
'bundle_class' % self._path)
def get_name(self):
return self._name
def get_library_version(self):
return self._library_version
def get_l10n(self):
return self._l10n
def get_locale(self):
return self._locale
def get_category(self):
return self._category
def get_category_icon(self):
return self._category_icon
def get_category_class(self):
return self._category_class
def get_subcategory(self):
return self._subcategory
def get_bundle_class(self):
return self._bundle_class
def get_activity_start(self):
return self._activity_start
def _run_indexer(self):
xdg_data_dirs = os.getenv('XDG_DATA_DIRS',
'/usr/local/share/:/usr/share/')
for path in xdg_data_dirs.split(':'):
indexer = os.path.join(path, 'library-common', 'make_index.py')
if os.path.exists(indexer):
os.spawnlp(os.P_WAIT, 'python', 'python', indexer)
def get_root_dir(self):
return os.path.join(env.get_user_library_path(), self._zip_root_dir)
def get_start_path(self):
return os.path.join(self.get_root_dir(), self._activity_start)
def get_start_uri(self):
return 'file://' + urllib.pathname2url(self.get_start_path())
def get_bundle_id(self):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
if self._bundle_class is not None:
return self._bundle_class
else:
return self._global_name
def get_activity_version(self):
# TODO treat ContentBundle in special way
# needs rethinking while fixing ContentBundle support
return self._library_version
def is_installed(self):
if self._zip_file is None:
return True
elif os.path.isdir(self.get_root_dir()):
return ContentBundle(self.get_root_dir()).get_library_version() \
== self.get_library_version()
else:
return False
def install(self):
# TODO ignore passed install_path argument
# needs rethinking while fixing ContentBundle support
install_path = env.get_user_library_path()
self._unzip(install_path)
self._run_indexer()
return self.get_root_dir()
def uninstall(self):
if self._zip_file is None:
if not self.is_installed():
raise NotInstalledException
install_dir = self._path
else:
install_dir = os.path.join(self.get_root_dir())
self._uninstall(install_dir)
self._run_indexer()