Package zeroinstall :: Package injector :: Module driver
[frames] | no frames]

Source Code for Module zeroinstall.injector.driver

  1  """ 
  2  This class brings together a L{solve.Solver} to choose a set of implmentations, a 
  3  L{fetch.Fetcher} to download additional components, and the user's configuration 
  4  settings. 
  5  @since: 0.53 
  6  """ 
  7   
  8  # Copyright (C) 2011, Thomas Leonard 
  9  # See the README file for details, or visit http://0install.net. 
 10   
 11  from zeroinstall import _, logger 
 12  import os 
 13   
 14  from zeroinstall.injector import arch 
 15  from zeroinstall.injector.model import network_offline 
 16  from zeroinstall.support import tasks 
17 18 -class Driver(object):
19 """Chooses a set of implementations based on a policy. 20 Typical use: 21 1. Create a Driver object, giving it the requirements about the program to be run. 22 2. Call L{solve_with_downloads}. If more information is needed, a L{fetch.Fetcher} will be used to download it. 23 3. When all downloads are complete, the L{solver} contains the chosen versions. 24 4. Use L{get_uncached_implementations} to find where to get these versions and download them 25 using L{download_uncached_implementations}. 26 27 @ivar target_arch: target architecture for binaries (deprecated) 28 @type target_arch: L{arch.Architecture} 29 @ivar solver: solver used to choose a set of implementations 30 @type solver: L{solve.Solver} 31 @ivar watchers: callbacks to invoke after solving 32 """ 33 __slots__ = ['watchers', 'requirements', 'config', 'target_arch', 'solver'] 34
35 - def __init__(self, config, requirements):
36 """@param config: The configuration settings to use 37 @type config: L{config.Config} 38 @param requirements: Details about the program we want to run 39 @type requirements: L{requirements.Requirements} 40 @since: 0.53""" 41 self.watchers = [] 42 43 assert config 44 self.config = config 45 46 assert requirements 47 self.requirements = requirements 48 49 self.target_arch = arch.get_architecture(requirements.os, requirements.cpu) 50 51 from zeroinstall.injector.solver import DefaultSolver 52 self.solver = DefaultSolver(self.config) 53 54 logger.debug(_("Supported systems: '%s'"), arch.os_ranks) 55 logger.debug(_("Supported processors: '%s'"), arch.machine_ranks) 56 57 self.solver.extra_restrictions = requirements.get_extra_restrictions(self.config.iface_cache)
58
60 """List all chosen implementations which aren't yet available locally. 61 @rtype: [(L{model.Interface}, L{model.Implementation})]""" 62 iface_cache = self.config.iface_cache 63 stores = self.config.stores 64 uncached = [] 65 for uri, selection in self.solver.selections.selections.items(): 66 impl = selection.impl 67 assert impl, self.solver.selections 68 if not impl.is_available(stores): 69 uncached.append((iface_cache.get_interface(uri), impl)) 70 return uncached
71 72 @tasks.async
73 - def solve_with_downloads(self, force = False, update_local = False):
74 """Run the solver, then download any feeds that are missing or 75 that need to be updated. Each time a new feed is imported into 76 the cache, the solver is run again, possibly adding new downloads. 77 @param force: whether to download even if we're already ready to run. 78 @type force: bool 79 @param update_local: fetch PackageKit feeds even if we're ready to run. 80 @type update_local: bool""" 81 82 downloads_finished = set() # Successful or otherwise 83 downloads_in_progress = {} # URL -> Download 84 85 # There are three cases: 86 # 1. We want to run immediately if possible. If not, download all the information we can. 87 # (force = False, update_local = False) 88 # 2. We're in no hurry, but don't want to use the network unnecessarily. 89 # We should still update local information (from PackageKit). 90 # (force = False, update_local = True) 91 # 3. The user explicitly asked us to refresh everything. 92 # (force = True) 93 94 try_quick_exit = not (force or update_local) 95 96 while True: 97 self.solver.solve_for(self.requirements) 98 for w in self.watchers: w() 99 100 if try_quick_exit and self.solver.ready: 101 break 102 try_quick_exit = False 103 104 if not self.solver.ready: 105 force = True 106 107 for f in self.solver.feeds_used: 108 if f in downloads_finished or f in downloads_in_progress: 109 continue 110 if os.path.isabs(f): 111 if force: 112 self.config.iface_cache.get_feed(f, force = True) 113 downloads_in_progress[f] = tasks.IdleBlocker('Refresh local feed') 114 continue 115 elif f.startswith('distribution:'): 116 if force or update_local: 117 downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache) 118 elif force and self.config.network_use != network_offline: 119 downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache) 120 # Once we've starting downloading some things, 121 # we might as well get them all. 122 force = True 123 124 if not downloads_in_progress: 125 if self.config.network_use == network_offline: 126 logger.info(_("Can't choose versions and in off-line mode, so aborting")) 127 break 128 129 # Wait for at least one download to finish 130 blockers = downloads_in_progress.values() 131 yield blockers 132 tasks.check(blockers, self.config.handler.report_error) 133 134 for f in list(downloads_in_progress.keys()): 135 if f in downloads_in_progress and downloads_in_progress[f].happened: 136 del downloads_in_progress[f] 137 downloads_finished.add(f) 138 139 # Need to refetch any "distribution" feed that 140 # depends on this one 141 distro_feed_url = 'distribution:' + f 142 if distro_feed_url in downloads_finished: 143 downloads_finished.remove(distro_feed_url) 144 if distro_feed_url in downloads_in_progress: 145 del downloads_in_progress[distro_feed_url]
146 147 @tasks.async
148 - def solve_and_download_impls(self, refresh = False, select_only = False):
149 """Run L{solve_with_downloads} and then get the selected implementations too. 150 @type refresh: bool 151 @type select_only: bool 152 @raise SafeException: if we couldn't select a set of implementations 153 @since: 0.40""" 154 refreshed = self.solve_with_downloads(refresh) 155 if refreshed: 156 yield refreshed 157 tasks.check(refreshed) 158 159 if not self.solver.ready: 160 raise self.solver.get_failure_reason() 161 162 if not select_only: 163 downloaded = self.download_uncached_implementations() 164 if downloaded: 165 yield downloaded 166 tasks.check(downloaded)
167
168 - def need_download(self):
169 """Decide whether we need to download anything (but don't do it!) 170 @return: true if we MUST download something (feeds or implementations) 171 @rtype: bool""" 172 self.solver.solve_for(self.requirements) 173 for w in self.watchers: w() 174 175 if not self.solver.ready: 176 return True # Maybe a newer version will work? 177 178 if self.get_uncached_implementations(): 179 return True 180 181 return False
182
184 """Download all implementations chosen by the solver that are missing from the cache. 185 @rtype: L{zeroinstall.support.tasks.Blocker}""" 186 assert self.solver.ready, "Solver is not ready!\n%s" % self.solver.selections 187 stores = self.config.stores 188 return self.config.fetcher.download_impls([impl for impl in self.solver.selections.values() if not impl.is_available(stores)], 189 stores)
190