2 Parses an XML interface into a Python representation.
5 # Copyright (C) 2009, Thomas Leonard
6 # See the README file for details, or visit http://0install.net.
8 from zeroinstall
import _
10 from logging
import debug
, info
, warn
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
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.
29 if interface
.uri
.startswith('/'):
30 debug(_("Loading local interface file '%s'"), interface
.uri
)
31 update(interface
, interface
.uri
, local
= True)
34 cached
= basedir
.load_first_cache(config_site
, 'interfaces', escape(interface
.uri
))
36 debug(_("Loading cached information for %(interface)s from %(cached)s"), {'interface': interface
, 'cached': 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
))
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
)
50 def update_user_overrides(interface
, main_feed
= None):
51 """Update an interface with user-supplied information.
52 @param interface: the interface object to update
53 @type interface: L{model.Interface}
54 @param main_feed: feed to update with last_checked information
55 @note: feed updates shouldn't really be here. main_feed may go away in future.
57 user
= basedir
.load_first_config(config_site
, config_prog
,
58 'user_overrides', escape(interface
.uri
))
63 root
= qdom
.parse(file(user
))
65 warn(_("Error reading '%(user)s': %(exception)s"), {'user': user
, 'exception': ex
})
68 # This is a bit wrong; this information is about the feed,
71 last_checked
= root
.getAttribute('last-checked')
73 main_feed
.last_checked
= int(last_checked
)
75 stability_policy
= root
.getAttribute('stability-policy')
77 interface
.set_stability_policy(stability_levels
[str(stability_policy
)])
79 for item
in root
.childNodes
:
80 if item
.uri
!= XMLNS_IFACE
: continue
81 if item
.name
== 'implementation':
82 id = item
.getAttribute('id')
84 if not (id.startswith('/') or id.startswith('.') or id.startswith('package:')):
86 impl
= interface
.implementations
.get(id, None)
88 debug(_("Ignoring user-override for unknown implementation %(id)s in %(interface)s"), {'id': id, 'interface': interface
})
91 user_stability
= item
.getAttribute('user-stability')
93 impl
.user_stability
= stability_levels
[str(user_stability
)]
94 elif item
.name
== 'feed':
95 feed_src
= item
.getAttribute('src')
97 raise InvalidInterface(_('Missing "src" attribute in <feed>'))
98 interface
.extra_feeds
.append(Feed(feed_src
, item
.getAttribute('arch'), True, langs
= item
.getAttribute('langs')))
100 def check_readable(interface_uri
, source
):
101 """Test whether an interface file is valid.
102 @param interface_uri: the interface's URI
103 @type interface_uri: str
104 @param source: the name of the file to test
106 @return: the modification time in src (usually just the mtime of the file)
108 @raise InvalidInterface: If the source's syntax is incorrect,
110 tmp
= Interface(interface_uri
)
113 except InvalidInterface
, ex
:
114 info(_("Error loading feed:\n"
115 "Interface URI: %(uri)s\n"
116 "Local file: %(source)s\n"
118 {'uri': interface_uri
, 'source': source
, 'exception': ex
})
119 raise InvalidInterface(_("Error loading feed '%(uri)s':\n\n%(exception)s") % {'uri': interface_uri
, 'exception': ex
})
120 return tmp
.last_modified
122 def update(interface
, source
, local
= False):
123 """Read in information about an interface.
124 @param interface: the interface object to update
125 @type interface: L{model.Interface}
126 @param source: the name of the file to read
128 @param local: use file's mtime for last-modified, and uri attribute is ignored
129 @raise InvalidInterface: if the source's syntax is incorrect
130 @return: the new feed (since 0.32)
131 @see: L{update_from_cache}, which calls this"""
132 assert isinstance(interface
, Interface
)
135 root
= qdom
.parse(file(source
))
138 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
)
139 raise InvalidInterface(_("Can't read file"), ex
)
140 except Exception, ex
:
141 raise InvalidInterface(_("Invalid XML"), ex
)
147 feed
= ZeroInstallFeed(root
, local_path
, distro
.get_host_distribution())
148 feed
.last_modified
= int(os
.stat(source
).st_mtime
)
151 if feed
.url
!= interface
.uri
:
152 raise InvalidInterface(_("Incorrect URL used for feed.\n\n"
153 "%(feed_url)s is given in the feed, but\n"
154 "%(interface_uri)s was requested") %
155 {'feed_url': feed
.url
, 'interface_uri': interface
.uri
})
157 interface
._main
_feed
= feed