Removed warning about old GnuPG.
[zeroinstall.git] / zeroinstall / injector / selections.py
blobe01df9b466e28af5608c856ff7388bfaa4756153
1 """
2 Load and save a set of chosen implementations.
3 @since: 0.27
4 """
6 # Copyright (C) 2007, Thomas Leonard
7 # See the README file for details, or visit http://0install.net.
9 import os
11 from zeroinstall.injector.policy import Policy
12 from zeroinstall.injector.model import EnvironmentBinding, InterfaceDependency, process_binding, process_depends, binding_names
13 from zeroinstall.injector.namespaces import XMLNS_IFACE
14 from zeroinstall.injector.qdom import Element
16 class Selection(object):
17 """A single selected implementation in a L{Selections} set.
18 @ivar dependencies: list of dependencies
19 @type dependencies: [L{model.Dependency}]
20 @ivar version: the implementation's version number
21 @type version: str"""
22 __slots__ = ['interface', 'bindings', 'id', 'version', 'feed', 'dependencies', 'main']
24 def __init__(self, interface, id, version, feed, main, dependencies, bindings = None):
25 assert interface
26 assert id
27 assert version
28 assert feed
29 if bindings is None: bindings = []
30 self.interface = interface
31 self.id = id
32 self.version = version
33 self.feed = feed
34 self.main = main
35 self.dependencies = dependencies
36 self.bindings = bindings
38 def __repr__(self):
39 return self.id
41 class Selections(object):
42 """
43 A selected set of components which will make up a complete program.
44 @ivar interface: the interface of the program
45 @type interface: str
46 @ivar selections: the selected implementations
47 @type selections: {str: L{Selection}}
48 """
49 __slots__ = ['interface', 'selections']
51 def __init__(self, source):
52 self.selections = {}
53 if isinstance(source, Policy):
54 self._init_from_policy(source)
55 elif isinstance(source, Element):
56 self._init_from_qdom(source)
57 else:
58 raise Exception("Source not a Policy or qdom.Element!")
60 def _init_from_policy(self, policy):
61 """Set the selections from a policy.
62 @param policy: the policy giving the selected implementations."""
63 self.interface = policy.root
65 for needed_iface in policy.implementation:
66 impl = policy.implementation[needed_iface]
67 assert impl
69 self.selections[needed_iface.uri] = Selection(needed_iface.uri, impl.id,
70 impl.get_version(), impl.feed.url,
71 impl.main,
72 impl.requires,
73 impl.bindings)
75 def _init_from_qdom(self, root):
76 """Parse and load a selections document.
77 @param root: a saved set of selections."""
78 self.interface = root.getAttribute('interface')
79 assert self.interface
81 for selection in root.childNodes:
82 if selection.uri != XMLNS_IFACE:
83 continue
84 if selection.name != 'selection':
85 continue
87 requires = []
88 bindings = []
89 for dep_elem in selection.childNodes:
90 if dep_elem.uri != XMLNS_IFACE:
91 continue
92 if dep_elem.name in binding_names:
93 bindings.append(process_binding(dep_elem))
94 elif dep_elem.name == 'requires':
95 dep = process_depends(dep_elem)
96 requires.append(dep)
98 iface_uri = selection.getAttribute('interface')
99 s = Selection(iface_uri,
100 selection.getAttribute('id'),
101 selection.getAttribute('version'),
102 selection.getAttribute('from-feed') or iface_uri,
103 selection.getAttribute('main'),
104 requires,
105 bindings)
106 self.selections[iface_uri] = s
108 def toDOM(self):
109 """Create a DOM document for the selected implementations.
110 The document gives the URI of the root, plus each selected implementation.
111 For each selected implementation, we record the ID, the version, the URI and
112 (if different) the feed URL. We also record all the bindings needed.
113 @return: a new DOM Document"""
114 from xml.dom import minidom, XMLNS_NAMESPACE
116 assert self.interface
118 impl = minidom.getDOMImplementation()
120 doc = impl.createDocument(XMLNS_IFACE, "selections", None)
122 root = doc.documentElement
123 root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns', XMLNS_IFACE)
125 root.setAttributeNS(None, 'interface', self.interface)
127 def ensure_prefix(prefixes, ns):
128 prefix = prefixes.get(ns, None)
129 if prefix:
130 return prefix
131 prefix = 'ns%d' % len(prefixes)
132 prefixes[ns] = prefix
133 return prefix
135 prefixes = {}
137 for iface, selection in sorted(self.selections.items()):
138 selection_elem = doc.createElementNS(XMLNS_IFACE, 'selection')
139 selection_elem.setAttributeNS(None, 'interface', selection.interface)
140 root.appendChild(selection_elem)
142 selection_elem.setAttributeNS(None, 'id', selection.id)
143 selection_elem.setAttributeNS(None, 'version', selection.version)
144 if selection.main:
145 selection_elem.setAttributeNS(None, 'main', selection.main)
146 if selection.interface != selection.feed:
147 selection_elem.setAttributeNS(None, 'from-feed', selection.feed)
149 for b in selection.bindings:
150 selection_elem.appendChild(b._toxml(doc))
152 for dep in selection.dependencies:
153 dep_elem = doc.createElementNS(XMLNS_IFACE, 'requires')
154 dep_elem.setAttributeNS(None, 'interface', dep.interface)
155 selection_elem.appendChild(dep_elem)
157 for m in dep.metadata:
158 parts = m.split(' ', 1)
159 if len(parts) == 1:
160 ns = None
161 localName = parts[0]
162 dep_elem.setAttributeNS(None, localName, dep.metadata[m])
163 else:
164 ns, localName = parts
165 dep_elem.setAttributeNS(ns, ensure_prefix(prefixes, ns) + ':' + localName, dep.metadata[m])
167 for b in dep.bindings:
168 dep_elem.appendChild(b._toxml(doc))
170 for ns, prefix in prefixes.items():
171 root.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:' + prefix, ns)
173 return doc
175 def __repr__(self):
176 return "Selections for " + self.interface