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

Source Code for Module zeroinstall.injector.handler

  1  """ 
  2  Integrates download callbacks with an external mainloop. 
  3  While things are being downloaded, Zero Install returns control to your program. 
  4  Your mainloop is responsible for monitoring the state of the downloads and notifying 
  5  Zero Install when they are complete. 
  6   
  7  To do this, you supply a L{Handler} to the L{policy}. 
  8  """ 
  9   
 10  # Copyright (C) 2006, Thomas Leonard 
 11  # See the README file for details, or visit http://0install.net. 
 12   
 13  import os, sys 
 14  from logging import debug, info, warn 
 15   
 16  from zeroinstall import NeedDownload 
 17  from zeroinstall.support import tasks 
 18  from zeroinstall.injector import model, download 
 19  from zeroinstall.injector.iface_cache import iface_cache 
20 21 -class NoTrustedKeys(model.SafeException):
22 """Thrown by L{Handler.confirm_trust_keys} on failure.""" 23 pass
24
25 -class Handler(object):
26 """ 27 This implementation uses the GLib mainloop. Note that QT4 can use the GLib mainloop too. 28 29 @ivar monitored_downloads: dict of downloads in progress 30 @type monitored_downloads: {URL: L{download.Download}} 31 @ivar n_completed_downloads: number of downloads which have finished for GUIs, etc (can be reset as desired). 32 @type n_completed_downloads: int 33 @ivar total_bytes_downloaded: informational counter for GUIs, etc (can be reset as desired). Updated when download finishes. 34 @type total_bytes_downloaded: int 35 """ 36 37 __slots__ = ['monitored_downloads', '_loop', 'dry_run', 'total_bytes_downloaded', 'n_completed_downloads'] 38
39 - def __init__(self, mainloop = None, dry_run = False):
40 self.monitored_downloads = {} 41 self._loop = None 42 self.dry_run = dry_run 43 self.n_completed_downloads = 0 44 self.total_bytes_downloaded = 0
45
46 - def monitor_download(self, dl):
47 """Called when a new L{download} is started. 48 This is mainly used by the GUI to display the progress bar.""" 49 dl.start() 50 self.monitored_downloads[dl.url] = dl 51 self.downloads_changed() 52 53 @tasks.async 54 def download_done_stats(): 55 yield dl.downloaded 56 # NB: we don't check for exceptions here; someone else should be doing that 57 try: 58 self.n_completed_downloads += 1 59 self.total_bytes_downloaded += dl.get_bytes_downloaded_so_far() 60 del self.monitored_downloads[dl.url] 61 self.downloads_changed() 62 except Exception, ex: 63 self.report_error(ex)
64 download_done_stats()
65
66 - def impl_added_to_store(self, impl):
67 """Called by the L{fetch.Fetcher} when adding an implementation. 68 The GUI uses this to update its display. 69 @param impl: the implementation which has been added 70 @type impl: L{model.Implementation} 71 """ 72 pass
73
74 - def downloads_changed(self):
75 """This is just for the GUI to override to update its display.""" 76 pass
77
78 - def wait_for_blocker(self, blocker):
79 """Run a recursive mainloop until blocker is triggered. 80 @param blocker: event to wait on 81 @type blocker: L{tasks.Blocker}""" 82 if not blocker.happened: 83 import gobject 84 85 def quitter(): 86 yield blocker 87 self._loop.quit()
88 quit = tasks.Task(quitter(), "quitter") 89 90 assert self._loop is None # Avoid recursion 91 self._loop = gobject.MainLoop(gobject.main_context_default()) 92 try: 93 debug("Entering mainloop, waiting for %s", blocker) 94 self._loop.run() 95 finally: 96 self._loop = None 97 98 assert blocker.happened, "Someone quit the main loop!" 99 100 tasks.check(blocker) 101
102 - def get_download(self, url, force = False, hint = None):
103 """Return the Download object currently downloading 'url'. 104 If no download for this URL has been started, start one now (and 105 start monitoring it). 106 If the download failed and force is False, return it anyway. 107 If force is True, abort any current or failed download and start 108 a new one. 109 @rtype: L{download.Download} 110 """ 111 if self.dry_run: 112 raise NeedDownload(url) 113 114 try: 115 dl = self.monitored_downloads[url] 116 if dl and force: 117 dl.abort() 118 raise KeyError 119 except KeyError: 120 dl = download.Download(url, hint) 121 self.monitor_download(dl) 122 return dl
123
124 - def confirm_trust_keys(self, interface, sigs, iface_xml):
125 """We don't trust any of the signatures yet. Ask the user. 126 When done update the L{trust} database, and then call L{trust.TrustDB.notify}. 127 @arg interface: the interface being updated 128 @arg sigs: a list of signatures (from L{gpg.check_stream}) 129 @arg iface_xml: the downloaded data (not yet trusted) 130 @return: a blocker, if confirmation will happen asynchronously, or None 131 @rtype: L{tasks.Blocker}""" 132 from zeroinstall.injector import trust, gpg 133 assert sigs 134 valid_sigs = [s for s in sigs if isinstance(s, gpg.ValidSig)] 135 if not valid_sigs: 136 raise model.SafeException('No valid signatures found. Signatures:' + 137 ''.join(['\n- ' + str(s) for s in sigs])) 138 139 domain = trust.domain_from_url(interface.uri) 140 141 print "\nInterface:", interface.uri 142 print "The interface is correctly signed with the following keys:" 143 for x in valid_sigs: 144 print "-", x 145 146 if len(valid_sigs) == 1: 147 print "Do you want to trust this key to sign feeds from '%s'?" % domain 148 else: 149 print "Do you want to trust all of these keys to sign feeds from '%s'?" % domain 150 while True: 151 i = raw_input("Trust [Y/N] ") 152 if not i: continue 153 if i in 'Nn': 154 raise NoTrustedKeys('Not signed with a trusted key') 155 if i in 'Yy': 156 break 157 for key in valid_sigs: 158 print "Trusting", key.fingerprint, "for", domain 159 trust.trust_db.trust_key(key.fingerprint, domain) 160 161 trust.trust_db.notify()
162
163 - def report_error(self, exception, tb = None):
164 """Report an exception to the user. 165 @param exception: the exception to report 166 @type exception: L{SafeException} 167 @param tb: optional traceback 168 @since: 0.25""" 169 warn("%s", exception)
170 #import traceback 171 #traceback.print_exception(exception, None, tb) 172