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

Source Code for Module zeroinstall.injector.reader

  1  """ 
  2  Parses an XML feed into a Python representation. You should probably use L{iface_cache.iface_cache} rather than the functions here. 
  3  """ 
  4   
  5  # Copyright (C) 2009, Thomas Leonard 
  6  # See the README file for details, or visit http://0install.net. 
  7   
  8  from zeroinstall import _, logger 
  9  import os 
 10  import errno 
 11   
 12  from zeroinstall import support 
 13  from zeroinstall.support import basedir 
 14  from zeroinstall.injector import qdom 
 15  from zeroinstall.injector.namespaces import config_site, config_prog, XMLNS_IFACE 
 16  from zeroinstall.injector.model import Interface, InvalidInterface, ZeroInstallFeed, escape, Feed, stability_levels 
 17  from zeroinstall.injector import model 
 18   
19 -class MissingLocalFeed(InvalidInterface):
20 pass
21
22 -def _add_site_packages(interface, site_packages, known_site_feeds):
23 """@type interface: L{Interface} 24 @type site_packages: str 25 @type known_site_feeds: {str}""" 26 for impl in os.listdir(site_packages): 27 if impl.startswith('.'): continue 28 feed = os.path.join(site_packages, impl, '0install', 'feed.xml') 29 if not os.path.exists(feed): 30 logger.warning(_("Site-local feed {path} not found").format(path = feed)) 31 logger.debug("Adding site-local feed '%s'", feed) 32 33 # (we treat these as user overrides in order to let old versions of 0install 34 # find them) 35 interface.extra_feeds.append(Feed(feed, None, user_override = True, site_package = True)) 36 known_site_feeds.add(feed)
37
38 -def update_from_cache(interface, iface_cache = None):
39 """Read a cached interface and any native feeds or user overrides. 40 @param interface: the interface object to update 41 @type interface: L{model.Interface} 42 @type iface_cache: L{zeroinstall.injector.iface_cache.IfaceCache} | None 43 @return: True if cached version and user overrides loaded OK. 44 False if upstream not cached. Local interfaces (starting with /) are 45 always considered to be cached, although they are not actually stored in the cache. 46 @rtype: bool 47 @note: internal; use L{iface_cache.IfaceCache.get_interface} instread.""" 48 interface.reset() 49 if iface_cache is None: 50 import warnings 51 warnings.warn("iface_cache should be specified", DeprecationWarning, 2) 52 from zeroinstall.injector import policy 53 iface_cache = policy.get_deprecated_singleton_config().iface_cache 54 55 # Add the distribution package manager's version, if any 56 path = basedir.load_first_data(config_site, 'native_feeds', model._pretty_escape(interface.uri)) 57 if path: 58 # Resolve any symlinks 59 logger.info(_("Adding native packager feed '%s'"), path) 60 interface.extra_feeds.append(Feed(os.path.realpath(path), None, False)) 61 62 # Add locally-compiled binaries, if any 63 escaped_uri = model.escape_interface_uri(interface.uri) 64 known_site_feeds = set() 65 for path in basedir.load_data_paths(config_site, 'site-packages', *escaped_uri): 66 try: 67 _add_site_packages(interface, path, known_site_feeds) 68 except Exception as ex: 69 logger.warning("Error loading site packages from {path}: {ex}".format(path = path, ex = ex)) 70 71 update_user_overrides(interface, known_site_feeds) 72 73 main_feed = iface_cache.get_feed(interface.uri, force = True) 74 if main_feed: 75 update_user_feed_overrides(main_feed) 76 77 return main_feed is not None
78
79 -def load_feed_from_cache(url, selections_ok = False):
80 """Load a feed. If the feed is remote, load from the cache. If local, load it directly. 81 @type url: str 82 @type selections_ok: bool 83 @return: the feed, or None if it's remote and not cached. 84 @rtype: L{ZeroInstallFeed} | None""" 85 try: 86 if os.path.isabs(url): 87 logger.debug(_("Loading local feed file '%s'"), url) 88 return load_feed(url, local = True, selections_ok = selections_ok) 89 else: 90 cached = basedir.load_first_cache(config_site, 'interfaces', escape(url)) 91 if cached: 92 logger.debug(_("Loading cached information for %(interface)s from %(cached)s"), {'interface': url, 'cached': cached}) 93 return load_feed(cached, local = False) 94 else: 95 return None 96 except InvalidInterface as ex: 97 ex.feed_url = url 98 raise
99
100 -def update_user_feed_overrides(feed):
101 """Update a feed with user-supplied information. 102 Sets last_checked and user_stability ratings. 103 @param feed: feed to update 104 @type feed: L{ZeroInstallFeed} 105 @since 0.49""" 106 user = basedir.load_first_config(config_site, config_prog, 107 'feeds', model._pretty_escape(feed.url)) 108 if user is None: 109 # For files saved by 0launch < 0.49 110 user = basedir.load_first_config(config_site, config_prog, 111 'user_overrides', escape(feed.url)) 112 if not user: 113 return 114 115 try: 116 with open(user, 'rb') as stream: 117 root = qdom.parse(stream) 118 except Exception as ex: 119 logger.warning(_("Error reading '%(user)s': %(exception)s"), {'user': user, 'exception': ex}) 120 raise 121 122 last_checked = root.getAttribute('last-checked') 123 if last_checked: 124 feed.last_checked = int(last_checked) 125 126 for item in root.childNodes: 127 if item.uri != XMLNS_IFACE: continue 128 if item.name == 'implementation': 129 id = item.getAttribute('id') 130 assert id is not None 131 impl = feed.implementations.get(id, None) 132 if not impl: 133 logger.debug(_("Ignoring user-override for unknown implementation %(id)s in %(interface)s"), {'id': id, 'interface': feed}) 134 continue 135 136 user_stability = item.getAttribute('user-stability') 137 if user_stability: 138 impl.user_stability = stability_levels[str(user_stability)]
139
140 -def update_user_overrides(interface, known_site_feeds = frozenset()):
141 """Update an interface with user-supplied information. 142 Sets preferred stability and updates extra_feeds. 143 @param interface: the interface object to update 144 @type interface: L{model.Interface} 145 @param known_site_feeds: feeds to ignore (for backwards compatibility) 146 @type known_site_feeds: {str}""" 147 user = basedir.load_first_config(config_site, config_prog, 148 'interfaces', model._pretty_escape(interface.uri)) 149 if user is None: 150 # For files saved by 0launch < 0.49 151 user = basedir.load_first_config(config_site, config_prog, 152 'user_overrides', escape(interface.uri)) 153 if not user: 154 return 155 156 try: 157 with open(user, 'rb') as stream: 158 root = qdom.parse(stream) 159 except Exception as ex: 160 logger.warning(_("Error reading '%(user)s': %(exception)s"), {'user': user, 'exception': ex}) 161 raise 162 163 stability_policy = root.getAttribute('stability-policy') 164 if stability_policy: 165 interface.set_stability_policy(stability_levels[str(stability_policy)]) 166 167 for item in root.childNodes: 168 if item.uri != XMLNS_IFACE: continue 169 if item.name == 'feed': 170 feed_src = item.getAttribute('src') 171 if not feed_src: 172 raise InvalidInterface(_('Missing "src" attribute in <feed>')) 173 # (note: 0install 1.9..1.12 used a different scheme and the "site-package" attribute; 174 # we deliberately use a different attribute name to avoid confusion) 175 if item.getAttribute('is-site-package'): 176 # Site packages are detected earlier. This test isn't completely reliable, 177 # since older versions will remove the attribute when saving the config 178 # (hence the next test). 179 continue 180 if feed_src in known_site_feeds: 181 continue 182 interface.extra_feeds.append(Feed(feed_src, item.getAttribute('arch'), True, langs = item.getAttribute('langs')))
183
184 -def check_readable(feed_url, source):
185 """Test whether a feed file is valid. 186 @param feed_url: the feed's expected URL 187 @type feed_url: str 188 @param source: the name of the file to test 189 @type source: str 190 @return: the modification time in src (usually just the mtime of the file) 191 @rtype: int 192 @raise InvalidInterface: If the source's syntax is incorrect""" 193 try: 194 feed = load_feed(source, local = False) 195 196 if feed.url != feed_url: 197 raise InvalidInterface(_("Incorrect URL used for feed.\n\n" 198 "%(feed_url)s is given in the feed, but\n" 199 "%(interface_uri)s was requested") % 200 {'feed_url': feed.url, 'interface_uri': feed_url}) 201 return feed.last_modified 202 except InvalidInterface as ex: 203 logger.info(_("Error loading feed:\n" 204 "Interface URI: %(uri)s\n" 205 "Local file: %(source)s\n" 206 "%(exception)s") % 207 {'uri': feed_url, 'source': source, 'exception': ex}) 208 raise InvalidInterface(_("Error loading feed '%(uri)s':\n\n%(exception)s") % {'uri': feed_url, 'exception': ex})
209
210 -def update(interface, source, local = False, iface_cache = None):
211 """Read in information about an interface. 212 Deprecated. 213 @param interface: the interface object to update 214 @type interface: L{model.Interface} 215 @param source: the name of the file to read 216 @type source: str 217 @param local: use file's mtime for last-modified, and uri attribute is ignored 218 @type local: bool 219 @type iface_cache: L{zeroinstall.injector.iface_cache.IfaceCache} | None 220 @return: the new feed (since 0.32) 221 @rtype: L{ZeroInstallFeed} 222 @raise InvalidInterface: if the source's syntax is incorrect 223 @see: L{update_from_cache}, which calls this""" 224 assert isinstance(interface, Interface) 225 226 feed = load_feed(source, local) 227 228 if not local: 229 if feed.url != interface.uri: 230 raise InvalidInterface(_("Incorrect URL used for feed.\n\n" 231 "%(feed_url)s is given in the feed, but\n" 232 "%(interface_uri)s was requested") % 233 {'feed_url': feed.url, 'interface_uri': interface.uri}) 234 235 if iface_cache is None: 236 import warnings 237 warnings.warn("iface_cache should be specified", DeprecationWarning, 2) 238 from zeroinstall.injector import policy 239 iface_cache = policy.get_deprecated_singleton_config().iface_cache 240 iface_cache._feeds[support.unicode(interface.uri)] = feed 241 242 return feed
243
244 -def load_feed(source, local = False, selections_ok = False):
245 """Load a feed from a local file. 246 @param source: the name of the file to read 247 @type source: str 248 @param local: this is a local feed 249 @type local: bool 250 @param selections_ok: if it turns out to be a local selections document, return that instead 251 @type selections_ok: bool 252 @return: the new feed 253 @rtype: L{ZeroInstallFeed} 254 @raise InvalidInterface: if the source's syntax is incorrect 255 @since: 0.48 256 @see: L{iface_cache.iface_cache}, which uses this to load the feeds""" 257 try: 258 with open(source, 'rb') as stream: 259 root = qdom.parse(stream, filter_for_version = True) 260 except IOError as ex: 261 if ex.errno == errno.ENOENT and local: 262 raise MissingLocalFeed(_("Feed not found. Perhaps this is a local feed that no longer exists? You can remove it from the list of feeds in that case.")) 263 raise InvalidInterface(_("Can't read file"), ex) 264 except Exception as ex: 265 raise InvalidInterface(_("Invalid XML"), ex) 266 267 if local: 268 if selections_ok and root.uri == XMLNS_IFACE and root.name == 'selections': 269 from zeroinstall.injector import selections 270 return selections.Selections(root) 271 assert os.path.isabs(source), source 272 local_path = source 273 else: 274 local_path = None 275 feed = ZeroInstallFeed(root, local_path) 276 feed.last_modified = int(os.stat(source).st_mtime) 277 return feed
278