1
2 """
3 ElementTree interface to an XRD document.
4 """
5
6 __all__ = [
7 'nsTag',
8 'mkXRDTag',
9 'isXRDS',
10 'parseXRDS',
11 'getCanonicalID',
12 'getYadisXRD',
13 'getPriorityStrict',
14 'getPriority',
15 'prioSort',
16 'iterServices',
17 'expandService',
18 'expandServices',
19 ]
20
21 import random
22
23 from elementtree.ElementTree import ElementTree
24
25
26 try:
27 from xml.parsers.expat import ExpatError as XMLError
28 from elementtree.ElementTree import XMLTreeBuilder
29 except ImportError:
30 from elementtree.SimpleXMLTreeBuilder import TreeBuilder as XMLTreeBuilder
31 from xmllib import Error as XMLError
32
33 from yadis import xri
34
36 """An error with the XRDS document."""
37
38
39 reason = None
40
41
42
44 """Raised when there's an assertion in the XRDS that it does not have
45 the authority to make.
46 """
47
48
49
51 """Parse the given text as an XRDS document.
52
53 @return: ElementTree containing an XRDS document
54
55 @raises XRDSError: When there is a parse error or the document does
56 not contain an XRDS.
57 """
58 try:
59 parser = XMLTreeBuilder()
60 parser.feed(text)
61 element = parser.close()
62 except XMLError, why:
63 exc = XRDSError('Error parsing document as XML')
64 exc.reason = why
65 raise exc
66 else:
67 tree = ElementTree(element)
68 if not isXRDS(tree):
69 raise XRDSError('Not an XRDS document')
70
71 return tree
72
73 XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
74 XRDS_NS = 'xri://$xrds'
75
77 return '{%s}%s' % (ns, t)
78
80 """basestring -> basestring
81
82 Create a tag name in the XRD 2.0 XML namespace suitable for using
83 with ElementTree
84 """
85 return nsTag(XRD_NS_2_0, t)
86
88 """basestring -> basestring
89
90 Create a tag name in the XRDS XML namespace suitable for using
91 with ElementTree
92 """
93 return nsTag(XRDS_NS, t)
94
95
96 root_tag = mkXRDSTag('XRDS')
97 service_tag = mkXRDTag('Service')
98 xrd_tag = mkXRDTag('XRD')
99 type_tag = mkXRDTag('Type')
100 uri_tag = mkXRDTag('URI')
101
102
103 canonicalID_tag = mkXRDTag('CanonicalID')
104
106 """Is this document an XRDS document?"""
107 root = xrd_tree.getroot()
108 return root.tag == root_tag
109
111 """Return the XRD element that should contain the Yadis services"""
112 xrd = None
113
114
115
116 for xrd in xrd_tree.findall(xrd_tag):
117 pass
118
119
120
121 if xrd is None:
122 raise XRDSError('No XRD present in tree')
123
124 return xrd
125
126
128 """Return the CanonicalID from this XRDS document.
129
130 @param iname: the XRI being resolved.
131 @type iname: unicode
132
133 @param xrd_tree: The XRDS output from the resolver.
134 @type xrd_tree: ElementTree
135
136 @returns: The XRI CanonicalID or None.
137 @returntype: unicode or None
138 """
139 xrd_list = xrd_tree.findall(xrd_tag)
140 xrd_list.reverse()
141
142 try:
143 canonicalID = xri.XRI(xrd_list[0].findall(canonicalID_tag)[-1].text)
144 except IndexError:
145 return None
146
147 childID = canonicalID
148
149 for xrd in xrd_list[1:]:
150
151 parent_sought = childID[:childID.rindex('!')]
152 parent_list = [xri.XRI(c.text) for c in xrd.findall(canonicalID_tag)]
153 if parent_sought not in parent_list:
154 raise XRDSFraud("%r can not come from any of %s" % (parent_sought,
155 parent_list))
156
157 childID = parent_sought
158
159 root = xri.rootAuthority(iname)
160 if not xri.providerIsAuthoritative(root, childID):
161 raise XRDSFraud("%r can not come from root %r" % (childID, root))
162
163 return canonicalID
164
165
166
168 """Value that compares greater than any other value.
169
170 Should only be used as a singleton. Implemented for use as a
171 priority value for when a priority is not specified."""
173 if other is self:
174 return 0
175
176 return 1
177
178 Max = _Max()
179
181 """Get the priority of this element.
182
183 Raises ValueError if the value of the priority is invalid. If no
184 priority is specified, it returns a value that compares greater
185 than any other value.
186 """
187 prio_str = element.get('priority')
188 if prio_str is not None:
189 prio_val = int(prio_str)
190 if prio_val >= 0:
191 return prio_val
192 else:
193 raise ValueError('Priority values must be non-negative integers')
194
195
196 return Max
197
199 """Get the priority of this element
200
201 Returns Max if no priority is specified or the priority value is invalid.
202 """
203 try:
204 return getPriorityStrict(element)
205 except ValueError:
206 return Max
207
209 """Sort a list of elements that have priority attributes"""
210
211
212 random.shuffle(elements)
213
214 prio_elems = [(getPriority(e), e) for e in elements]
215 prio_elems.sort()
216 sorted_elems = [s for (_, s) in prio_elems]
217 return sorted_elems
218
220 """Return an iterable over the Service elements in the Yadis XRD
221
222 sorted by priority"""
223 xrd = getYadisXRD(xrd_tree)
224 return prioSort(xrd.findall(service_tag))
225
227 """Given a Service element, return a list of the contents of all
228 URI tags in priority order."""
229 return [uri_element.text for uri_element
230 in prioSort(service_element.findall(uri_tag))]
231
233 """Given a Service element, return a list of the contents of all
234 Type tags"""
235 return [type_element.text for type_element
236 in service_element.findall(type_tag)]
237
239 """Take a service element and expand it into an iterator of:
240 ([type_uri], uri, service_element)
241 """
242 uris = sortedURIs(service_element)
243 if not uris:
244 uris = [None]
245
246 expanded = []
247 for uri in uris:
248 type_uris = getTypeURIs(service_element)
249 expanded.append((type_uris, uri, service_element))
250
251 return expanded
252
254 """Take a sorted iterator of service elements and expand it into a
255 sorted iterator of:
256 ([type_uri], uri, service_element)
257
258 There may be more than one item in the resulting list for each
259 service element if there is more than one URI or type for a
260 service, but each triple will be unique.
261
262 If there is no URI or Type for a Service element, it will not
263 appear in the result.
264 """
265 expanded = []
266 for service_element in service_elements:
267 expanded.extend(expandService(service_element))
268
269 return expanded
270