From 25027e7f15a955b7ae245befb18e94fb982ebb42 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Wed, 18 Jun 2008 12:01:24 +0100 Subject: [PATCH] Added Selections.download_missing Starts of download of any implementations that are missing. Also fetches any required feeds or keys. --- tests/selections.xml | 1 + tests/testdownload.py | 21 +++++++++++++++++- tests/testlaunch.py | 5 +++++ zeroinstall/injector/cli.py | 37 +++++--------------------------- zeroinstall/injector/selections.py | 44 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 33 deletions(-) create mode 100644 tests/selections.xml mode change 100755 => 100644 zeroinstall/injector/cli.py diff --git a/tests/selections.xml b/tests/selections.xml new file mode 100644 index 0000000..234bc0a --- /dev/null +++ b/tests/selections.xml @@ -0,0 +1 @@ + diff --git a/tests/testdownload.py b/tests/testdownload.py index bffefdb..1add471 100755 --- a/tests/testdownload.py +++ b/tests/testdownload.py @@ -7,7 +7,7 @@ from logging import getLogger, DEBUG, INFO, WARN, ERROR sys.path.insert(0, '..') -from zeroinstall.injector import model, autopolicy, gpg, iface_cache, download, reader, trust, handler, background, arch +from zeroinstall.injector import model, autopolicy, gpg, iface_cache, download, reader, trust, handler, background, arch, selections, qdom from zeroinstall.zerostore import Store; Store._add_with_helper = lambda *unused: False from zeroinstall.support import basedir, tasks import data @@ -147,6 +147,25 @@ class TestDownload(BaseTest): cli.main(['--import', 'Hello']) finally: sys.stdout = old_out + + def testSelections(self): + from zeroinstall.injector.cli import _download_missing_selections + root = qdom.parse(file("selections.xml")) + sels = selections.Selections(root) + class Options: dry_run = False + + old_out = sys.stdout + try: + sys.stdout = StringIO() + self.child = server.handle_requests('Hello.xml', '6FCF121BE2390E0B.gpg', 'HelloWorld.tgz') + sys.stdin = Reply("Y\n") + _download_missing_selections(Options(), sels) + path = iface_cache.iface_cache.stores.lookup(sels.selections['http://localhost:8000/Hello.xml'].id) + assert os.path.exists(os.path.join(path, 'HelloWorld', 'main')) + + assert sels.download_missing(iface_cache.iface_cache, None) is None + finally: + sys.stdout = old_out def testAcceptKey(self): old_out = sys.stdout diff --git a/tests/testlaunch.py b/tests/testlaunch.py index ebb26f0..5a8960c 100755 --- a/tests/testlaunch.py +++ b/tests/testlaunch.py @@ -104,6 +104,11 @@ class TestLaunch(BaseTest): self.assertEquals("Would download 'http://foo'\nFinished\n", out) self.assertEquals("", err) + def testOffline(self): + out, err = self.run_0launch(['--offline', 'http://foo']) + self.assertEquals("Can't find all required implementations:\n- -> None\n", err) + self.assertEquals("", out) + def testDisplay(self): os.environ['DISPLAY'] = ':foo' out, err = self.run_0launch(['--dry-run', 'http://foo']) diff --git a/zeroinstall/injector/cli.py b/zeroinstall/injector/cli.py old mode 100755 new mode 100644 index 84afe03..3a4ed6b --- a/zeroinstall/injector/cli.py +++ b/zeroinstall/injector/cli.py @@ -254,42 +254,15 @@ def _fork_gui(iface_uri, gui_args, prog_args, options = None): return helpers.get_selections_gui(iface_uri, gui_args, test_callback) def _download_missing_selections(options, sels): - from zeroinstall.zerostore import NotStored - - # Check that every required selection is cached - needed_downloads = [] - for sel in sels.selections.values(): - iid = sel.id - if not iid.startswith('/'): - try: - iface_cache.stores.lookup(iid) - except NotStored, ex: - logging.info("Not stored: %s", ex) - needed_downloads.append(sel) - if not needed_downloads: - return - - logging.info("Some selected implementations are missing. Attempting to locate them...") - - # We're missing some. For each one, get the feed it came from - # and find the corresponding in that. This will - # tell us where to get it from. - needed_impls = [] - for sel in needed_downloads: - feed_url = sel.attrs.get('from-feed', None) or sel.attrs['interface'] - feed = iface_cache.get_feed(feed_url) - # TODO: we should download missing feeds too - impl = feed.implementations[sel.id] - needed_impls.append(impl) - from zeroinstall.injector import fetch from zeroinstall.injector.handler import Handler handler = Handler(dry_run = options.dry_run) fetcher = fetch.Fetcher(handler) - blocker = fetcher.download_impls(needed_impls, iface_cache.stores) - logging.info("Waiting for selected implementations to be downloaded...") - handler.wait_for_blocker(blocker) - + blocker = sels.download_missing(iface_cache, fetcher) + if blocker: + logging.info("Waiting for selected implementations to be downloaded...") + handler.wait_for_blocker(blocker) + def _get_selections(policy): import selections doc = selections.Selections(policy).toDOM() diff --git a/zeroinstall/injector/selections.py b/zeroinstall/injector/selections.py index 893137a..39f5b77 100644 --- a/zeroinstall/injector/selections.py +++ b/zeroinstall/injector/selections.py @@ -12,6 +12,7 @@ from zeroinstall.injector.policy import Policy from zeroinstall.injector.model import EnvironmentBinding, InterfaceDependency, process_binding, process_depends, binding_names from zeroinstall.injector.namespaces import XMLNS_IFACE from zeroinstall.injector.qdom import Element +from zeroinstall.support import tasks class Selection(object): """A single selected implementation in a L{Selections} set. @@ -54,6 +55,10 @@ class Selections(object): __slots__ = ['interface', 'selections'] def __init__(self, source): + """Constructor. + @param source: a map of implementations, policy or selections document + @type source: {str: L{Selection}} | L{Policy} | L{Element} + """ if isinstance(source, dict): self.selections = source elif isinstance(source, Policy): @@ -181,3 +186,42 @@ class Selections(object): def __repr__(self): return "Selections for " + self.interface + + def download_missing(self, iface_cache, fetcher): + """Cache all selected implementations are available. + Download any that are not present. + @param iface_cache: cache to find feeds with download information + @param fetcher: used to download missing implementations + @return: a L{tasks.Blocker} or None""" + from zeroinstall.zerostore import NotStored + + # Check that every required selection is cached + needed_downloads = [] + for sel in self.selections.values(): + iid = sel.id + if not iid.startswith('/'): + try: + iface_cache.stores.lookup(iid) + except NotStored, ex: + needed_downloads.append(sel) + if not needed_downloads: + return + + @tasks.async + def download(): + # We're missing some. For each one, get the feed it came from + # and find the corresponding in that. This will + # tell us where to get it from. + needed_impls = [] + for sel in needed_downloads: + feed_url = sel.attrs.get('from-feed', None) or sel.attrs['interface'] + feed = iface_cache.get_feed(feed_url) + if feed is None or self.id not in feed.implementations: + yield fetcher.download_and_import_feed(feed_url, iface_cache) + feed = iface_cache.get_feed(feed_url) + assert feed, "Failed to get feed for %s" % feed_url + impl = feed.implementations[sel.id] + needed_impls.append(impl) + + yield fetcher.download_impls(needed_impls, iface_cache.stores) + return download() -- 2.11.4.GIT