Package zeroinstall :: Package injector :: Module qdom
[frames] | no frames]

Source Code for Module zeroinstall.injector.qdom

  1  """A quick DOM implementation. 
  2   
  3  Python's xml.dom is very slow. The xml.sax module is also slow (as it imports urllib2). 
  4  This is our light-weight version. 
  5  """ 
  6   
  7  # Copyright (C) 2009, Thomas Leonard 
  8  # See the README file for details, or visit http://0install.net. 
  9   
 10  from xml.parsers import expat 
 11   
 12  import zeroinstall 
 13  from zeroinstall.injector import versions 
 14   
 15  _parsed_version = versions.parse_version(zeroinstall.version) 
 16   
17 -class Element(object):
18 """An XML element. 19 @ivar uri: the element's namespace 20 @type uri: str 21 @ivar name: the element's localName 22 @type name: str 23 @ivar attrs: the element's attributes (key is in the form [namespace " "] localName) 24 @type attrs: {str: str} 25 @ivar childNodes: children 26 @type childNodes: [L{Element}] 27 @ivar content: the text content 28 @type content: str""" 29 __slots__ = ['uri', 'name', 'attrs', 'childNodes', 'content']
30 - def __init__(self, uri, name, attrs):
31 """@type uri: str 32 @type name: str 33 @type attrs: {str: str}""" 34 self.uri = uri 35 self.name = name 36 self.attrs = attrs.copy() 37 self.content = None 38 self.childNodes = []
39
40 - def __str__(self):
41 """@rtype: str""" 42 attrs = [n + '=' + self.attrs[n] for n in self.attrs] 43 start = '<{%s}%s %s' % (self.uri, self.name, ' '.join(attrs)) 44 if self.childNodes: 45 return start + '>' + '\n'.join(map(str, self.childNodes)) + ('</%s>' % (self.name)) 46 elif self.content: 47 return start + '>' + self.content + ('</%s>' % (self.name)) 48 else: 49 return start + '/>'
50
51 - def getAttribute(self, name):
52 """@type name: str 53 @rtype: str""" 54 return self.attrs.get(name, None)
55
56 - def toDOM(self, doc, prefixes):
57 """Create a DOM Element for this qdom.Element. 58 @param doc: document to use to create the element 59 @type prefixes: L{Prefixes} 60 @return: the new element""" 61 elem = prefixes.createElementNS(doc, self.uri, self.name) 62 63 for fullname, value in self.attrs.items(): 64 if ' ' in fullname: 65 ns, localName = fullname.split(' ', 1) 66 else: 67 ns, localName = None, fullname 68 prefixes.setAttributeNS(elem, ns, localName, value) 69 for child in self.childNodes: 70 elem.appendChild(child.toDOM(doc, prefixes)) 71 if self.content: 72 elem.appendChild(doc.createTextNode(self.content)) 73 return elem
74
75 -class QSAXhandler(object):
76 """SAXHandler that builds a tree of L{Element}s"""
77 - def __init__(self, filter_for_version = False):
78 """@param filter_for_version: skip elements if their if-0install-version attribute doesn't match L{zeroinstall.version} (since 1.13). 79 @type filter_for_version: bool 80 @rtype: bool""" 81 self.stack = [] 82 if filter_for_version: 83 self.filter_range = lambda expr: versions.parse_version_expression(expr)(_parsed_version) 84 else: 85 self.filter_range = lambda x: True
86
87 - def startElementNS(self, fullname, attrs):
88 """@type fullname: str 89 @type attrs: {str: str}""" 90 split = fullname.split(' ', 1) 91 if len(split) == 2: 92 self.stack.append(Element(split[0], split[1], attrs)) 93 else: 94 self.stack.append(Element(None, fullname, attrs)) 95 self.contents = ''
96
97 - def characters(self, data):
98 """@type data: str""" 99 self.contents += data
100
101 - def endElementNS(self, name):
102 """@type name: str""" 103 contents = self.contents.strip() 104 self.stack[-1].content = contents 105 self.contents = '' 106 new = self.stack.pop() 107 if self.stack: 108 target_versions = new.attrs.get('if-0install-version') 109 if target_versions and not self.filter_range(target_versions): 110 return 111 112 self.stack[-1].childNodes.append(new) 113 else: 114 self.doc = new
115
116 -def parse(source, filter_for_version = False):
117 """Parse an XML stream into a tree of L{Element}s. 118 @param source: data to parse 119 @type source: file 120 @param filter_for_version: skip elements if their if-0install-version attribute doesn't match L{zeroinstall.version} (since 1.13). 121 @type filter_for_version: bool 122 @return: the root 123 @rtype: L{Element}""" 124 handler = QSAXhandler(filter_for_version) 125 parser = expat.ParserCreate(namespace_separator = ' ') 126 127 parser.StartElementHandler = handler.startElementNS 128 parser.EndElementHandler = handler.endElementNS 129 parser.CharacterDataHandler = handler.characters 130 131 parser.ParseFile(source) 132 return handler.doc
133
134 -class Prefixes(object):
135 """Keep track of namespace prefixes. Used when serialising a document. 136 @since: 0.54 137 """
138 - def __init__(self, default_ns):
139 """@type default_ns: str""" 140 self.prefixes = {} 141 self.default_ns = default_ns
142
143 - def get(self, ns):
144 """@type ns: str 145 @rtype: str""" 146 prefix = self.prefixes.get(ns, None) 147 if prefix: 148 return prefix 149 prefix = 'ns%d' % len(self.prefixes) 150 self.prefixes[ns] = prefix 151 return prefix
152
153 - def setAttributeNS(self, elem, uri, localName, value):
154 """@type uri: str 155 @type localName: str 156 @type value: str""" 157 if uri is None: 158 elem.setAttributeNS(None, localName, value) 159 else: 160 elem.setAttributeNS(uri, self.get(uri) + ':' + localName, value)
161
162 - def createElementNS(self, doc, uri, localName):
163 """@type uri: str 164 @type localName: str""" 165 if uri == self.default_ns: 166 return doc.createElementNS(uri, localName) 167 else: 168 return doc.createElementNS(uri, self.get(uri) + ':' + localName)
169