1 """In-memory representation of interfaces and other data structures.
2
3 The objects in this module are used to build a representation of an XML interface
4 file in memory.
5
6 @see: L{reader} constructs these data-structures
7 @see: U{http://0install.net/interface-spec.html} description of the domain model
8
9 @var defaults: Default values for the 'default' attribute for <environment> bindings of
10 well-known variables.
11 """
12
13
14
15
16 import os, re
17 from logging import warn, info, debug
18 from zeroinstall import SafeException, version
19 from zeroinstall.injector.namespaces import XMLNS_IFACE
20
21
22 binding_names = frozenset(['environment'])
23
24 network_offline = 'off-line'
25 network_minimal = 'minimal'
26 network_full = 'full'
27 network_levels = (network_offline, network_minimal, network_full)
28
29 stability_levels = {}
30
31 defaults = {
32 'PATH': '/bin:/usr/bin',
33 'XDG_CONFIG_DIRS': '/etc/xdg',
34 'XDG_DATA_DIRS': '/usr/local/share:/usr/share',
35 }
36
38 """Raised when parsing an invalid feed."""
43
45 """Split an arch into an (os, machine) tuple. Either or both parts may be None."""
46 if not arch:
47 return None, None
48 elif '-' not in arch:
49 raise SafeException("Malformed arch '%s'" % arch)
50 else:
51 os, machine = arch.split('-', 1)
52 if os == '*': os = None
53 if machine == '*': machine = None
54 return os, machine
55
59
61 """A stability rating. Each implementation has an upstream stability rating and,
62 optionally, a user-set rating."""
63 __slots__ = ['level', 'name', 'description']
64 - def __init__(self, level, name, description):
70
72 return cmp(self.level, other.level)
73
76
79
99
117
118
119 insecure = Stability(0, 'insecure', 'This is a security risk')
120 buggy = Stability(5, 'buggy', 'Known to have serious bugs')
121 developer = Stability(10, 'developer', 'Work-in-progress - bugs likely')
122 testing = Stability(20, 'testing', 'Stability unknown - please test!')
123 stable = Stability(30, 'stable', 'Tested - no serious problems found')
124 packaged = Stability(35, 'packaged', 'Supplied by the local package manager')
125 preferred = Stability(40, 'preferred', 'Best of all - must be set manually')
126
128 """A Restriction limits the allowed implementations of an Interface."""
129 __slots__ = []
130
132 raise NotImplementedError("Abstract")
133
135 """Only versions within the given range are acceptable"""
136 __slots__ = ['before', 'not_before']
137 - def __init__(self, before, not_before):
138 self.before = before
139 self.not_before = not_before
140
142 if self.not_before and impl.version < self.not_before:
143 return False
144 if self.before and impl.version >= self.before:
145 return False
146 return True
147
149 if self.not_before is not None or self.before is not None:
150 range = ''
151 if self.not_before is not None:
152 range += format_version(self.not_before) + ' <= '
153 range += 'version'
154 if self.before is not None:
155 range += ' < ' + format_version(self.before)
156 else:
157 range = 'none'
158 return "(restriction: %s)" % range
159
161 """Information about how the choice of a Dependency is made known
162 to the application being run."""
163
165 """Indicate the chosen implementation using an environment variable."""
166 __slots__ = ['name', 'insert', 'default', 'mode']
167
168 PREPEND = 'prepend'
169 APPEND = 'append'
170 REPLACE = 'replace'
171
173 """mode argument added in version 0.28"""
174 self.name = name
175 self.insert = insert
176 self.default = default
177 self.mode = mode
178
180 return "<environ %s %s %s>" % (self.name, self.mode, self.insert)
181
182 __repr__ = __str__
183
185 """Calculate the new value of the environment variable after applying this binding.
186 @param path: the path to the selected implementation
187 @param old_value: the current value of the environment variable
188 @return: the new value for the environment variable"""
189 extra = os.path.join(path, self.insert)
190
191 if self.mode == EnvironmentBinding.REPLACE:
192 return extra
193
194 if old_value is None:
195 old_value = self.default or defaults.get(self.name, None)
196 if old_value is None:
197 return extra
198 if self.mode == EnvironmentBinding.PREPEND:
199 return extra + ':' + old_value
200 else:
201 return old_value + ':' + extra
202
204 """Create a DOM element for this binding.
205 @param doc: document to use to create the element
206 @return: the new element
207 """
208 env_elem = doc.createElementNS(XMLNS_IFACE, 'environment')
209 env_elem.setAttributeNS(None, 'name', self.name)
210 env_elem.setAttributeNS(None, 'insert', self.insert)
211 if self.default:
212 env_elem.setAttributeNS(None, 'default', self.default)
213 return env_elem
214
216 """An interface's feeds are other interfaces whose implementations can also be
217 used as implementations of this interface."""
218 __slots__ = ['uri', 'os', 'machine', 'user_override', 'langs']
219 - def __init__(self, uri, arch, user_override, langs = None):
220 self.uri = uri
221
222
223 self.user_override = user_override
224 self.os, self.machine = _split_arch(arch)
225 self.langs = langs
226
228 return "<Feed from %s>" % self.uri
229 __repr__ = __str__
230
231 arch = property(lambda self: _join_arch(self.os, self.machine))
232
234 """A Dependency indicates that an Implementation requires some additional
235 code to function. This is an abstract base class.
236 @ivar metadata: any extra attributes from the XML element
237 @type metadata: {str: str}
238 """
239 __slots__ = ['metadata']
240
247
249 """A Dependency on a Zero Install interface.
250 @ivar interface: the interface required by this dependency
251 @type interface: str
252 @ivar restrictions: a list of constraints on acceptable implementations
253 @type restrictions: [L{Restriction}]
254 @ivar bindings: how to make the choice of implementation known
255 @type bindings: [L{Binding}]
256 @since: 0.28
257 """
258 __slots__ = ['interface', 'restrictions', 'bindings', 'metadata']
259
260 - def __init__(self, interface, restrictions = None, metadata = None):
270
272 return "<Dependency on %s; bindings: %s%s>" % (self.interface, self.bindings, self.restrictions)
273
275 """A RetrievalMethod provides a way to fetch an implementation."""
276 __slots__ = []
277
279 """A DownloadSource provides a way to fetch an implementation."""
280 __slots__ = ['implementation', 'url', 'size', 'extract', 'start_offset', 'type']
281
282 - def __init__(self, implementation, url, size, extract, start_offset = 0, type = None):
289
290 -class Recipe(RetrievalMethod):
291 """Get an implementation by following a series of steps.
292 @ivar size: the combined download sizes from all the steps
293 @type size: int
294 @ivar steps: the sequence of steps which must be performed
295 @type steps: [L{RetrievalMethod}]"""
296 __slots__ = ['steps']
297