Update year to 2009 in various places
[zeroinstall/zeroinstall-rsl.git] / zeroinstall / injector / reader.py
blobc5f9c2950414ec719b34bc89f99b2af345a25f84
1 """
2 Parses an XML interface into a Python representation.
3 """
5 # Copyright (C) 2009, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 import os
9 from logging import debug, info, warn
10 from os.path import dirname
12 from zeroinstall.support import basedir
13 from zeroinstall.injector import qdom, distro
14 from zeroinstall.injector.namespaces import config_site, config_prog, XMLNS_IFACE, injector_gui_uri
15 from zeroinstall.injector.model import Interface, InvalidInterface, ZeroInstallFeed, escape, Feed, stability_levels
16 from zeroinstall.injector import model
18 def update_from_cache(interface):
19 """Read a cached interface and any native feeds or user overrides.
20 @param interface: the interface object to update
21 @type interface: L{model.Interface}
22 @return: True if cached version and user overrides loaded OK.
23 False if upstream not cached. Local interfaces (starting with /) are
24 always considered to be cached, although they are not actually stored in the cache.
25 @rtype: bool"""
26 interface.reset()
27 main_feed = None
29 if interface.uri.startswith('/'):
30 debug("Loading local interface file '%s'", interface.uri)
31 update(interface, interface.uri, local = True)
32 cached = True
33 else:
34 cached = basedir.load_first_cache(config_site, 'interfaces', escape(interface.uri))
35 if cached:
36 debug("Loading cached information for %s from %s", interface, cached)
37 main_feed = update(interface, cached)
39 # Add the distribution package manager's version, if any
40 path = basedir.load_first_data(config_site, 'native_feeds', model._pretty_escape(interface.uri))
41 if path:
42 # Resolve any symlinks
43 info("Adding native packager feed '%s'", path)
44 interface.extra_feeds.append(Feed(os.path.realpath(path), None, False))
46 update_user_overrides(interface, main_feed)
48 # Special case: add our fall-back local copy of the injector as a feed
49 if interface.uri == injector_gui_uri:
50 local_gui = os.path.join(os.path.abspath(dirname(dirname(__file__))), '0launch-gui', 'ZeroInstall-GUI.xml')
51 interface.extra_feeds.append(Feed(local_gui, None, False))
53 return bool(cached)
55 def update_user_overrides(interface, main_feed = None):
56 """Update an interface with user-supplied information.
57 @param interface: the interface object to update
58 @type interface: L{model.Interface}
59 @param main_feed: feed to update with last_checked information
60 @note: feed updates shouldn't really be here. main_feed may go away in future.
61 """
62 user = basedir.load_first_config(config_site, config_prog,
63 'user_overrides', escape(interface.uri))
64 if not user:
65 return
67 try:
68 root = qdom.parse(file(user))
69 except Exception, ex:
70 warn("Error reading '%s': %s", user, ex)
71 raise
73 # This is a bit wrong; this information is about the feed,
74 # not the interface.
75 if main_feed:
76 last_checked = root.getAttribute('last-checked')
77 if last_checked:
78 main_feed.last_checked = int(last_checked)
80 stability_policy = root.getAttribute('stability-policy')
81 if stability_policy:
82 interface.set_stability_policy(stability_levels[str(stability_policy)])
84 for item in root.childNodes:
85 if item.uri != XMLNS_IFACE: continue
86 if item.name == 'implementation':
87 id = item.getAttribute('id')
88 assert id is not None
89 if not (id.startswith('/') or id.startswith('.') or id.startswith('package:')):
90 assert '=' in id
91 impl = interface.implementations.get(id, None)
92 if not impl:
93 debug("Ignoring user-override for unknown implementation %s in %s", id, interface)
94 continue
96 user_stability = item.getAttribute('user-stability')
97 if user_stability:
98 impl.user_stability = stability_levels[str(user_stability)]
99 elif item.name == 'feed':
100 feed_src = item.getAttribute('src')
101 if not feed_src:
102 raise InvalidInterface('Missing "src" attribute in <feed>')
103 interface.extra_feeds.append(Feed(feed_src, item.getAttribute('arch'), True, langs = item.getAttribute('langs')))
105 def check_readable(interface_uri, source):
106 """Test whether an interface file is valid.
107 @param interface_uri: the interface's URI
108 @type interface_uri: str
109 @param source: the name of the file to test
110 @type source: str
111 @return: the modification time in src (usually just the mtime of the file)
112 @rtype: int
113 @raise InvalidInterface: If the source's syntax is incorrect,
115 tmp = Interface(interface_uri)
116 try:
117 update(tmp, source)
118 except InvalidInterface, ex:
119 info("Error loading feed:\n"
120 "Interface URI: %s\n"
121 "Local file: %s\n%s" %
122 (interface_uri, source, ex))
123 raise InvalidInterface("Error loading feed '%s':\n\n%s" % (interface_uri, ex))
124 return tmp.last_modified
126 def update(interface, source, local = False):
127 """Read in information about an interface.
128 @param interface: the interface object to update
129 @type interface: L{model.Interface}
130 @param source: the name of the file to read
131 @type source: str
132 @param local: use file's mtime for last-modified, and uri attribute is ignored
133 @raise InvalidInterface: if the source's syntax is incorrect
134 @return: the new feed (since 0.32)
135 @see: L{update_from_cache}, which calls this"""
136 assert isinstance(interface, Interface)
138 try:
139 root = qdom.parse(file(source))
140 except IOError, ex:
141 if ex.errno == 2:
142 raise InvalidInterface("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.", ex)
143 raise InvalidInterface("Can't read file", ex)
144 except Exception, ex:
145 raise InvalidInterface("Invalid XML", ex)
147 if local:
148 local_path = source
149 else:
150 local_path = None
151 feed = ZeroInstallFeed(root, local_path, distro.get_host_distribution())
152 feed.last_modified = int(os.stat(source).st_mtime)
154 if not local:
155 if feed.url != interface.uri:
156 raise InvalidInterface("Incorrect URL used for feed.\n\n"
157 "%s is given in the feed, but\n"
158 "%s was requested" %
159 (feed.url, interface.uri))
161 interface._main_feed = feed
162 return feed