diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2012-03-17 14:09:21 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2012-03-17 14:09:21 (GMT) |
commit | 5c8536a570edc57c0eb6807ca0bb1abb12309251 (patch) | |
tree | 276ae8e55eaf2262b8112805bef4b698716ba488 | |
parent | 1020ea33efc1bdfe98984808bf883cfee076d437 (diff) |
Start porting sweets code
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | sugar_network/sweets/__init__.py | 41 | ||||
-rw-r--r-- | sugar_network/sweets/config.py | 19 | ||||
-rw-r--r-- | sugar_network/sweets/feeds.py | 156 | ||||
m--------- | sugar_network/sweets/lib/zeroinstall-injector | 0 | ||||
-rw-r--r-- | sugar_network/sweets/solution.py | 146 | ||||
-rw-r--r-- | sugar_network/sweets/solver.py | 51 |
8 files changed, 417 insertions, 0 deletions
@@ -6,3 +6,4 @@ restful_document sugar_network_server sugar_stats_server tmp +sugar_network/sweets/lib/zeroinstall diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9869d59 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sugar_network/sweets/lib/zeroinstall-injector"] + path = sugar_network/sweets/lib/zeroinstall-injector + url = git://git.sugarlabs.org/0sugar/zeroinstall-injector.git diff --git a/sugar_network/sweets/__init__.py b/sugar_network/sweets/__init__.py new file mode 100644 index 0000000..d32efe0 --- /dev/null +++ b/sugar_network/sweets/__init__.py @@ -0,0 +1,41 @@ +# Copyright (C) 2010-2012, Aleksey Lim +# +# 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 Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import sys +from os.path import join, abspath, dirname +sys.path.insert(0, join(abspath(dirname(__file__)), 'lib')) + +from zeroinstall.injector.requirements import Requirements + +from sugar_network.sweets.solver import solve + + +def _inject_sweets(): + from zeroinstall.injector import reader, model + from sugar_network.sweets import feeds + from sugar_network.util import enforce + + def Interface_init(self, url): + enforce(url) + self.uri = url + self.reset() + + model.Interface.__init__ = Interface_init + reader.load_feed_from_cache = \ + lambda url, * args, ** kwargs: feeds.load(url) + reader.check_readable = lambda * args, ** kwargs: True + + +_inject_sweets() diff --git a/sugar_network/sweets/config.py b/sugar_network/sweets/config.py new file mode 100644 index 0000000..6301f3d --- /dev/null +++ b/sugar_network/sweets/config.py @@ -0,0 +1,19 @@ +# Copyright (C) 2011-2012, Aleksey Lim +# +# 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 Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from zeroinstall.injector import config as injector_config + + +config = injector_config.Config() diff --git a/sugar_network/sweets/feeds.py b/sugar_network/sweets/feeds.py new file mode 100644 index 0000000..ce04f0e --- /dev/null +++ b/sugar_network/sweets/feeds.py @@ -0,0 +1,156 @@ +# Copyright (C) 2011-2012, Aleksey Lim +# +# 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 Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import re +from gettext import gettext as _ + +from zeroinstall.injector import model + +import sugar_network as client +from sugar_network.util import enforce + + +_VERSION_RE = re.compile('(\s*(>=|<|=)\s*([.0-9]*[0-9]))') + + +def load(context): + feed = _Feed(context) + + for src in client.Implementation.find(context=context): + enforce(src['stability'] in model.stability_levels, + _('Unknown stability "%s" for %s implementation'), + src['stability'], src['guid']) + + stability = model.stability_levels[src['stability']] + requires = [] + + for guid, props in src['feed'].get('requires', {}).items(): + dep = _Dependency(guid, + props.get('importance', model.Dependency.Essential)) + dep.restrictions.extend( + _parse_version(props.get('constraints') or '')) + requires.append(dep) + + impl_id = src['guid'] + impl = model.ZeroInstallImplementation(feed, impl_id, None) + impl.version = model.parse_version(src['version']) + impl.released = src['date'] + impl.arch = '*-*' + impl.upstream_stability = stability + impl.commands['run'] = _Command() + impl.requires.extend(requires) + impl.add_download_source(impl_id, 0, None) + + feed.implementations[impl_id] = impl + + return feed + + +def _parse_version(args): + result = [] + line = ''.join(args) + + while line: + match = _VERSION_RE.match(line) + if match is None: + break + word, relation, version = match.groups() + line = line[len(word):] + if relation == '>=': + before = None + not_before = version + elif relation == '<': + before = version + not_before = None + elif relation == '=': + not_before = version + parts = version.split('.') + before = '.'.join(parts[:-1] + [str(int(parts[-1]) + 1)]) + else: + continue + result.append(model.VersionRangeRestriction( + not_before=model.parse_version(not_before), + before=model.parse_version(before))) + + return result + + +class _Feed(model.ZeroInstallFeed): + + def __init__(self, context): + self.local_path = None + self.implementations = {} + self.name = context + self.summaries = {} # { lang: str } + self.first_summary = context + self.descriptions = {} # { lang: str } + self.first_description = None + self.last_modified = None + self.feeds = [] + self.feed_for = set([context]) + self.metadata = [] + self.last_checked = None + self._package_implementations = [] + self.url = context + + +class _Dependency(model.InterfaceDependency): + + def __init__(self, guid, importance): + self._importance = importance + self._metadata = {} + self.qdom = None + self.interface = guid + self.restrictions = [] + self.bindings = [] + + @property + def metadata(self): + return self._metadata + + @property + def importance(self): + return self._importance + + def get_required_commands(self): + return [] + + @property + def command(self): + pass + + +class _Command(model.Command): + + def __init__(self): + self.qdom = None + + @property + def path(self): + return '' + + @property + def requires(self): + return [] + + def get_runner(self): + pass + + def __str__(self): + return '' + + @property + def bindings(self): + return [] diff --git a/sugar_network/sweets/lib/zeroinstall-injector b/sugar_network/sweets/lib/zeroinstall-injector new file mode 160000 +Subproject d3f4b21c7ba039e999daf164c2c6d346f77f7ce diff --git a/sugar_network/sweets/solution.py b/sugar_network/sweets/solution.py new file mode 100644 index 0000000..aa2f0ff --- /dev/null +++ b/sugar_network/sweets/solution.py @@ -0,0 +1,146 @@ +# Copyright (C) 2011-2012, Aleksey Lim +# +# 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 Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +DEEP = 2 + + +class Solution(object): + + interface = property(lambda self: self.value.interface) + selections = property(lambda self: self.value.selections) + + def __init__(self, value, req): + self.value = value + self.ready = True + self.details = {} + self.failure_reason = None + self.requirements = req + self._for_build = False#hooks.pkg_type != 'runtime' + + def __getitem__(self, url): + result = self.selections.get(url) + if result is not None: + return _Selection(result) + else: + return None + + def __iter__(self): + for i in self.selections.values(): + yield _Selection(i) + + def __hash__(self): + return hash(self.id) + + def __cmp__(self, other): + return cmp(self.id, other.id) + + @property + def id(self): + return (self.interface, tuple([i.path for i in self.commands])) + + @property + def top(self): + return self[self.interface] + + @property + def commands(self): + if self.top is None: + return [] + else: + return self.value.commands + + def walk(self, reverse=False, depth=DEEP, uniq=True, include_top=True): + done = set() + + def process_node(url, parent_dep, extra_deps, path): + if uniq: + if url in done: + return + done.add(url) + + sel = self[url] + if sel is None: + yield _Selection(None, url), parent_dep, path + return + + if reverse: + if include_top or url != self.interface: + yield sel, parent_dep, path + + if _is_shallow(len(path) + 1, depth): + for dep in sel.dependencies + extra_deps: + for i in process_node( + dep.interface, dep, [], path + [sel]): + yield i + + if not reverse: + if include_top or url != self.interface: + yield sel, parent_dep, path + + extra_deps = [] + for i in self.commands: + extra_deps += i.requires + + return process_node(self.interface, None, extra_deps, []) + + +class _Selection(object): + + def __init__(self, orig, interface=None): + self._value = orig + self._installed = None + self._to_install = None + self._interface = interface + + def __repr__(self): + return self.interface + + @property + def interface(self): + if self._value is None: + return self._interface + else: + return self['interface'] + + @property + def bindings(self): + return self._value.bindings + + @property + def dependencies(self): + return self._value.dependencies + + def __contains__(self, key): + return key in self._value.attrs + + def __getitem__(self, key): + return self._value.attrs.get(key) + + def __setitem__(self, key, value): + self._value.attrs[key] = value + + def __hash__(self): + return hash(self['id']) + + def __cmp__(self, other): + return cmp(self['id'], other['id']) + + +def _is_shallow(node_depth, max_depth=None): + if max_depth is None: + max_depth = DEEP + max_depth = max(0, max_depth) + return node_depth <= max_depth or max_depth >= DEEP diff --git a/sugar_network/sweets/solver.py b/sugar_network/sweets/solver.py new file mode 100644 index 0000000..7891eef --- /dev/null +++ b/sugar_network/sweets/solver.py @@ -0,0 +1,51 @@ +# Copyright (C) 2011-2012, Aleksey Lim +# +# 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 Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import logging +from gettext import gettext as _ + +from zeroinstall.injector import model +from zeroinstall.injector.driver import Driver + +from sugar_network.sweets.config import config +from sugar_network.sweets.solution import Solution + + +_logger = logging.getLogger('sweets') + + +def solve(req, record_details=False): + driver = Driver(config=config, requirements=req) + driver.solver.record_details = record_details + + driver.solver.solve(req.interface_uri, + driver.target_arch, command_name=req.command) + + result = Solution(driver.solver.selections, req) + result.details = dict((k.uri, v) \ + for k, v in (driver.solver.details or {}).items()) + result.ready = driver.solver.ready + + if not result.ready: + # pylint: disable-msg=W0212 + failure_reason = driver.solver._failure_reason + if not failure_reason: + missed_ifaces = [iface.uri for iface, impl in \ + driver.solver.selections.items() if impl is None] + failure_reason = _('Cannot find requireed implementations ' \ + 'for %s') % ', '.join(missed_ifaces) + result.failure_reason = model.SafeException(failure_reason) + + return result |