Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2012-03-17 14:09:21 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2012-03-17 14:09:21 (GMT)
commit5c8536a570edc57c0eb6807ca0bb1abb12309251 (patch)
tree276ae8e55eaf2262b8112805bef4b698716ba488
parent1020ea33efc1bdfe98984808bf883cfee076d437 (diff)
Start porting sweets code
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--sugar_network/sweets/__init__.py41
-rw-r--r--sugar_network/sweets/config.py19
-rw-r--r--sugar_network/sweets/feeds.py156
m---------sugar_network/sweets/lib/zeroinstall-injector0
-rw-r--r--sugar_network/sweets/solution.py146
-rw-r--r--sugar_network/sweets/solver.py51
8 files changed, 417 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 66ae5c5..624b0ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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