Package yadis :: Module etxrd
[hide private]
[frames] | no frames]

Source Code for Module yadis.etxrd

  1  # -*- test-case-name: yadis.test.test_etxrd -*- 
  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  # Use expat if it's present. Otherwise, use xmllib 
 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   
35 -class XRDSError(Exception):
36 """An error with the XRDS document.""" 37 38 # The exception that triggered this exception 39 reason = None
40 41 42
43 -class XRDSFraud(XRDSError):
44 """Raised when there's an assertion in the XRDS that it does not have 45 the authority to make. 46 """
47 48 49
50 -def parseXRDS(text):
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
76 -def nsTag(ns, t):
77 return '{%s}%s' % (ns, t)
78
79 -def mkXRDTag(t):
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
87 -def mkXRDSTag(t):
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 # Tags that are used in Yadis documents 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 # Other XRD tags 103 canonicalID_tag = mkXRDTag('CanonicalID') 104
105 -def isXRDS(xrd_tree):
106 """Is this document an XRDS document?""" 107 root = xrd_tree.getroot() 108 return root.tag == root_tag
109
110 -def getYadisXRD(xrd_tree):
111 """Return the XRD element that should contain the Yadis services""" 112 xrd = None 113 114 # for the side-effect of assigning the last one in the list to the 115 # xrd variable 116 for xrd in xrd_tree.findall(xrd_tag): 117 pass 118 119 # There were no elements found, or else xrd would be set to the 120 # last one 121 if xrd is None: 122 raise XRDSError('No XRD present in tree') 123 124 return xrd
125 126
127 -def getCanonicalID(iname, xrd_tree):
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 # XXX: can't use rsplit until we require python >= 2.4. 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
167 -class _Max(object):
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."""
172 - def __cmp__(self, other):
173 if other is self: 174 return 0 175 176 return 1
177 178 Max = _Max() 179
180 -def getPriorityStrict(element):
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 # Any errors in parsing the priority fall through to here 196 return Max
197
198 -def getPriority(element):
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
208 -def prioSort(elements):
209 """Sort a list of elements that have priority attributes""" 210 # Randomize the services before sorting so that equal priority 211 # elements are load-balanced. 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
219 -def iterServices(xrd_tree):
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
226 -def sortedURIs(service_element):
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
232 -def getTypeURIs(service_element):
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
238 -def expandService(service_element):
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
253 -def expandServices(service_elements):
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