diff options
Diffstat (limited to 'sugar_network')
-rw-r--r-- | sugar_network/model/__init__.py | 26 | ||||
-rw-r--r-- | sugar_network/node/model.py | 37 | ||||
-rw-r--r-- | sugar_network/node/routes.py | 5 | ||||
-rw-r--r-- | sugar_network/toolkit/spec.py | 135 |
4 files changed, 60 insertions, 143 deletions
diff --git a/sugar_network/model/__init__.py b/sugar_network/model/__init__.py index f7be261..5b7a245 100644 --- a/sugar_network/model/__init__.py +++ b/sugar_network/model/__init__.py @@ -83,15 +83,15 @@ class Release(object): __, release = load_bundle( blobs.post(release, this.request.content_type), context=this.request.guid) - return release['spec']['*-*']['bundle'], release + return release['bundles']['*-*']['blob'], release def teardown(self, release): if this.resource.exists and \ 'activity' not in this.resource['type'] and \ 'book' not in this.resource['type']: return - for spec in release['spec'].values(): - blobs.delete(spec['bundle']) + for bundle in release['bundles'].values(): + blobs.delete(bundle['blob']) def encode(self, value): return [] @@ -147,9 +147,11 @@ def load_bundle(blob, context=None, initial=False, extra_deps=None): release['license'] = this.request['license'] if isinstance(release['license'], basestring): release['license'] = [release['license']] - release['spec'] = {'*-*': { - 'bundle': blob.digest, - }} + release['bundles'] = { + '*-*': { + 'bundle': blob.digest, + }, + } else: context_type = 'activity' unpack_size = 0 @@ -177,12 +179,14 @@ def load_bundle(blob, context=None, initial=False, extra_deps=None): release['stability'] = spec['stability'] if spec['license'] is not EMPTY_LICENSE: release['license'] = spec['license'] - release['commands'] = spec.commands + release['command'] = spec.command release['requires'] = spec.requires - release['spec'] = {'*-*': { - 'bundle': blob.digest, - }} - release['unpack_size'] = unpack_size + release['bundles'] = { + '*-*': { + 'blob': blob.digest, + 'unpack_size': unpack_size, + }, + } blob['content-type'] = 'application/vnd.olpc-sugar' enforce(context, http.BadRequest, 'Context is not specified') diff --git a/sugar_network/node/model.py b/sugar_network/node/model.py index 7276b75..9ead01c 100644 --- a/sugar_network/node/model.py +++ b/sugar_network/node/model.py @@ -176,7 +176,7 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, top_stability = stability or ['stable'] if isinstance(top_stability, basestring): top_stability = [top_stability] - top_cond = None + top_cond = [] top_requires = {} if isinstance(requires, basestring): top_requires.update(spec.parse_requires(requires)) @@ -196,15 +196,6 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, _logger.debug('Solve %r lsb_id=%r lsb_release=%r stability=%r requires=%r', top_context.guid, lsb_id, lsb_release, top_stability, top_requires) - def ensure_version(version, cond): - if not cond: - return True - for not_before, before in cond['restrictions']: - if before is not None and version >= before or \ - not_before is not None and version < not_before: - return False - return True - def rate_release(digest, release): return [_STABILITY_RATES.get(release['stability']) or 0, release['version'], @@ -217,7 +208,7 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, for dep, cond in deps.items(): dep_clause = [-v_usage] for v_release in add_context(dep): - if ensure_version(varset[v_release][0], cond): + if spec.ensure(varset[v_release][1]['version'], cond): dep_clause.append(v_release) clauses.append(dep_clause) @@ -242,7 +233,10 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, pkg_lst = alias.get('binary', []) + alias.get('devel', []) if pkg_lst: clause.append(len(varset)) - varset.append((pkg_ver, 'packages', {context.guid: pkg_lst})) + varset.append(( + context.guid, + {'version': pkg_ver, 'packages': pkg_lst}, + )) else: candidates = [] for digest, release in releases.items(): @@ -251,18 +245,17 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, release = release['value'] if release['stability'] not in top_stability or \ context.guid == top_context.guid and \ - not ensure_version(release['version'], top_cond): + not spec.ensure(release['version'], top_cond): continue bisect.insort(candidates, rate_release(digest, release)) for release in reversed(candidates): digest = release[-1] release = releases[digest]['value'] + release_info = {'version': release['version'], 'blob': digest} + if context.guid == top_context.guid: + release_info['command'] = release['command'] v_release = len(varset) - varset.append(( - release['version'], - 'files', - {context.guid: digest}, - )) + varset.append((context.guid, release_info)) clause.append(v_release) add_deps(context, v_release, release.get('requires') or {}) @@ -283,13 +276,7 @@ def solve(volume, top_context, lsb_id=None, lsb_release=None, if not top_context.guid in result: _logger.debug('No top versions for %r', top_context.guid) return None - - solution = {'files': {}, 'packages': {}} - for v in result.values(): - __, key, items = varset[v] - solution[key].update(items) - top_release = top_context['releases'][solution['files'][top_context.guid]] - solution['commands'] = top_release['value']['commands'] + solution = dict([varset[i] for i in result.values()]) _logger.debug('Solution for %r: %r', top_context.guid, solution) diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py index 66b7823..6d5d200 100644 --- a/sugar_network/node/routes.py +++ b/sugar_network/node/routes.py @@ -27,8 +27,7 @@ from sugar_network.node import stats_user, model # pylint: disable-msg=W0611 from sugar_network.toolkit.router import route, preroute, postroute, ACL from sugar_network.toolkit.router import Unauthorized, Request, fallbackroute -from sugar_network.toolkit.spec import parse_requires, ensure_requires -from sugar_network.toolkit.spec import parse_version +from sugar_network.toolkit.spec import parse_requires, parse_version from sugar_network.toolkit.bundle import Bundle from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import pylru, http, coroutine, exception, enforce @@ -157,7 +156,7 @@ class NodeRoutes(db.Routes, FrontRoutes): arguments={'requires': list}) def get_clone(self, request, response): solution = self.solve(request) - return blobs.get(solution['files'][request.guid]) + return blobs.get(solution[request.guid]['blob']) @route('GET', ['user', None], cmd='stats-info', mime_type='application/json', acl=ACL.AUTH) diff --git a/sugar_network/toolkit/spec.py b/sugar_network/toolkit/spec.py index 78c6007..a8489ce 100644 --- a/sugar_network/toolkit/spec.py +++ b/sugar_network/toolkit/spec.py @@ -15,7 +15,6 @@ import re import os -import sys import logging from os.path import join, exists, dirname from ConfigParser import ConfigParser @@ -45,7 +44,7 @@ _STABILITIES = ('insecure', 'buggy', 'developer', 'testing', 'stable') _POLICY_URL = 'http://wiki.sugarlabs.org/go/Sugar_Network/Policy' _LIST_SEPARATOR = ',' -_RESTRICTION_RE = re.compile('(<|<=|=|>|>=)\\s*([0-9.]+)') +_RESTRICTION_RE = re.compile('(<|<=|=|==|>|>=)\\s*([0-9.]+)') _VERSION_RE = re.compile('-([a-z]*)') _VERSION_MOD_TO_VALUE = { @@ -146,89 +145,47 @@ def format_version(version): return ''.join(version) -def format_next_version(version, deep=True): - """Convert incremented version to string representation. - - Before convertation, the last version's rank will be incremented. - If string value is passed, it will be parsed to procuduce - canonicalized string representation. - - """ - if version is None: - return None - if isinstance(version, basestring): - version = parse_version(version) - if deep: - version[-2] += [1] - else: - version[-2][-1] += 1 - return format_version(version) - - def parse_requires(requires): result = {} for dep_str in _parse_list(requires): - dep = _Dependency() - - if dep_str.startswith('[') and dep_str.endswith(']'): - dep_str = dep_str[1:-1] - dep['importance'] = 'recommended' - parts = _RESTRICTION_RE.split(dep_str) - enforce(parts[0], 'Can parse dependency from "%s" string', dep_str) - + enforce(parts[0], 'Cannot parse %r dependency', dep_str) dep_name = parts.pop(0).strip() - if dep_name in result: - result[dep_name].update(dep) - dep = result[dep_name] - else: - result[dep_name] = dep + dep = result.setdefault(dep_name, []) - not_before = None - before = None while len(parts) >= 3: - if parts[0] == '<': - before = format_version(parts[1]) - elif parts[0] == '<=': - before = format_next_version(parts[1], False) - elif parts[0] == '>': - not_before = format_next_version(parts[1], False) - elif parts[0] == '>=': - not_before = format_version(parts[1]) - elif parts[0] == '=': - not_before = format_version(parts[1]) - before = format_next_version(parts[1], False) + rel = parts[0] + if rel in ('=', '=='): + rel = [0] + elif rel == '<': + rel = [-1] + elif rel == '>': + rel = [1] + elif rel == '<=': + rel = [-1, 0] + elif rel == '>=': + rel = [1, 0] + dep.append((rel, parse_version(parts[1]))) del parts[:3] enforce(not parts or not parts[0].strip(), - 'Cannot parse "%s", it should be in format ' - '"<dependency> (>=|<|=) <version>"', dep_str) - - if before or not_before: - dep.setdefault('restrictions', []) - dep['restrictions'].append((not_before, before)) + 'Cannot parse %r dependency', dep_str) return result -def ensure_requires(to_consider, to_apply): - - def intersect(x, y): - l = max([parse_version(i) for i, __ in (x + y)]) - r = min([[[sys.maxint]] if i is None else parse_version(i) - for __, i in (x + y)]) - return l is None or r is None or l < r - - for name, cond in to_apply.items(): - dep = to_consider.get(name) - if dep is None: - return False - if 'restrictions' not in dep or 'restrictions' not in cond: - continue - if not intersect(dep['restrictions'], cond['restrictions']): - return False - +def ensure(version, cond): + if cond: + for op, cond_version in cond: + if op == [0]: + # Make `version` the same length as `cond_version` + if len(version) > len(cond_version): + version = version[:len(cond_version) - 1] + [0] + if len(version[0]) > len(cond_version[0]): + version = [version[0][:len(cond_version[0])], 0] + if cmp(version, cond_version) not in op: + return False return True @@ -236,7 +193,7 @@ class Spec(object): def __init__(self, spec=None, root=None): self.path = None - self.commands = {} + self.command = None self.bindings = set() self.requires = {} self.build_requires = [] @@ -391,14 +348,12 @@ class Spec(object): i.name = '-'.join(i.section.split(':')[1:]) def _new_command(self, section, requires, name): + enforce(self.command is None, 'Only one command is allowed') cmdline = self._get(section, 'exec') enforce(cmdline, 'Option "exec" should exist for [%s] section', section) - command = self.commands[name] = _Command(name, cmdline) - if ':' in section: - command.requires.update(requires) - else: - self.requires.update(requires) + self.command = cmdline + self.requires.update(requires) def _new_activity(self, section, requires): enforce(':' not in section, '[Activity] should be singular') @@ -476,34 +431,6 @@ class _Library(_Section): pass -class _Command(dict): - - def __init__(self, name, cmdline): - dict.__init__(self) - self['exec'] = cmdline - self.name = name - self.requires = {} - - -class _Dependency(dict): - - def versions_range(self): - for not_before, before in self.get('restrictions') or []: - if not_before is None: - continue - i = parse_version(not_before)[0] - yield format_version([i, 0]) - if before is None: - continue - end = parse_version(before)[0] - i = i[:min(len(i), len(end))] - while True: - i[-1] += 1 - if i >= end: - break - yield format_version([i, 0]) - - def _parse_bindings(text): result = set() |