Start development series 0.42.1-post
[zeroinstall/zeroinstall-rsl.git] / zeroinstall / injector / qdom.py
blob7cf5426cb51e5130c4e9eec475c3e5a8ce04faff
1 """A quick DOM implementation.
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 """
7 # Copyright (C) 2009, Thomas Leonard
8 # See the README file for details, or visit http://0install.net.
10 from zeroinstall import _
11 from xml.parsers import expat
13 class Element(object):
14 """An XML element.
15 @ivar uri: the element's namespace
16 @type uri: str
17 @ivar name: the element's localName
18 @type name: str
19 @ivar attrs: the element's attributes (key is in the form [namespace " "] localName
20 @type attrs: {str: str}
21 @ivar childNodes: children
22 @type childNodes: [L{Element}]
23 @ivar content: the text content
24 @type content: str"""
25 __slots__ = ['uri', 'name', 'attrs', 'childNodes', 'content']
26 def __init__(self, uri, name, attrs):
27 self.uri = uri
28 self.name = name
29 self.attrs = attrs.copy()
30 self.childNodes = []
32 def __str__(self):
33 attrs = [n + '=' + self.attrs[n] for n in self.attrs]
34 start = '<{%s}%s %s' % (self.uri, self.name, ' '.join(attrs))
35 if self.childNodes:
36 return start + '>' + '\n'.join(map(str, self.childNodes)) + ('</%s>' % (self.name))
37 elif self.content:
38 return start + '>' + self.content + ('</%s>' % (self.name))
39 else:
40 return start + '/>'
42 def getAttribute(self, name):
43 return self.attrs.get(name, None)
45 class QSAXhandler:
46 """SAXHandler that builds a tree of L{Element}s"""
47 def __init__(self):
48 self.stack = []
50 def startElementNS(self, fullname, attrs):
51 split = fullname.split(' ', 1)
52 if len(split) == 2:
53 self.stack.append(Element(split[0], split[1], attrs))
54 else:
55 self.stack.append(Element(None, fullname, attrs))
56 self.contents = ''
58 def characters(self, data):
59 self.contents += data
61 def endElementNS(self, name):
62 contents = self.contents.strip()
63 self.stack[-1].content = contents
64 self.contents = ''
65 new = self.stack.pop()
66 if self.stack:
67 self.stack[-1].childNodes.append(new)
68 else:
69 self.doc = new
71 def parse(source):
72 """Parse an XML stream into a tree of L{Element}s.
73 @param source: data to parse
74 @type source: file
75 @return: the root
76 @rtype: L{Element}"""
77 handler = QSAXhandler()
78 parser = expat.ParserCreate(namespace_separator = ' ')
80 parser.StartElementHandler = handler.startElementNS
81 parser.EndElementHandler = handler.endElementNS
82 parser.CharacterDataHandler = handler.characters
84 parser.ParseFile(source)
85 return handler.doc