def download_selections(self, sels): """Download any missing implementations in the given selections. If no downloads are needed, but we haven't checked for a while, start a background process to check for updates (but return None immediately). @return: a blocker which resolves when all needed implementations are available @rtype: L{tasks.Blocker} | None """ # Check the selections are still available blocker = sels.download_missing(self.config) # TODO: package impls if blocker: return blocker else: # Nothing to download, but is it time for a background update? timestamp_path = os.path.join(self.path, 'last-checked') try: utime = os.stat(timestamp_path).st_mtime staleness = time.time() - utime logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60)) freshness_threshold = self.config.freshness need_update = freshness_threshold > 0 and staleness >= freshness_threshold if need_update: last_check_attempt_path = os.path.join( self.path, 'last-check-attempt') if os.path.exists(last_check_attempt_path): last_check_attempt = os.stat( last_check_attempt_path).st_mtime if last_check_attempt + 60 * 60 > time.time(): logger.info( "Tried to check within last hour; not trying again now" ) need_update = False except Exception as ex: logger.warn("Failed to get time-stamp of %s: %s", timestamp_path, ex) need_update = True if need_update: self.set_last_check_attempt() from zeroinstall.injector import background r = self.get_requirements() background.spawn_background_update2(r, False, self)
def download_selections(self, sels): """Download any missing implementations in the given selections. If no downloads are needed, but we haven't checked for a while, start a background process to check for updates (but return None immediately). @return: a blocker which resolves when all needed implementations are available @rtype: L{tasks.Blocker} | None """ # Check the selections are still available blocker = sels.download_missing(self.config) # TODO: package impls if blocker: return blocker else: # Nothing to download, but is it time for a background update? timestamp_path = os.path.join(self.path, "last-checked") try: utime = os.stat(timestamp_path).st_mtime staleness = time.time() - utime logger.info("Staleness of app %s is %d hours", self, staleness / (60 * 60)) freshness_threshold = self.config.freshness need_update = freshness_threshold > 0 and staleness >= freshness_threshold if need_update: last_check_attempt_path = os.path.join(self.path, "last-check-attempt") if os.path.exists(last_check_attempt_path): last_check_attempt = os.stat(last_check_attempt_path).st_mtime if last_check_attempt + 60 * 60 > time.time(): logger.info("Tried to check within last hour; not trying again now") need_update = False except Exception as ex: logger.warn("Failed to get time-stamp of %s: %s", timestamp_path, ex) need_update = True if need_update: self.set_last_check_attempt() from zeroinstall.injector import background r = self.get_requirements() background.spawn_background_update2(r, False, self)
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 _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
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
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