1 #!/usr/bin/env python 2 3 """ 4 DOM wrapper around libxml2, specifically the libxml2mod Python extension module. 5 """ 6 7 __version__ = "0.2" 8 9 import libxml2 10 from libxml2dom.macrolib import * 11 from libxml2dom.macrolib import \ 12 createDocument as Node_createDocument, \ 13 parseString as Node_parseString, parseURI as Node_parseURI, \ 14 parseFile as Node_parseFile, \ 15 toString as Node_toString, toStream as Node_toStream, \ 16 toFile as Node_toFile 17 import weakref 18 19 # NOTE: Consider a generator instead. 20 21 class NamedNodeMap(object): 22 23 """ 24 A wrapper around Node objects providing DOM and dictionary convenience 25 methods. 26 """ 27 28 def __init__(self, node): 29 self.node = node 30 31 def getNamedItem(self, name): 32 return self.node.getAttributeNode(name) 33 34 def getNamedItemNS(self, ns, localName): 35 return self.node.getAttributeNodeNS(ns, localName) 36 37 def setNamedItem(self, node): 38 self.node.setAttributeNode(node.name, node) 39 40 def setNamedItemNS(self, node): 41 self.node.setAttributeNodeNS(node.namespaceURI, node.localName, node) 42 43 def __getitem__(self, name): 44 return self.getNamedItem(name) 45 46 def __setitem__(self, name, node): 47 if name == node.nodeName: 48 self.setNamedItem(node) 49 else: 50 raise KeyError, name 51 52 def __delitem__(self, name): 53 # NOTE: To be implemented. 54 pass 55 56 def values(self): 57 return [Node(_node, self.ownerDocument) for _node in Node_attributes(self.node.as_native_node()).values()] 58 59 def keys(self): 60 return [(attr.namespaceURI, attr.localName) for attr in self.values()] 61 62 def items(self): 63 return [((attr.namespaceURI, attr.localName), attr) for attr in self.values()] 64 65 def __repr__(self): 66 return str(self) 67 68 def __str__(self): 69 return "{%s}" % ",\n".join(["%s : %s" % (repr(key), repr(value)) for key, value in self.items()]) 70 71 class Node(object): 72 73 """ 74 A DOM-style wrapper around libxml2mod objects. 75 """ 76 77 ATTRIBUTE_NODE = xml.dom.Node.ATTRIBUTE_NODE 78 COMMENT_NODE = xml.dom.Node.COMMENT_NODE 79 DOCUMENT_NODE = xml.dom.Node.DOCUMENT_NODE 80 DOCUMENT_TYPE_NODE = xml.dom.Node.DOCUMENT_TYPE_NODE 81 ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE 82 ENTITY_NODE = xml.dom.Node.ENTITY_NODE 83 ENTITY_REFERENCE_NODE = xml.dom.Node.ENTITY_REFERENCE_NODE 84 NOTATION_NODE = xml.dom.Node.NOTATION_NODE 85 PROCESSING_INSTRUCTION_NODE = xml.dom.Node.PROCESSING_INSTRUCTION_NODE 86 TEXT_NODE = xml.dom.Node.TEXT_NODE 87 88 def __init__(self, node, ownerDocument=None): 89 self._node = node 90 self.ownerDocument = ownerDocument 91 92 def as_native_node(self): 93 return self._node 94 95 def _nodeType(self): 96 return Node_nodeType(self._node) 97 98 def _childNodes(self): 99 100 # NOTE: Consider a generator instead. 101 102 return [Node(_node, self.ownerDocument) for _node in Node_childNodes(self._node)] 103 104 def _attributes(self): 105 return NamedNodeMap(self) 106 107 def _namespaceURI(self): 108 return Node_namespaceURI(self._node) 109 110 def _nodeValue(self): 111 return Node_nodeValue(self._node) 112 113 def _prefix(self): 114 return Node_prefix(self._node) 115 116 def _nodeName(self): 117 return Node_nodeName(self._node) 118 119 def _tagName(self): 120 return Node_tagName(self._node) 121 122 def _localName(self): 123 return Node_localName(self._node) 124 125 def _parentNode(self): 126 return Node(Node_parentNode(self._node), self.ownerDocument) 127 128 def _previousSibling(self): 129 return Node(Node_previousSibling(self._node), self.ownerDocument) 130 131 def _nextSibling(self): 132 return Node(Node_nextSibling(self._node), self.ownerDocument) 133 134 def hasAttributeNS(self, ns, localName): 135 return Node_hasAttributeNS(self._node, ns, localName) 136 137 def hasAttribute(self, name): 138 return Node_hasAttribute(self._node, name) 139 140 def getAttributeNS(self, ns, localName): 141 return Node_getAttributeNS(self._node, ns, localName) 142 143 def getAttribute(self, name): 144 return Node_getAttribute(self._node, name) 145 146 def getAttributeNodeNS(self, ns, localName): 147 return Node(Node_getAttributeNodeNS(self._node, ns, localName), self.ownerDocument) 148 149 def getAttributeNode(self, localName): 150 return Node(Node_getAttributeNode(self._node, localName), self.ownerDocument) 151 152 def setAttributeNS(self, ns, name, value): 153 Node_setAttributeNS(self._node, ns, name, value) 154 155 def setAttribute(self, name, value): 156 Node_setAttribute(self._node, name, value) 157 158 def setAttributeNodeNS(self, ns, name, node): 159 Node_setAttributeNodeNS(self._node, ns, name, node) 160 161 def setAttributeNode(self, name, node): 162 Node_setAttributeNode(self._node, name, node) 163 164 def createElementNS(self, ns, name): 165 return Node(Node_createElementNS(self._node, ns, name), self.ownerDocument) 166 167 def createElement(self, name): 168 return Node(Node_createElement(self._node, name), self.ownerDocument) 169 170 def createAttributeNS(self, ns, name): 171 # Returns a special temporary node. 172 return Node_createAttributeNS(self._node, ns, name) 173 174 def createAttribute(self, name): 175 # Returns a special temporary node. 176 return Node_createAttribute(self._node, name) 177 178 def createTextNode(self, value): 179 return Node(Node_createTextNode(self._node, value), self.ownerDocument) 180 181 def createComment(self, value): 182 return Node(Node_createComment(self._node, value), self.ownerDocument) 183 184 def importNode(self, node, deep): 185 if hasattr(node, "as_native_node"): 186 return Node(Node_importNode(self._node, node.as_native_node(), deep), self.ownerDocument) 187 else: 188 return Node(Node_importNode_DOM(self._node, node, deep), self.ownerDocument) 189 190 def insertBefore(self, tmp, oldNode): 191 if hasattr(tmp, "as_native_node"): 192 return Node(Node_insertBefore(self._node, tmp.as_native_node(), oldNode.as_native_node()), self.ownerDocument) 193 else: 194 return Node(Node_insertBefore(self._node, tmp, oldNode.as_native_node()), self.ownerDocument) 195 196 def replaceChild(self, tmp, oldNode): 197 if hasattr(tmp, "as_native_node"): 198 return Node(Node_replaceChild(self._node, tmp.as_native_node(), oldNode.as_native_node()), self.ownerDocument) 199 else: 200 return Node(Node_replaceChild(self._node, tmp, oldNode.as_native_node()), self.ownerDocument) 201 202 def appendChild(self, tmp): 203 if hasattr(tmp, "as_native_node"): 204 return Node(Node_appendChild(self._node, tmp.as_native_node()), self.ownerDocument) 205 else: 206 return Node(Node_appendChild(self._node, tmp), self.ownerDocument) 207 208 def removeChild(self, tmp): 209 if hasattr(tmp, "as_native_node"): 210 Node_removeChild(self._node, tmp.as_native_node()) 211 else: 212 Node_removeChild(self._node, tmp) 213 214 childNodes = property(_childNodes) 215 value = data = nodeValue = property(_nodeValue) 216 name = nodeName = property(_nodeName) 217 tagName = property(_tagName) 218 namespaceURI = property(_namespaceURI) 219 prefix = property(_prefix) 220 localName = property(_localName) 221 ownerElement = parentNode = property(_parentNode) 222 nodeType = property(_nodeType) 223 attributes = property(_attributes) 224 previousSibling = property(_previousSibling) 225 nextSibling = property(_nextSibling) 226 227 #def isSameNode(self, other): 228 # return self._node.nodePath() == other._node.nodePath() 229 230 #def __eq__(self, other): 231 # return self._node.nodePath() == other._node.nodePath() 232 233 # 4DOM extensions to the usual PyXML API. 234 # NOTE: To be finished. 235 236 def xpath(self, expr, variables=None, namespaces=None): 237 result = Node_xpath(self._node, expr, variables, namespaces) 238 if hasattr(result, "len"): 239 return [Node(_node, self.ownerDocument) for _node in result] 240 else: 241 return result 242 243 # Convenience methods. 244 245 def toString(self, encoding=None): 246 return toString(self, encoding) 247 248 def toStream(self, stream, encoding=None): 249 toStream(self, stream, encoding) 250 251 def toFile(self, f, encoding=None): 252 toFile(self, f, encoding) 253 254 # Document housekeeping mechanisms. 255 256 class Document(Node): 257 258 "A class providing document-level housekeeping." 259 260 def __init__(self, node): 261 self._node = node 262 self.weakref_ownerDocument = weakref.ref(self) 263 264 def _ownerDocument(self): 265 return self.weakref_ownerDocument() 266 267 def __del__(self): 268 print "Freeing document", self._node 269 libxml2mod.xmlFreeDoc(self._node) 270 271 ownerDocument = property(_ownerDocument) 272 273 # Utility functions. 274 275 def createDocumentType(localName, publicId, systemId): 276 return None 277 278 def createDocument(namespaceURI, localName, doctype): 279 return Document(Node_createDocument(namespaceURI, localName, doctype)) 280 281 def parse(stream_or_string): 282 if hasattr(stream_or_string, "read"): 283 stream = stream_or_string 284 return parseString(stream.read()) 285 else: 286 return parseFile(stream_or_string) 287 288 def parseFile(s): 289 return Document(Node_parseFile(s)) 290 291 def parseString(s): 292 return Document(Node_parseString(s)) 293 294 def parseURI(uri): 295 return Document(Node_parseURI(uri)) 296 297 def toString(node, encoding=None): 298 return Node_toString(node.as_native_node(), encoding) 299 300 def toStream(node, stream, encoding=None): 301 Node_toStream(node.as_native_node(), stream, encoding) 302 303 def toFile(node, f, encoding=None): 304 Node_toFile(node.as_native_node(), f, encoding) 305 306 def adoptNodes(nodes): 307 if len(nodes) == 0: 308 return [] 309 doc = Document(libxml2mod.doc(nodes[0])) 310 results = [] 311 for node in nodes: 312 results.append(Node(node, doc)) 313 return results 314 315 # vim: tabstop=4 expandtab shiftwidth=4