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
6
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
21
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
34
35 interface.extra_feeds.append(Feed(feed, None, user_override = True, site_package = True))
36 known_site_feeds.add(feed)
37
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
56 path = basedir.load_first_data(config_site, 'native_feeds', model._pretty_escape(interface.uri))
57 if path:
58
59 logger.info(_("Adding native packager feed '%s'"), path)
60 interface.extra_feeds.append(Feed(os.path.realpath(path), None, False))
61
62
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
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
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
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
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
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
174
175 if item.getAttribute('is-site-package'):
176
177
178
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
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