2 Parses an XML interface into a Python representation.
5 # Copyright (C) 2006, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
12 from logging
import debug
, warn
, info
13 from os
.path
import dirname
15 from zeroinstall
.support
import basedir
16 from zeroinstall
.injector
import qdom
, distro
17 from zeroinstall
.injector
.namespaces
import *
18 from zeroinstall
.injector
.model
import *
19 from zeroinstall
.injector
import model
20 from zeroinstall
import version
, SafeException
22 def update_from_cache(interface
):
23 """Read a cached interface and any native feeds or user overrides.
24 @param interface: the interface object to update
25 @type interface: L{model.Interface}
26 @return: True if cached version and user overrides loaded OK.
27 False if upstream not cached. Local interfaces (starting with /) are
28 always considered to be cached, although they are not actually stored in the cache.
33 if interface
.uri
.startswith('/'):
34 debug("Loading local interface file '%s'", interface
.uri
)
35 update(interface
, interface
.uri
, local
= True)
38 cached
= basedir
.load_first_cache(config_site
, 'interfaces', escape(interface
.uri
))
40 debug("Loading cached information for %s from %s", interface
, cached
)
41 main_feed
= update(interface
, cached
)
43 # Add the distribution package manager's version, if any
44 path
= basedir
.load_first_data(config_site
, 'native_feeds', model
._pretty
_escape
(interface
.uri
))
46 # Resolve any symlinks
47 info("Adding native packager feed '%s'", path
)
48 interface
.extra_feeds
.append(Feed(os
.path
.realpath(path
), None, False))
50 update_user_overrides(interface
, main_feed
)
52 # Special case: add our fall-back local copy of the injector as a feed
53 if interface
.uri
== injector_gui_uri
:
54 local_gui
= os
.path
.join(os
.path
.abspath(dirname(dirname(__file__
))), '0launch-gui', 'ZeroInstall-GUI.xml')
55 interface
.extra_feeds
.append(Feed(local_gui
, None, False))
59 def update_user_overrides(interface
, main_feed
= None):
60 """Update an interface with user-supplied information.
61 @param interface: the interface object to update
62 @type interface: L{model.Interface}
63 @param main_feed: feed to update with last_checked information
64 @note: feed updates shouldn't really be here. main_feed may go away in future.
66 user
= basedir
.load_first_config(config_site
, config_prog
,
67 'user_overrides', escape(interface
.uri
))
71 root
= qdom
.parse(file(user
))
73 # This is a bit wrong; this information is about the feed,
76 last_checked
= root
.getAttribute('last-checked')
78 main_feed
.last_checked
= int(last_checked
)
80 stability_policy
= root
.getAttribute('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')
89 if not (id.startswith('/') or id.startswith('.') or id.startswith('package:')):
91 impl
= interface
.implementations
.get(id, None)
93 debug("Ignoring user-override for unknown implementation %s in %s", id, interface
)
96 user_stability
= item
.getAttribute('user-stability')
98 impl
.user_stability
= stability_levels
[str(user_stability
)]
99 elif item
.name
== 'feed':
100 feed_src
= item
.getAttribute('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
111 @return: the modification time in src (usually just the mtime of the file)
113 @raise InvalidInterface: If the source's syntax is incorrect,
115 tmp
= Interface(interface_uri
)
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
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
)
139 root
= qdom
.parse(file(source
))
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
)
151 feed
= ZeroInstallFeed(root
, local_path
, distro
.get_host_distribution())
152 feed
.last_modified
= int(os
.stat(source
).st_mtime
)
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"
159 (feed
.url
, interface
.uri
))
161 interface
._main
_feed
= feed