Example #1
0
	def testNoNeedDl(self):
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		assert driver.need_download()

		driver = Driver(requirements = Requirements(os.path.abspath('Foo.xml')), config = self.config)
		assert not driver.need_download()
		assert driver.solver.ready
	def testLocalPath(self):
		# 0launch --get-selections Local.xml
		iface = os.path.join(mydir, "Local.xml")
		driver = Driver(requirements = Requirements(iface), config = self.config)
		driver.need_download()
		assert driver.solver.ready
		s1 = driver.solver.selections
		xml = s1.toDOM().toxml("utf-8")

		# Reload selections and check they're the same
		root = qdom.parse(BytesIO(xml))
		s2 = selections.Selections(root)
		local_path = s2.selections[iface].local_path
		assert os.path.isdir(local_path), local_path
		assert not s2.selections[iface].digests, s2.selections[iface].digests

		# Add a newer implementation and try again
		feed = self.config.iface_cache.get_feed(iface)
		impl = model.ZeroInstallImplementation(feed, "foo bar=123", local_path = None)
		impl.version = model.parse_version('1.0')
		impl.commands["run"] = model.Command(qdom.Element(namespaces.XMLNS_IFACE, 'command', {'path': 'dummy', 'name': 'run'}), None)
		impl.add_download_source('http://localhost/bar.tgz', 1000, None)
		feed.implementations = {impl.id: impl}
		assert driver.need_download()
		assert driver.solver.ready, driver.solver.get_failure_reason()
		s1 = driver.solver.selections
		xml = s1.toDOM().toxml("utf-8")
		root = qdom.parse(BytesIO(xml))
		s2 = selections.Selections(root)
		xml = s2.toDOM().toxml("utf-8")
		qdom.parse(BytesIO(xml))
		assert s2.selections[iface].local_path is None
		assert not s2.selections[iface].digests, s2.selections[iface].digests
		assert s2.selections[iface].id == 'foo bar=123'
Example #3
0
    def testDLfeed(self):
        self.cache_iface(
            foo_iface_uri,
            """<?xml version="1.0" ?>
<interface last-modified="1110752708"
 uri="%s"
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <feed src='http://example.com'/>
</interface>"""
            % foo_iface_uri,
        )
        driver = Driver(requirements=Requirements(foo_iface_uri), config=self.config)
        self.config.network_use = model.network_full

        assert driver.need_download()

        feed = self.config.iface_cache.get_feed(foo_iface_uri)
        feed.feeds = [model.Feed("/BadFeed", None, False)]

        logger.setLevel(logging.ERROR)
        assert driver.need_download()  # Triggers warning
        logger.setLevel(logging.WARN)
Example #4
0
	def testNoNeedDl(self):
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		assert driver.need_download()

		driver = Driver(requirements = Requirements(os.path.abspath('Foo.xml')), config = self.config)
		assert not driver.need_download()
		assert driver.solver.ready
Example #5
0
    def testLocalArchive(self):
        local_iface = os.path.join(mydir, 'LocalArchive.xml')
        with open(local_iface, 'rb') as stream:
            root = qdom.parse(stream)

        # Not local => error
        feed = model.ZeroInstallFeed(root)
        impl = feed.implementations['impl1']
        blocker = self.config.fetcher.download_impls([impl],
                                                     self.config.stores)
        try:
            tasks.wait_for_blocker(blocker)
            assert 0
        except model.SafeException as ex:
            assert "Relative URL 'HelloWorld.tgz' in non-local feed" in str(
                ex), ex

        feed = model.ZeroInstallFeed(root, local_path=local_iface)

        # Missing file
        impl2 = feed.implementations['impl2']
        blocker = self.config.fetcher.download_impls([impl2],
                                                     self.config.stores)
        try:
            tasks.wait_for_blocker(blocker)
            assert 0
        except model.SafeException as ex:
            assert 'tests/IDONTEXIST.tgz' in str(ex), ex

        # Wrong size
        impl3 = feed.implementations['impl3']
        blocker = self.config.fetcher.download_impls([impl3],
                                                     self.config.stores)
        try:
            tasks.wait_for_blocker(blocker)
            assert 0
        except model.SafeException as ex:
            assert 'feed says 177, but actually 176 bytes' in str(ex), ex

        self.config.network_use = model.network_offline
        r = Requirements(local_iface)
        r.command = None
        driver = Driver(requirements=r, config=self.config)
        driver.need_download()
        assert driver.solver.ready, driver.solver.get_failure_reason()

        # Local => OK
        impl = feed.implementations['impl1']

        path = self.config.stores.lookup_maybe(impl.digests)
        assert not path

        blocker = self.config.fetcher.download_impls([impl],
                                                     self.config.stores)
        tasks.wait_for_blocker(blocker)

        path = self.config.stores.lookup_any(impl.digests)
        assert os.path.exists(os.path.join(path, 'HelloWorld'))
Example #6
0
def ensure_cached(uri, command = 'run', config = None):
	"""Ensure that an implementation of uri is cached.
	If not, it downloads one. It uses the GUI if a display is
	available, or the console otherwise.
	@param uri: the required interface
	@type uri: str
	@return: the selected implementations, or None if the user cancelled
	@rtype: L{zeroinstall.injector.selections.Selections}
	"""
	from zeroinstall.injector.driver import Driver

	if config is None:
		from zeroinstall.injector.config import load_config
		config = load_config()

	from zeroinstall.injector.requirements import Requirements
	requirements = Requirements(uri)
	requirements.command = command

	d = Driver(config, requirements)

	if d.need_download() or not d.solver.ready:
		sels = get_selections_gui(uri, ['--command', command], use_gui = None)
		if sels != DontUseGUI:
			return sels
		done = d.solve_and_download_impls()
		tasks.wait_for_blocker(done)

	return d.solver.selections
Example #7
0
def ensure_cached(uri, command = 'run', config = None):
	"""Ensure that an implementation of uri is cached.
	If not, it downloads one. It uses the GUI if a display is
	available, or the console otherwise.
	@param uri: the required interface
	@type uri: str
	@return: the selected implementations, or None if the user cancelled
	@rtype: L{zeroinstall.injector.selections.Selections}
	"""
	from zeroinstall.injector.driver import Driver

	if config is None:
		from zeroinstall.injector.config import load_config
		config = load_config()

	from zeroinstall.injector.requirements import Requirements
	requirements = Requirements(uri)
	requirements.command = command

	d = Driver(config, requirements)

	if d.need_download() or not d.solver.ready:
		sels = get_selections_gui(uri, ['--command', command], use_gui = None)
		if sels != DontUseGUI:
			return sels
		done = d.solve_and_download_impls()
		tasks.wait_for_blocker(done)

	return d.solver.selections
Example #8
0
	def testDistro(self):
		with output_suppressed():
			native_url = 'http://example.com:8000/Native.xml'

			# Initially, we don't have the feed at all...
			master_feed = self.config.iface_cache.get_feed(native_url)
			assert master_feed is None, master_feed

			trust.trust_db.trust_key('DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000')
			run_server('Native.xml', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B')
			driver = Driver(requirements = Requirements(native_url), config = self.config)
			assert driver.need_download()

			solve = driver.solve_with_downloads()
			tasks.wait_for_blocker(solve)
			tasks.check(solve)

			master_feed = self.config.iface_cache.get_feed(native_url)
			assert master_feed is not None
			assert master_feed.implementations == {}

			distro_feed_url = master_feed.get_distro_feed()
			assert distro_feed_url is not None
			distro_feed = self.config.iface_cache.get_feed(distro_feed_url)
			assert distro_feed is not None
			assert len(distro_feed.implementations) == 2, distro_feed.implementations
Example #9
0
    def testDistro(self):
        with output_suppressed():
            native_url = 'http://example.com:8000/Native.xml'

            # Initially, we don't have the feed at all...
            master_feed = self.config.iface_cache.get_feed(native_url)
            assert master_feed is None, master_feed

            trust.trust_db.trust_key(
                'DE937DD411906ACF7C263B396FCF121BE2390E0B', 'example.com:8000')
            run_server(
                'Native.xml', '6FCF121BE2390E0B.gpg',
                '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B')
            driver = Driver(requirements=Requirements(native_url),
                            config=self.config)
            assert driver.need_download()

            solve = driver.solve_with_downloads()
            tasks.wait_for_blocker(solve)
            tasks.check(solve)

            master_feed = self.config.iface_cache.get_feed(native_url)
            assert master_feed is not None
            assert master_feed.implementations == {}

            distro_feed_url = master_feed.get_distro_feed()
            assert distro_feed_url is not None
            distro_feed = self.config.iface_cache.get_feed(distro_feed_url)
            assert distro_feed is not None
            assert len(
                distro_feed.implementations) == 2, distro_feed.implementations
Example #10
0
    def testCommands(self):
        iface = os.path.join(mydir, "Command.xml")
        driver = Driver(requirements=Requirements(iface), config=self.config)
        driver.need_download()
        assert driver.solver.ready

        impl = driver.solver.selections[self.config.iface_cache.get_interface(
            iface)]
        assert impl.id == 'c'
        assert impl.main == 'test-gui'

        dep_impl_uri = impl.commands['run'].requires[0].interface
        dep_impl = driver.solver.selections[
            self.config.iface_cache.get_interface(dep_impl_uri)]
        assert dep_impl.id == 'sha1=256'

        s1 = driver.solver.selections
        assert s1.commands[0].path == 'test-gui'
        xml = s1.toDOM().toxml("utf-8")
        root = qdom.parse(BytesIO(xml))
        s2 = selections.Selections(root)

        assert s2.commands[0].path == 'test-gui'
        impl = s2.selections[iface]
        assert impl.id == 'c'

        assert s2.commands[0].qdom.attrs['http://custom attr'] == 'namespaced'
        custom_element = s2.commands[0].qdom.childNodes[0]
        assert custom_element.name == 'child'

        dep_impl = s2.selections[dep_impl_uri]
        assert dep_impl.id == 'sha1=256'

        d = Driver(self.config, requirements.Requirements(runexec))
        need_download = d.need_download()
        assert need_download == False

        xml = d.solver.selections.toDOM().toxml("utf-8")
        root = qdom.parse(BytesIO(xml))
        s3 = selections.Selections(root)
        runnable_impl = s3.selections[runnable]
        assert 'foo' in runnable_impl.commands
        assert 'run' in runnable_impl.commands
	def testCommands(self):
		iface = os.path.join(mydir, "Command.xml")
		driver = Driver(requirements = Requirements(iface), config = self.config)
		driver.need_download()
		assert driver.solver.ready

		impl = driver.solver.selections[self.config.iface_cache.get_interface(iface)]
		assert impl.id == 'c'
		assert impl.main == 'test-gui'

		dep_impl_uri = impl.commands['run'].requires[0].interface
		dep_impl = driver.solver.selections[self.config.iface_cache.get_interface(dep_impl_uri)]
		assert dep_impl.id == 'sha1=256'

		s1 = driver.solver.selections
		assert s1.commands[0].path == 'test-gui'
		xml = s1.toDOM().toxml("utf-8")
		root = qdom.parse(BytesIO(xml))
		s2 = selections.Selections(root)

		assert s2.commands[0].path == 'test-gui'
		impl = s2.selections[iface]
		assert impl.id == 'c'

		assert s2.commands[0].qdom.attrs['http://custom attr'] == 'namespaced'
		custom_element = s2.commands[0].qdom.childNodes[0]
		assert custom_element.name == 'child'

		dep_impl = s2.selections[dep_impl_uri]
		assert dep_impl.id == 'sha1=256'

		d = Driver(self.config, requirements.Requirements(runexec))
		need_download = d.need_download()
		assert need_download == False

		xml = d.solver.selections.toDOM().toxml("utf-8")
		root = qdom.parse(BytesIO(xml))
		s3 = selections.Selections(root)
		runnable_impl = s3.selections[runnable]
		assert 'foo' in runnable_impl.commands
		assert 'run' in runnable_impl.commands
Example #12
0
	def testAcceptKey(self):
		with output_suppressed():
			run_server('Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz')
			driver = Driver(requirements = Requirements('http://localhost:8000/Hello'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("Y\n")
			try:
				download_and_execute(driver, ['Hello'], main = 'Missing')
				assert 0
			except model.SafeException as ex:
				if "HelloWorld/Missing" not in str(ex):
					raise
Example #13
0
	def testAcceptKey(self):
		with output_suppressed():
			run_server('Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz')
			driver = Driver(requirements = Requirements('http://localhost:8000/Hello'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("Y\n")
			try:
				download_and_execute(driver, ['Hello'], main = 'Missing')
				assert 0
			except model.SafeException as ex:
				if "HelloWorld/Missing" not in str(ex):
					raise
Example #14
0
    def testLocalPath(self):
        # 0launch --get-selections Local.xml
        iface = os.path.join(mydir, "Local.xml")
        driver = Driver(requirements=Requirements(iface), config=self.config)
        driver.need_download()
        assert driver.solver.ready
        s1 = driver.solver.selections
        xml = s1.toDOM().toxml("utf-8")

        # Reload selections and check they're the same
        root = qdom.parse(BytesIO(xml))
        s2 = selections.Selections(root)
        local_path = s2.selections[iface].local_path
        assert os.path.isdir(local_path), local_path
        assert not s2.selections[iface].digests, s2.selections[iface].digests

        # Add a newer implementation and try again
        feed = self.config.iface_cache.get_feed(iface)
        impl = model.ZeroInstallImplementation(feed,
                                               "foo bar=123",
                                               local_path=None)
        impl.version = model.parse_version('1.0')
        impl.commands["run"] = model.Command(
            qdom.Element(namespaces.XMLNS_IFACE, 'command', {
                'path': 'dummy',
                'name': 'run'
            }), None)
        impl.add_download_source('http://localhost/bar.tgz', 1000, None)
        feed.implementations = {impl.id: impl}
        assert driver.need_download()
        assert driver.solver.ready, driver.solver.get_failure_reason()
        s1 = driver.solver.selections
        xml = s1.toDOM().toxml("utf-8")
        root = qdom.parse(BytesIO(xml))
        s2 = selections.Selections(root)
        xml = s2.toDOM().toxml("utf-8")
        qdom.parse(BytesIO(xml))
        assert s2.selections[iface].local_path is None
        assert not s2.selections[iface].digests, s2.selections[iface].digests
        assert s2.selections[iface].id == 'foo bar=123'
Example #15
0
	def testDLfeed(self):
		self.cache_iface(foo_iface_uri,
"""<?xml version="1.0" ?>
<interface last-modified="1110752708"
 uri="%s"
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <feed src='http://example.com'/>
</interface>""" % foo_iface_uri)
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		self.config.network_use = model.network_full

		assert driver.need_download()

		feed = self.config.iface_cache.get_feed(foo_iface_uri)
		feed.feeds = [model.Feed('/BadFeed', None, False)]

		logger.setLevel(logging.ERROR)
		assert driver.need_download()	# Triggers warning
		logger.setLevel(logging.WARN)
Example #16
0
	def testDryRun(self):
		with output_suppressed():
			run_server('Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz')
			self.config.handler.dry_run = True
			driver = Driver(requirements = Requirements('http://localhost:8000/Hello'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("Y\n")
			sys.stdout = StringIO()
			download_and_execute(driver, ['Hello'], main = 'Missing', dry_run = True)

			out = sys.stdout.getvalue()
			assert '[dry-run] would trust key DE937DD411906ACF7C263B396FCF121BE2390E0B for localhost:8000' in out, out
			assert '[dry-run] would cache feed http://localhost:8000/Hello as ' in out, out
			assert '[dry-run] would store implementation as ' in out, out
			assert '[dry-run] would execute:' in out, out
Example #17
0
	def testRejectKeyXML(self):
		with output_suppressed():
			run_server('Hello.xml', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B')
			driver = Driver(requirements = Requirements('http://example.com:8000/Hello.xml'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("N\n")
			try:
				download_and_execute(driver, ['Hello'])
				assert 0
			except model.SafeException as ex:
				if "has no usable implementations" not in str(ex):
					raise ex
				if "Not signed with a trusted key" not in str(self.config.handler.ex):
					raise
				self.config.handler.ex = None
Example #18
0
	def testDryRun(self):
		with output_suppressed():
			run_server('Hello', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B', 'HelloWorld.tgz')
			self.config.handler.dry_run = True
			driver = Driver(requirements = Requirements('http://localhost:8000/Hello'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("Y\n")
			sys.stdout = StringIO()
			download_and_execute(driver, ['Hello'], main = 'Missing', dry_run = True)

			out = sys.stdout.getvalue()
			assert '[dry-run] would trust key DE937DD411906ACF7C263B396FCF121BE2390E0B for localhost:8000' in out, out
			assert '[dry-run] would cache feed http://localhost:8000/Hello as ' in out, out
			assert '[dry-run] would store implementation as ' in out, out
			assert '[dry-run] would execute:' in out, out
Example #19
0
	def testRejectKeyXML(self):
		with output_suppressed():
			run_server('Hello.xml', '6FCF121BE2390E0B.gpg', '/key-info/key/DE937DD411906ACF7C263B396FCF121BE2390E0B')
			driver = Driver(requirements = Requirements('http://example.com:8000/Hello.xml'), config = self.config)
			assert driver.need_download()
			sys.stdin = Reply("N\n")
			try:
				download_and_execute(driver, ['Hello'])
				assert 0
			except model.SafeException as ex:
				if "No known implementations at all" not in str(ex):
					raise ex
				if "Not signed with a trusted key" not in str(self.config.handler.ex):
					raise
				self.config.handler.ex = None
Example #20
0
	def testNeedDL(self):
		self.cache_iface(foo_iface_uri,
"""<?xml version="1.0" ?>
<interface last-modified="0"
 uri="%s"
 main='ThisBetterNotExist'
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <implementation version='1.0' id='sha1=123'>
    <archive href='http://foo/foo.tgz' size='100'/>
  </implementation>
</interface>""" % foo_iface_uri)
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		self.config.network_use = model.network_full
		recalculate(driver)
		assert driver.need_download()
		assert driver.solver.ready
Example #21
0
def _check_for_updates(requirements, verbose):
	from zeroinstall.injector.driver import Driver
	from zeroinstall.injector.config import load_config

	background_handler = BackgroundHandler(requirements.interface_uri, requirements.interface_uri)
	background_config = load_config(background_handler)
	root_iface = background_config.iface_cache.get_interface(requirements.interface_uri).get_name()
	background_handler.title = root_iface

	driver = Driver(config = background_config, requirements = requirements)

	info(_("Checking for updates to '%s' in a background process"), root_iface)
	if verbose:
		background_handler.notify("Zero Install", _("Checking for updates to '%s'...") % root_iface, timeout = 1)

	network_state = background_handler.get_network_state()
	if network_state not in (_NetworkState.NM_STATE_CONNECTED_SITE, _NetworkState.NM_STATE_CONNECTED_GLOBAL):
		info(_("Not yet connected to network (status = %d). Sleeping for a bit..."), network_state)
		import time
		time.sleep(120)
		if network_state in (_NetworkState.NM_STATE_DISCONNECTED, _NetworkState.NM_STATE_ASLEEP):
			info(_("Still not connected to network. Giving up."))
			sys.exit(1)
	else:
		info(_("NetworkManager says we're on-line. Good!"))

	background_config.freshness = 0			# Don't bother trying to refresh when getting the interface
	refresh = driver.solve_with_downloads(force = True)	# (causes confusing log messages)
	tasks.wait_for_blocker(refresh)

	# We could even download the archives here, but for now just
	# update the interfaces.

	if not driver.need_download():
		if verbose:
			background_handler.notify("Zero Install", _("No updates to download."), timeout = 1)
		sys.exit(0)

	background_handler.notify("Zero Install",
			      _("Updates ready to download for '%s'.") % root_iface,
			      timeout = 1)
	_exec_gui(requirements.interface_uri, '--refresh', '--systray')
	sys.exit(1)
Example #22
0
	def testNeedDL(self):
		self.cache_iface(foo_iface_uri,
"""<?xml version="1.0" ?>
<interface last-modified="0"
 uri="%s"
 main='ThisBetterNotExist'
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <implementation version='1.0' id='sha1=123'>
    <archive href='http://foo/foo.tgz' size='100'/>
  </implementation>
</interface>""" % foo_iface_uri)
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		self.config.network_use = model.network_full
		recalculate(driver)
		assert driver.need_download()
		assert driver.solver.ready
Example #23
0
	def testUnknownAlg(self):
		self.cache_iface(foo_iface_uri,
"""<?xml version="1.0" ?>
<interface
 uri="%s"
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <implementation main='.' id='unknown=123' version='1.0'>
    <archive href='http://foo/foo.tgz' size='100'/>
  </implementation>
</interface>""" % foo_iface_uri)
		self.config.fetcher = fetch.Fetcher(self.config)
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		try:
			assert driver.need_download()
			download_and_execute(driver, [])
		except model.SafeException as ex:
			assert "Use '_' not '=' for new algorithms, in unknown=123" in str(ex), ex
Example #24
0
	def testUnknownAlg(self):
		self.cache_iface(foo_iface_uri,
"""<?xml version="1.0" ?>
<interface
 uri="%s"
 xmlns="http://zero-install.sourceforge.net/2004/injector/interface">
  <name>Foo</name>
  <summary>Foo</summary>
  <description>Foo</description>
  <implementation main='.' id='unknown=123' version='1.0'>
    <archive href='http://foo/foo.tgz' size='100'/>
  </implementation>
</interface>""" % foo_iface_uri)
		self.config.fetcher = fetch.Fetcher(self.config)
		driver = Driver(requirements = Requirements(foo_iface_uri), config = self.config)
		try:
			assert driver.need_download()
			download_and_execute(driver, [])
		except model.SafeException as ex:
			assert "Use '_' not '=' for new algorithms, in unknown=123" in str(ex), ex
Example #25
0
def get_selections(config, options, iface_uri, select_only, download_only, test_callback):
	"""Get selections for iface_uri, according to the options passed.
	Will switch to GUI mode if necessary.
	@param options: options from OptionParser
	@param iface_uri: canonical URI of the interface
	@param select_only: return immediately even if the selected versions aren't cached
	@param download_only: wait for stale feeds, and display GUI button as Download, not Run
	@return: the selected versions, or None if the user cancels
	@rtype: L{selections.Selections} | None
	"""
	if options.offline:
		config.network_use = model.network_offline

	iface_cache = config.iface_cache

	# Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a
	# selections document and we return immediately.
	maybe_selections = iface_cache.get_feed(iface_uri, selections_ok = True)
	if isinstance(maybe_selections, selections.Selections):
		if not select_only:
			blocker = maybe_selections.download_missing(config)
			if blocker:
				logging.info(_("Waiting for selected implementations to be downloaded..."))
				tasks.wait_for_blocker(blocker)
		return maybe_selections

	r = requirements.Requirements(iface_uri)
	r.parse_options(options)

	driver = Driver(config = config, requirements = r)

	# Note that need_download() triggers a solve
	if options.refresh or options.gui:
		# We could run immediately, but the user asked us not to
		can_run_immediately = False
	else:
		if select_only:
			# --select-only: we only care that we've made a selection, not that we've cached the implementations
			driver.need_download()
			can_run_immediately = driver.solver.ready
		else:
			can_run_immediately = not driver.need_download()

		stale_feeds = [feed for feed in driver.solver.feeds_used if
				not os.path.isabs(feed) and			# Ignore local feeds (note: file might be missing too)
				not feed.startswith('distribution:') and	# Ignore (memory-only) PackageKit feeds
				iface_cache.is_stale(iface_cache.get_feed(feed), config.freshness)]

		if download_only and stale_feeds:
			can_run_immediately = False

	if can_run_immediately:
		if stale_feeds:
			if config.network_use == model.network_offline:
				logging.debug(_("No doing background update because we are in off-line mode."))
			else:
				# There are feeds we should update, but we can run without them.
				# Do the update in the background while the program is running.
				from zeroinstall.injector import background
				background.spawn_background_update(driver, options.verbose > 0)
		return driver.solver.selections

	# If the user didn't say whether to use the GUI, choose for them.
	if options.gui is None and os.environ.get('DISPLAY', None):
		options.gui = True
		# If we need to download anything, we might as well
		# refresh all the feeds first.
		options.refresh = True
		logging.info(_("Switching to GUI mode... (use --console to disable)"))

	if options.gui:
		gui_args = driver.requirements.get_as_options()
		if download_only:
			# Just changes the button's label
			gui_args.append('--download-only')
		if options.refresh:
			gui_args.append('--refresh')
		if options.verbose:
			gui_args.insert(0, '--verbose')
			if options.verbose > 1:
				gui_args.insert(0, '--verbose')
		if options.with_store:
			for x in options.with_store:
				gui_args += ['--with-store', x]
		if select_only:
			gui_args.append('--select-only')

		from zeroinstall import helpers
		sels = helpers.get_selections_gui(iface_uri, gui_args, test_callback)

		if not sels:
			return None		# Aborted
	else:
		# Note: --download-only also makes us stop and download stale feeds first.
		downloaded = driver.solve_and_download_impls(refresh = options.refresh or download_only or False,
							     select_only = select_only)
		if downloaded:
			tasks.wait_for_blocker(downloaded)
		sels = driver.solver.selections

	return sels
Example #26
0
    def _check_for_updates(self, sels):
        """Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		We also schedule a background update from time-to-time anyway.
		@return: the selections to use
		@rtype: L{selections.Selections}"""
        need_solve = False  # Rerun solver (cached feeds have changed)
        need_update = False  # Update over the network

        utime = self._get_mtime('last-checked', warn_if_missing=True)
        last_solve = max(self._get_mtime('last-solve', warn_if_missing=False),
                         utime)

        # Ideally, this would return all the files which were inputs into the solver's
        # decision. Currently, we approximate with:
        # - the previously selected feed files (local or cached)
        # - configuration files for the selected interfaces
        # - the global configuration
        # We currently ignore feeds and interfaces which were
        # considered but not selected.
        # Can yield None (ignored), paths or (path, mtime) tuples.
        # If this throws an exception, we will log it and resolve anyway.
        def get_inputs():
            for sel in sels.selections.values():
                logger.info("Checking %s", sel.feed)
                feed = iface_cache.get_feed(sel.feed)
                if not feed:
                    raise IOError("Input %s missing; update" % sel.feed)
                else:
                    if feed.local_path:
                        yield feed.local_path
                    else:
                        yield (feed.url, feed.last_modified)

                # Per-feed configuration
                yield basedir.load_first_config(
                    namespaces.config_site, namespaces.config_prog,
                    'interfaces', model._pretty_escape(sel.interface))

            # Global configuration
            yield basedir.load_first_config(namespaces.config_site,
                                            namespaces.config_prog, 'global')

        # If any of the feeds we used have been updated since the last check, do a quick re-solve
        iface_cache = self.config.iface_cache
        try:
            for item in get_inputs():
                if not item: continue
                if isinstance(item, tuple):
                    path, mtime = item
                else:
                    path = item
                    mtime = os.stat(path).st_mtime

                if mtime and mtime > last_solve:
                    logger.info(
                        "Triggering update to %s because %s has changed", self,
                        path)
                    need_solve = True
                    break
        except Exception as ex:
            logger.info("Error checking modification times: %s", ex)
            need_solve = True
            need_update = True

        # Is it time for a background update anyway?
        if not need_update:
            staleness = time.time() - utime
            logger.info("Staleness of app %s is %d hours", self,
                        staleness / (60 * 60))
            freshness_threshold = self.config.freshness
            if freshness_threshold > 0 and staleness >= freshness_threshold:
                need_update = True

        if need_solve:
            from zeroinstall.injector.driver import Driver
            driver = Driver(config=self.config,
                            requirements=self.get_requirements())
            if driver.need_download():
                # Continue with the current (hopefully cached) selections while we download
                need_update = True
            else:
                old_sels = sels
                sels = driver.solver.selections
                from zeroinstall.support import xmltools
                if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
                    self.set_selections(sels, set_last_checked=False)
            self._touch('last-solve')

        # If we tried to check within the last hour, don't try again.
        if need_update:
            last_check_attempt = self._get_mtime('last-check-attempt',
                                                 warn_if_missing=False)
            if last_check_attempt and last_check_attempt + 60 * 60 > time.time(
            ):
                logger.info(
                    "Tried to check within last hour; not trying again now")
                need_update = False

        if need_update:
            self.set_last_check_attempt()
            from zeroinstall.injector import background
            r = self.get_requirements()
            background.spawn_background_update2(r, False, self)

        return sels
Example #27
0
	def testLocalArchive(self):
		local_iface = os.path.join(mydir, 'LocalArchive.xml')
		with open(local_iface, 'rb') as stream:
			root = qdom.parse(stream)

		# Not local => error
		feed = model.ZeroInstallFeed(root)
		impl = feed.implementations['impl1']
		blocker = self.config.fetcher.download_impls([impl], self.config.stores)
		try:
			tasks.wait_for_blocker(blocker)
			assert 0
		except model.SafeException as ex:
			assert "Relative URL 'HelloWorld.tgz' in non-local feed" in str(ex), ex

		feed = model.ZeroInstallFeed(root, local_path = local_iface)

		# Missing file
		impl2 = feed.implementations['impl2']
		blocker = self.config.fetcher.download_impls([impl2], self.config.stores)
		try:
			tasks.wait_for_blocker(blocker)
			assert 0
		except model.SafeException as ex:
			assert 'tests/IDONTEXIST.tgz' in str(ex), ex

		# Wrong size
		impl3 = feed.implementations['impl3']
		blocker = self.config.fetcher.download_impls([impl3], self.config.stores)
		try:
			tasks.wait_for_blocker(blocker)
			assert 0
		except model.SafeException as ex:
			assert 'feed says 177, but actually 176 bytes' in str(ex), ex

		self.config.network_use = model.network_offline
		r = Requirements(local_iface)
		r.command = None
		driver = Driver(requirements = r, config = self.config)
		driver.need_download()
		assert driver.solver.ready, driver.solver.get_failure_reason()

		# Local => OK
		impl = feed.implementations['impl1']

		path = self.config.stores.lookup_maybe(impl.digests)
		assert not path

		blocker = self.config.fetcher.download_impls([impl], self.config.stores)
		tasks.wait_for_blocker(blocker)

		path = self.config.stores.lookup_any(impl.digests)
		assert os.path.exists(os.path.join(path, 'HelloWorld'))

		# Local <file> => OK
		impl = feed.implementations['impl4']

		path = self.config.stores.lookup_maybe(impl.digests)
		assert not path

		blocker = self.config.fetcher.download_impls([impl], self.config.stores)
		tasks.wait_for_blocker(blocker)

		path = self.config.stores.lookup_any(impl.digests)
		assert os.path.exists(os.path.join(path, 'archive.tgz'))
Example #28
0
    def testSelections(self):
        requirements = Requirements('http://foo/Source.xml')
        requirements.source = True
        requirements.command = 'compile'
        driver = Driver(requirements=requirements, config=self.config)
        source = self.config.iface_cache.get_interface('http://foo/Source.xml')
        compiler = self.config.iface_cache.get_interface(
            'http://foo/Compiler.xml')
        self.import_feed(source.uri, 'Source.xml')
        self.import_feed(compiler.uri, 'Compiler.xml')

        self.config.network_use = model.network_full
        #import logging
        #logging.getLogger().setLevel(logging.DEBUG)
        assert driver.need_download()

        def assertSel(s):
            self.assertEqual('http://foo/Source.xml', s.interface)
            self.assertEqual(2, len(s.selections))

            sels = [(sel.interface, sel) for sel in s.selections.values()]
            sels.sort()
            sels = [sel for uri, sel in sels]

            self.assertEqual('http://foo/Compiler.xml', sels[0].interface)
            self.assertEqual('http://foo/Source.xml', sels[1].interface)

            self.assertEqual("sha1=345", sels[0].id)
            self.assertEqual("1.0", sels[0].version)

            self.assertEqual('sha1=234', sels[1].id)
            self.assertEqual("1.0", sels[1].version)
            self.assertEqual("bar", sels[1].attrs['http://namespace foo'])
            self.assertEqual("1.0", sels[1].attrs['version'])
            assert 'version-modifier' not in sels[1].attrs

            self.assertEqual(0, len(sels[0].bindings))
            self.assertEqual(0, len(sels[0].dependencies))

            self.assertEqual(3, len(sels[1].bindings))
            self.assertEqual('.', sels[1].bindings[0].insert)
            self.assertEqual('/', sels[1].bindings[1].mount_point)
            self.assertEqual('source', sels[1].bindings[2].qdom.attrs['foo'])

            self.assertEqual(1, len(sels[1].dependencies))
            dep = sels[1].dependencies[0]
            self.assertEqual('http://foo/Compiler.xml', dep.interface)
            self.assertEqual(4, len(dep.bindings))
            self.assertEqual('bin', dep.bindings[0].insert)
            self.assertEqual('PATH', dep.bindings[0].name)
            self.assertEqual('prepend', dep.bindings[0].mode)
            assert dep.bindings[0].separator in ';:'

            self.assertEqual('bin', dep.bindings[1].value)
            self.assertEqual('NO_PATH', dep.bindings[1].name)
            self.assertEqual(',', dep.bindings[1].separator)

            self.assertEqual('bin', dep.bindings[2].insert)
            self.assertEqual('BINDIR', dep.bindings[2].name)
            self.assertEqual('replace', dep.bindings[2].mode)

            foo_binding = dep.bindings[3]
            self.assertEqual('compiler', foo_binding.qdom.attrs['foo'])
            self.assertEqual('child', foo_binding.qdom.childNodes[0].name)
            self.assertEqual('run', foo_binding.command)

            self.assertEqual(["sha1=345", 'sha256new_345'],
                             sorted(sels[0].digests))

        assert driver.solver.ready, driver.solver.get_failure_reason()
        s1 = driver.solver.selections
        s1.selections['http://foo/Source.xml'].attrs[
            'http://namespace foo'] = 'bar'
        assertSel(s1)

        xml = s1.toDOM().toxml("utf-8")
        root = qdom.parse(BytesIO(xml))
        self.assertEqual(namespaces.XMLNS_IFACE, root.uri)

        s2 = selections.Selections(root)
        assertSel(s2)
Example #29
0
def get_selections_for(requirements, config, options, select_only,
                       download_only, test_callback):
    """Get selections for given requirements.
	@since: 1.9"""
    if options.offline:
        config.network_use = model.network_offline

    iface_cache = config.iface_cache

    driver = Driver(config=config, requirements=requirements)

    # Note that need_download() triggers a solve
    if options.refresh or options.gui:
        # We could run immediately, but the user asked us not to
        can_run_immediately = False
    else:
        if select_only:
            # --select-only: we only care that we've made a selection, not that we've cached the implementations
            driver.need_download()
            can_run_immediately = driver.solver.ready
        else:
            can_run_immediately = not driver.need_download()

        stale_feeds = [
            feed for feed in driver.solver.feeds_used
            if not feed.startswith('distribution:')
            and  # Ignore (memory-only) PackageKit feeds
            iface_cache.is_stale(feed, config.freshness)
        ]

        if download_only and stale_feeds:
            can_run_immediately = False

    if can_run_immediately:
        if stale_feeds:
            if config.network_use == model.network_offline:
                logger.debug(
                    _("No doing background update because we are in off-line mode."
                      ))
            else:
                # There are feeds we should update, but we can run without them.
                # Do the update in the background while the program is running.
                from zeroinstall.injector import background
                background.spawn_background_update(driver, options.verbose)
        return driver.solver.selections

    # If we need to download anything, we might as well
    # refresh all the feeds first.
    options.refresh = True

    if options.gui != False:
        # If the user didn't say whether to use the GUI, choose for them.
        gui_args = driver.requirements.get_as_options()
        if download_only:
            # Just changes the button's label
            gui_args.append('--download-only')
        if options.refresh:
            gui_args.append('--refresh')
        if options.verbose:
            gui_args.insert(0, '--verbose')
            if options.verbose > 1:
                gui_args.insert(0, '--verbose')
        if options.with_store:
            for x in options.with_store:
                gui_args += ['--with-store', x]
        if select_only:
            gui_args.append('--select-only')

        from zeroinstall import helpers
        sels = helpers.get_selections_gui(requirements.interface_uri,
                                          gui_args,
                                          test_callback,
                                          use_gui=options.gui)

        if not sels:
            return None  # Aborted
        elif sels is helpers.DontUseGUI:
            sels = None
    else:
        sels = None

    if sels is None:
        # Note: --download-only also makes us stop and download stale feeds first.
        downloaded = driver.solve_and_download_impls(refresh=options.refresh
                                                     or download_only or False,
                                                     select_only=select_only)
        if downloaded:
            tasks.wait_for_blocker(downloaded)
        sels = driver.solver.selections

    return sels
Example #30
0
def get_selections(config, options, iface_uri, select_only, download_only, test_callback):
	"""Get selections for iface_uri, according to the options passed.
	Will switch to GUI mode if necessary.
	@param options: options from OptionParser
	@param iface_uri: canonical URI of the interface
	@param select_only: return immediately even if the selected versions aren't cached
	@param download_only: wait for stale feeds, and display GUI button as Download, not Run
	@return: the selected versions, or None if the user cancels
	@rtype: L{selections.Selections} | None
	"""
	if options.offline:
		config.network_use = model.network_offline

	iface_cache = config.iface_cache

	# Try to load it as a feed. If it is a feed, it'll get cached. If not, it's a
	# selections document and we return immediately.
	maybe_selections = iface_cache.get_feed(iface_uri, selections_ok = True)
	if isinstance(maybe_selections, selections.Selections):
		if not select_only:
			blocker = maybe_selections.download_missing(config)
			if blocker:
				logging.info(_("Waiting for selected implementations to be downloaded..."))
				tasks.wait_for_blocker(blocker)
		return maybe_selections

	r = requirements.Requirements(iface_uri)
	r.parse_options(options)

	driver = Driver(config = config, requirements = r)

	# Note that need_download() triggers a solve
	if options.refresh or options.gui:
		# We could run immediately, but the user asked us not to
		can_run_immediately = False
	else:
		if select_only:
			# --select-only: we only care that we've made a selection, not that we've cached the implementations
			driver.need_download()
			can_run_immediately = driver.solver.ready
		else:
			can_run_immediately = not driver.need_download()

		stale_feeds = [feed for feed in driver.solver.feeds_used if
				not feed.startswith('distribution:') and	# Ignore (memory-only) PackageKit feeds
				iface_cache.is_stale(feed, config.freshness)]

		if download_only and stale_feeds:
			can_run_immediately = False

	if can_run_immediately:
		if stale_feeds:
			if config.network_use == model.network_offline:
				logging.debug(_("No doing background update because we are in off-line mode."))
			else:
				# There are feeds we should update, but we can run without them.
				# Do the update in the background while the program is running.
				from zeroinstall.injector import background
				background.spawn_background_update(driver, options.verbose > 0)
		return driver.solver.selections

	# If the user didn't say whether to use the GUI, choose for them.
	if options.gui is None and os.environ.get('DISPLAY', None):
		options.gui = True
		# If we need to download anything, we might as well
		# refresh all the feeds first.
		options.refresh = True
		logging.info(_("Switching to GUI mode... (use --console to disable)"))

	if options.gui:
		gui_args = driver.requirements.get_as_options()
		if download_only:
			# Just changes the button's label
			gui_args.append('--download-only')
		if options.refresh:
			gui_args.append('--refresh')
		if options.verbose:
			gui_args.insert(0, '--verbose')
			if options.verbose > 1:
				gui_args.insert(0, '--verbose')
		if options.with_store:
			for x in options.with_store:
				gui_args += ['--with-store', x]
		if select_only:
			gui_args.append('--select-only')

		from zeroinstall import helpers
		sels = helpers.get_selections_gui(iface_uri, gui_args, test_callback)

		if not sels:
			return None		# Aborted
	else:
		# Note: --download-only also makes us stop and download stale feeds first.
		downloaded = driver.solve_and_download_impls(refresh = options.refresh or download_only or False,
							     select_only = select_only)
		if downloaded:
			tasks.wait_for_blocker(downloaded)
		sels = driver.solver.selections

	return sels
Example #31
0
def get_selections_for(requirements, config, options, select_only, download_only, test_callback):
	"""Get selections for given requirements.
	@since: 1.9"""
	if options.offline:
		config.network_use = model.network_offline

	iface_cache = config.iface_cache

	driver = Driver(config = config, requirements = requirements)

	# Note that need_download() triggers a solve
	if options.refresh or options.gui:
		# We could run immediately, but the user asked us not to
		can_run_immediately = False
	else:
		if select_only:
			# --select-only: we only care that we've made a selection, not that we've cached the implementations
			driver.need_download()
			can_run_immediately = driver.solver.ready
		else:
			can_run_immediately = not driver.need_download()

		stale_feeds = [feed for feed in driver.solver.feeds_used if
				not feed.startswith('distribution:') and	# Ignore (memory-only) PackageKit feeds
				iface_cache.is_stale(feed, config.freshness)]

		if download_only and stale_feeds:
			can_run_immediately = False

	if can_run_immediately:
		if stale_feeds:
			if config.network_use == model.network_offline:
				logger.debug(_("No doing background update because we are in off-line mode."))
			else:
				# There are feeds we should update, but we can run without them.
				# Do the update in the background while the program is running.
				from zeroinstall.injector import background
				background.spawn_background_update(driver, options.verbose)
		return driver.solver.selections

	# If we need to download anything, we might as well
	# refresh all the feeds first.
	options.refresh = True

	if options.gui != False:
		# If the user didn't say whether to use the GUI, choose for them.
		gui_args = driver.requirements.get_as_options()
		if download_only:
			# Just changes the button's label
			gui_args.append('--download-only')
		if options.refresh:
			gui_args.append('--refresh')
		if options.verbose:
			gui_args.insert(0, '--verbose')
			if options.verbose > 1:
				gui_args.insert(0, '--verbose')
		if options.with_store:
			for x in options.with_store:
				gui_args += ['--with-store', x]
		if select_only:
			gui_args.append('--select-only')

		from zeroinstall import helpers
		sels = helpers.get_selections_gui(requirements.interface_uri, gui_args, test_callback, use_gui = options.gui)

		if not sels:
			return None		# Aborted
		elif sels is helpers.DontUseGUI:
			sels = None
	else:
		sels = None

	if sels is None:
		# Note: --download-only also makes us stop and download stale feeds first.
		downloaded = driver.solve_and_download_impls(refresh = options.refresh or download_only or False,
							     select_only = select_only)
		if downloaded:
			tasks.wait_for_blocker(downloaded)
		sels = driver.solver.selections

	return sels
Example #32
0
	def _check_for_updates(self, sels):
		"""Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		We also schedule a background update from time-to-time anyway.
		@return: the selections to use
		@rtype: L{selections.Selections}"""
		need_solve = False		# Rerun solver (cached feeds have changed)
		need_update = False		# Update over the network

		utime = self._get_mtime('last-checked', warn_if_missing = True)
		last_solve = max(self._get_mtime('last-solve', warn_if_missing = False), utime)

		# Ideally, this would return all the files which were inputs into the solver's
		# decision. Currently, we approximate with:
		# - the previously selected feed files (local or cached)
		# - configuration files for the selected interfaces
		# - the global configuration
		# We currently ignore feeds and interfaces which were
		# considered but not selected.
		# Can yield None (ignored), paths or (path, mtime) tuples.
		# If this throws an exception, we will log it and resolve anyway.
		def get_inputs():
			for sel in sels.selections.values():
				logger.info("Checking %s", sel.feed)
				feed = iface_cache.get_feed(sel.feed)
				if not feed:
					raise IOError("Input %s missing; update" % sel.feed)
				else:
					if feed.local_path:
						yield feed.local_path
					else:
						yield (feed.url, feed.last_modified)

				# Per-feed configuration
				yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog,
								   'interfaces', model._pretty_escape(sel.interface))

			# Global configuration
			yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog, 'global')

		# If any of the feeds we used have been updated since the last check, do a quick re-solve
		iface_cache = self.config.iface_cache
		try:
			for item in get_inputs():
				if not item: continue
				if isinstance(item, tuple):
					path, mtime = item
				else:
					path = item
					mtime = os.stat(path).st_mtime

				if mtime and mtime > last_solve:
					logger.info("Triggering update to %s because %s has changed", self, path)
					need_solve = True
					break
		except Exception as ex:
			logger.info("Error checking modification times: %s", ex)
			need_solve = True
			need_update = True

		# Is it time for a background update anyway?
		if not need_update:
			staleness = time.time() - utime
			logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60))
			freshness_threshold = self.config.freshness
			if freshness_threshold > 0 and staleness >= freshness_threshold:
				need_update = True

		if need_solve:
			from zeroinstall.injector.driver import Driver
			driver = Driver(config = self.config, requirements = self.get_requirements())
			if driver.need_download():
				# Continue with the current (hopefully cached) selections while we download
				need_update = True
			else:
				old_sels = sels
				sels = driver.solver.selections
				from zeroinstall.support import xmltools
				if not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
					self.set_selections(sels, set_last_checked = False)
			self._touch('last-solve')

		# If we tried to check within the last hour, don't try again.
		if need_update:
			last_check_attempt = self._get_mtime('last-check-attempt', warn_if_missing = False)
			if last_check_attempt and last_check_attempt + 60 * 60 > time.time():
				logger.info("Tried to check within last hour; not trying again now")
				need_update = False

		if need_update:
			self.set_last_check_attempt()
			from zeroinstall.injector import background
			r = self.get_requirements()
			background.spawn_background_update2(r, False, self)

		return sels
Example #33
0
	def _check_for_updates(self, sels, use_gui):
		"""Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		If we can't use the current selections, we update in the foreground.
		We also schedule a background update from time-to-time anyway.
		@type sels: L{zeroinstall.injector.selections.Selections}
		@type use_gui: bool
		@return: the selections to use
		@rtype: L{selections.Selections}"""
		need_solve = False		# Rerun solver (cached feeds have changed)
		need_update = False		# Update over the network

		if sels:
			utime = self._get_mtime('last-checked', warn_if_missing = True)
			last_solve = max(self._get_mtime('last-solve', warn_if_missing = False), utime)

			# Ideally, this would return all the files which were inputs into the solver's
			# decision. Currently, we approximate with:
			# - the previously selected feed files (local or cached)
			# - configuration files for the selected interfaces
			# - the global configuration
			# We currently ignore feeds and interfaces which were
			# considered but not selected.
			# Can yield None (ignored), paths or (path, mtime) tuples.
			# If this throws an exception, we will log it and resolve anyway.
			def get_inputs():
				for sel in sels.selections.values():
					logger.info("Checking %s", sel.feed)

					if sel.feed.startswith('distribution:'):
						# If the package has changed version, we'll detect that below
						# with get_unavailable_selections.
						pass
					elif os.path.isabs(sel.feed):
						# Local feed
						yield sel.feed
					else:
						# Cached feed
						cached = basedir.load_first_cache(namespaces.config_site, 'interfaces', model.escape(sel.feed))
						if cached:
							yield cached
						else:
							raise IOError("Input %s missing; update" % sel.feed)

					# Per-feed configuration
					yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog,
									   'interfaces', model._pretty_escape(sel.interface))

				# Global configuration
				yield basedir.load_first_config(namespaces.config_site, namespaces.config_prog, 'global')

			# If any of the feeds we used have been updated since the last check, do a quick re-solve
			try:
				for item in get_inputs():
					if not item: continue
					if isinstance(item, tuple):
						path, mtime = item
					else:
						path = item
						try:
							mtime = os.stat(path).st_mtime
						except OSError as ex:
							logger.info("Triggering update to {app} due to error: {ex}".format(
								app = self, path = path, ex = ex))
							need_solve = True
							break

					if mtime and mtime > last_solve:
						logger.info("Triggering update to %s because %s has changed", self, path)
						need_solve = True
						break
			except Exception as ex:
				logger.info("Error checking modification times: %s", ex)
				need_solve = True
				need_update = True

			# Is it time for a background update anyway?
			if not need_update:
				staleness = time.time() - utime
				logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60))
				freshness_threshold = self.config.freshness
				if freshness_threshold > 0 and staleness >= freshness_threshold:
					need_update = True

			# If any of the saved selections aren't available then we need
			# to download right now, not later in the background.
			unavailable_selections = sels.get_unavailable_selections(config = self.config, include_packages = True)
			if unavailable_selections:
				logger.info("Saved selections are unusable (missing %s)",
					    ', '.join(str(s) for s in unavailable_selections))
				need_solve = True
		else:
			# No current selections
			need_solve = True
			unavailable_selections = True

		if need_solve:
			from zeroinstall.injector.driver import Driver
			driver = Driver(config = self.config, requirements = self.get_requirements())
			if driver.need_download():
				if unavailable_selections:
					return self._foreground_update(driver, use_gui)
				else:
					# Continue with the current (cached) selections while we download
					need_update = True
			else:
				old_sels = sels
				sels = driver.solver.selections
				from zeroinstall.support import xmltools
				if old_sels is None or not xmltools.nodes_equal(sels.toDOM(), old_sels.toDOM()):
					self.set_selections(sels, set_last_checked = False)
			try:
				self._touch('last-solve')
			except OSError as ex:
				logger.warning("Error checking for updates: %s", ex)

		# If we tried to check within the last hour, don't try again.
		if need_update:
			last_check_attempt = self._get_mtime('last-check-attempt', warn_if_missing = False)
			if last_check_attempt and last_check_attempt + 60 * 60 > time.time():
				logger.info("Tried to check within last hour; not trying again now")
				need_update = False

		if need_update:
			try:
				self.set_last_check_attempt()
			except OSError as ex:
				logger.warning("Error checking for updates: %s", ex)
			else:
				from zeroinstall.injector import background
				r = self.get_requirements()
				background.spawn_background_update2(r, False, self)

		return sels
Example #34
0
    def _check_for_updates(self, sels, use_gui):
        """Check whether the selections need to be updated.
		If any input feeds have changed, we re-run the solver. If the
		new selections require a download, we schedule one in the
		background and return the old selections. Otherwise, we return the
		new selections. If we can select better versions without downloading,
		we update the app's selections and return the new selections.
		If we can't use the current selections, we update in the foreground.
		We also schedule a background update from time-to-time anyway.
		@type sels: L{zeroinstall.injector.selections.Selections}
		@type use_gui: bool
		@return: the selections to use
		@rtype: L{selections.Selections}"""
        need_solve = False  # Rerun solver (cached feeds have changed)
        need_update = False  # Update over the network

        if sels:
            utime = self._get_mtime('last-checked', warn_if_missing=True)
            last_solve = max(
                self._get_mtime('last-solve', warn_if_missing=False), utime)

            # Ideally, this would return all the files which were inputs into the solver's
            # decision. Currently, we approximate with:
            # - the previously selected feed files (local or cached)
            # - configuration files for the selected interfaces
            # - the global configuration
            # We currently ignore feeds and interfaces which were
            # considered but not selected.
            # Can yield None (ignored), paths or (path, mtime) tuples.
            # If this throws an exception, we will log it and resolve anyway.
            def get_inputs():
                for sel in sels.selections.values():
                    logger.info("Checking %s", sel.feed)

                    if sel.feed.startswith('distribution:'):
                        # If the package has changed version, we'll detect that below
                        # with get_unavailable_selections.
                        pass
                    elif os.path.isabs(sel.feed):
                        # Local feed
                        yield sel.feed
                    else:
                        # Cached feed
                        cached = basedir.load_first_cache(
                            namespaces.config_site, 'interfaces',
                            model.escape(sel.feed))
                        if cached:
                            yield cached
                        else:
                            raise IOError("Input %s missing; update" %
                                          sel.feed)

                    # Per-feed configuration
                    yield basedir.load_first_config(
                        namespaces.config_site, namespaces.config_prog,
                        'interfaces', model._pretty_escape(sel.interface))

                # Global configuration
                yield basedir.load_first_config(namespaces.config_site,
                                                namespaces.config_prog,
                                                'global')

            # If any of the feeds we used have been updated since the last check, do a quick re-solve
            try:
                for item in get_inputs():
                    if not item: continue
                    if isinstance(item, tuple):
                        path, mtime = item
                    else:
                        path = item
                        try:
                            mtime = os.stat(path).st_mtime
                        except OSError as ex:
                            logger.info(
                                "Triggering update to {app} due to error: {ex}"
                                .format(app=self, path=path, ex=ex))
                            need_solve = True
                            break

                    if mtime and mtime > last_solve:
                        logger.info(
                            "Triggering update to %s because %s has changed",
                            self, path)
                        need_solve = True
                        break
            except Exception as ex:
                logger.info("Error checking modification times: %s", ex)
                need_solve = True
                need_update = True

            # Is it time for a background update anyway?
            if not need_update:
                staleness = time.time() - utime
                logger.info("Staleness of app %s is %d hours", self,
                            staleness / (60 * 60))
                freshness_threshold = self.config.freshness
                if freshness_threshold > 0 and staleness >= freshness_threshold:
                    need_update = True

            # If any of the saved selections aren't available then we need
            # to download right now, not later in the background.
            unavailable_selections = sels.get_unavailable_selections(
                config=self.config, include_packages=True)
            if unavailable_selections:
                logger.info("Saved selections are unusable (missing %s)",
                            ', '.join(str(s) for s in unavailable_selections))
                need_solve = True
        else:
            # No current selections
            need_solve = True
            unavailable_selections = True

        if need_solve:
            from zeroinstall.injector.driver import Driver
            driver = Driver(config=self.config,
                            requirements=self.get_requirements())
            if driver.need_download():
                if unavailable_selections:
                    return self._foreground_update(driver, use_gui)
                else:
                    # Continue with the current (cached) selections while we download
                    need_update = True
            else:
                old_sels = sels
                sels = driver.solver.selections
                from zeroinstall.support import xmltools
                if old_sels is None or not xmltools.nodes_equal(
                        sels.toDOM(), old_sels.toDOM()):
                    self.set_selections(sels, set_last_checked=False)
            try:
                self._touch('last-solve')
            except OSError as ex:
                logger.warning("Error checking for updates: %s", ex)

        # If we tried to check within the last hour, don't try again.
        if need_update:
            last_check_attempt = self._get_mtime('last-check-attempt',
                                                 warn_if_missing=False)
            if last_check_attempt and last_check_attempt + 60 * 60 > time.time(
            ):
                logger.info(
                    "Tried to check within last hour; not trying again now")
                need_update = False

        if need_update:
            try:
                self.set_last_check_attempt()
            except OSError as ex:
                logger.warning("Error checking for updates: %s", ex)
            else:
                from zeroinstall.injector import background
                r = self.get_requirements()
                background.spawn_background_update2(r, False, self)

        return sels
	def testSelections(self):
		requirements = Requirements('http://foo/Source.xml')
		requirements.source = True
		requirements.command = 'compile'
		driver = Driver(requirements = requirements, config = self.config)
		source = self.config.iface_cache.get_interface('http://foo/Source.xml')
		compiler = self.config.iface_cache.get_interface('http://foo/Compiler.xml')
		self.import_feed(source.uri, 'Source.xml')
		self.import_feed(compiler.uri, 'Compiler.xml')

		self.config.network_use = model.network_full
		#import logging
		#logging.getLogger().setLevel(logging.DEBUG)
		assert driver.need_download()

		def assertSel(s):
			self.assertEqual('http://foo/Source.xml', s.interface)
			self.assertEqual(2, len(s.selections))

			sels = [(sel.interface, sel) for sel in s.selections.values()]
			sels.sort()
			sels = [sel for uri,sel in sels]
			
			self.assertEqual('http://foo/Compiler.xml', sels[0].interface)
			self.assertEqual('http://foo/Source.xml', sels[1].interface)

			self.assertEqual("sha1=345", sels[0].id)
			self.assertEqual("1.0", sels[0].version)

			self.assertEqual('sha1=234', sels[1].id)
			self.assertEqual("1.0", sels[1].version)
			self.assertEqual("bar", sels[1].attrs['http://namespace foo'])
			self.assertEqual("1.0", sels[1].attrs['version'])
			assert 'version-modifier' not in sels[1].attrs

			self.assertEqual(0, len(sels[0].bindings))
			self.assertEqual(0, len(sels[0].dependencies))

			self.assertEqual(3, len(sels[1].bindings))
			self.assertEqual('.', sels[1].bindings[0].insert)
			self.assertEqual('/', sels[1].bindings[1].mount_point)
			self.assertEqual('source', sels[1].bindings[2].qdom.attrs['foo'])

			self.assertEqual(1, len(sels[1].dependencies))
			dep = sels[1].dependencies[0]
			self.assertEqual('http://foo/Compiler.xml', dep.interface)
			self.assertEqual(4, len(dep.bindings))
			self.assertEqual('bin', dep.bindings[0].insert)
			self.assertEqual('PATH', dep.bindings[0].name)
			self.assertEqual('prepend', dep.bindings[0].mode)
			assert dep.bindings[0].separator in ';:'

			self.assertEqual('bin', dep.bindings[1].value)
			self.assertEqual('NO_PATH', dep.bindings[1].name)
			self.assertEqual(',', dep.bindings[1].separator)

			self.assertEqual('bin', dep.bindings[2].insert)
			self.assertEqual('BINDIR', dep.bindings[2].name)
			self.assertEqual('replace', dep.bindings[2].mode)

			foo_binding = dep.bindings[3]
			self.assertEqual('compiler', foo_binding.qdom.attrs['foo'])
			self.assertEqual('child', foo_binding.qdom.childNodes[0].name)
			self.assertEqual('run', foo_binding.command)

			self.assertEqual(["sha1=345", 'sha256new_345'], sorted(sels[0].digests))

		assert driver.solver.ready, driver.solver.get_failure_reason()
		s1 = driver.solver.selections
		s1.selections['http://foo/Source.xml'].attrs['http://namespace foo'] = 'bar'
		assertSel(s1)

		xml = s1.toDOM().toxml("utf-8")
		root = qdom.parse(BytesIO(xml))
		self.assertEqual(namespaces.XMLNS_IFACE, root.uri)

		s2 = selections.Selections(root)
		assertSel(s2)