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 # Attribute and node list wrappers. 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) 39 # NOTE: Non-standard return of attribute node, since the actual node is 40 # NOTE: not set on the target node. 41 return self.getNamedItem(node.name) 42 43 def setNamedItemNS(self, node): 44 self.node.setAttributeNodeNS(node) 45 # NOTE: Non-standard return of attribute node, since the actual node is 46 # NOTE: not set on the target node. 47 return self.getNamedItemNS(node.namespaceURI, node.name) 48 49 # Dictionary emulation methods. 50 51 def __getitem__(self, name): 52 return self.getNamedItem(name) 53 54 def __setitem__(self, name, node): 55 if name == node.nodeName: 56 self.setNamedItem(node) 57 else: 58 raise KeyError, name 59 60 def __delitem__(self, name): 61 # NOTE: To be implemented. 62 pass 63 64 def values(self): 65 return [Attribute(_node, self.node.ownerDocument) for _node in Node_attributes(self.node.as_native_node()).values()] 66 67 def keys(self): 68 return [(attr.namespaceURI, attr.localName) for attr in self.values()] 69 70 def items(self): 71 return [((attr.namespaceURI, attr.localName), attr) for attr in self.values()] 72 73 def __repr__(self): 74 return str(self) 75 76 def __str__(self): 77 return "{%s}" % ",\n".join(["%s : %s" % (repr(key), repr(value)) for key, value in self.items()]) 78 79 class NodeList(list): 80 81 "A wrapper around node lists." 82 83 def item(self, index): 84 return self[index] 85 86 def length(self): 87 return len(self) 88 89 # Node classes. 90 91 class Node(object): 92 93 """ 94 A DOM-style wrapper around libxml2mod objects. 95 """ 96 97 ATTRIBUTE_NODE = xml.dom.Node.ATTRIBUTE_NODE 98 COMMENT_NODE = xml.dom.Node.COMMENT_NODE 99 DOCUMENT_NODE = xml.dom.Node.DOCUMENT_NODE 100 DOCUMENT_TYPE_NODE = xml.dom.Node.DOCUMENT_TYPE_NODE 101 ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE 102 ENTITY_NODE = xml.dom.Node.ENTITY_NODE 103 ENTITY_REFERENCE_NODE = xml.dom.Node.ENTITY_REFERENCE_NODE 104 NOTATION_NODE = xml.dom.Node.NOTATION_NODE 105 PROCESSING_INSTRUCTION_NODE = xml.dom.Node.PROCESSING_INSTRUCTION_NODE 106 TEXT_NODE = xml.dom.Node.TEXT_NODE 107 108 def __init__(self, node, ownerDocument=None): 109 self._node = node 110 self.ownerDocument = ownerDocument 111 112 def as_native_node(self): 113 return self._node 114 115 def _nodeType(self): 116 return Node_nodeType(self._node) 117 118 def _childNodes(self): 119 120 # NOTE: Consider a generator instead. 121 122 return NodeList([Node(_node, self.ownerDocument) for _node in Node_childNodes(self._node)]) 123 124 def _attributes(self): 125 return NamedNodeMap(self) 126 127 def _namespaceURI(self): 128 return Node_namespaceURI(self._node) 129 130 def _nodeValue(self): 131 return Node_nodeValue(self._node) 132 133 def _setNodeValue(self, value): 134 Node_setNodeValue(self._node, value) 135 136 def _prefix(self): 137 return Node_prefix(self._node) 138 139 def _nodeName(self): 140 return Node_nodeName(self._node) 141 142 def _tagName(self): 143 return Node_tagName(self._node) 144 145 def _localName(self): 146 return Node_localName(self._node) 147 148 def _parentNode(self): 149 return get_node(Node_parentNode(self._node), self) 150 151 def _previousSibling(self): 152 return Node(Node_previousSibling(self._node), self.ownerDocument) 153 154 def _nextSibling(self): 155 return Node(Node_nextSibling(self._node), self.ownerDocument) 156 157 def hasAttributeNS(self, ns, localName): 158 return Node_hasAttributeNS(self._node, ns, localName) 159 160 def hasAttribute(self, name): 161 return Node_hasAttribute(self._node, name) 162 163 def getAttributeNS(self, ns, localName): 164 return Node_getAttributeNS(self._node, ns, localName) 165 166 def getAttribute(self, name): 167 return Node_getAttribute(self._node, name) 168 169 def getAttributeNodeNS(self, ns, localName): 170 return Attribute(Node_getAttributeNodeNS(self._node, ns, localName), self.ownerDocument) 171 172 def getAttributeNode(self, localName): 173 return Attribute(Node_getAttributeNode(self._node, localName), self.ownerDocument) 174 175 def setAttributeNS(self, ns, name, value): 176 Node_setAttributeNS(self._node, ns, name, value) 177 178 def setAttribute(self, name, value): 179 Node_setAttribute(self._node, name, value) 180 181 def setAttributeNodeNS(self, node): 182 Node_setAttributeNodeNS(self._node, node._node) 183 184 def setAttributeNode(self, node): 185 Node_setAttributeNode(self._node, node._node) 186 187 def createElementNS(self, ns, name): 188 return Node(Node_createElementNS(self._node, ns, name), self.ownerDocument) 189 190 def createElement(self, name): 191 return Node(Node_createElement(self._node, name), self.ownerDocument) 192 193 def createAttributeNS(self, ns, name): 194 tmp = self.createElement("tmp") 195 return Attribute(Node_createAttributeNS(tmp._node, ns, name)) 196 197 def createAttribute(self, name): 198 tmp = self.createElement("tmp") 199 return Attribute(Node_createAttribute(tmp._node, name)) 200 201 def createTextNode(self, value): 202 return Node(Node_createTextNode(self._node, value), self.ownerDocument) 203 204 def createComment(self, value): 205 return Node(Node_createComment(self._node, value), self.ownerDocument) 206 207 def importNode(self, node, deep): 208 if hasattr(node, "as_native_node"): 209 return Node(Node_importNode(self._node, node.as_native_node(), deep), self.ownerDocument) 210 else: 211 return Node(Node_importNode_DOM(self._node, node, deep), self.ownerDocument) 212 213 def insertBefore(self, tmp, oldNode): 214 if hasattr(tmp, "as_native_node"): 215 return Node(Node_insertBefore(self._node, tmp.as_native_node(), oldNode.as_native_node()), self.ownerDocument) 216 else: 217 return Node(Node_insertBefore(self._node, tmp, oldNode.as_native_node()), self.ownerDocument) 218 219 def replaceChild(self, tmp, oldNode): 220 if hasattr(tmp, "as_native_node"): 221 return Node(Node_replaceChild(self._node, tmp.as_native_node(), oldNode.as_native_node()), self.ownerDocument) 222 else: 223 return Node(Node_replaceChild(self._node, tmp, oldNode.as_native_node()), self.ownerDocument) 224 225 def appendChild(self, tmp): 226 if hasattr(tmp, "as_native_node"): 227 return Node(Node_appendChild(self._node, tmp.as_native_node()), self.ownerDocument) 228 else: 229 return Node(Node_appendChild(self._node, tmp), self.ownerDocument) 230 231 def removeChild(self, tmp): 232 if hasattr(tmp, "as_native_node"): 233 Node_removeChild(self._node, tmp.as_native_node()) 234 else: 235 Node_removeChild(self._node, tmp) 236 237 childNodes = property(_childNodes) 238 value = data = nodeValue = property(_nodeValue, _setNodeValue) 239 name = nodeName = property(_nodeName) 240 tagName = property(_tagName) 241 namespaceURI = property(_namespaceURI) 242 prefix = property(_prefix) 243 localName = property(_localName) 244 parentNode = property(_parentNode) 245 nodeType = property(_nodeType) 246 attributes = property(_attributes) 247 previousSibling = property(_previousSibling) 248 nextSibling = property(_nextSibling) 249 250 #def isSameNode(self, other): 251 # return self._node.nodePath() == other._node.nodePath() 252 253 #def __eq__(self, other): 254 # return self._node.nodePath() == other._node.nodePath() 255 256 def getElementsByTagName(self, tagName): 257 return self.xpath("//" + tagName) 258 259 # 4DOM extensions to the usual PyXML API. 260 # NOTE: To be finished. 261 262 def xpath(self, expr, variables=None, namespaces=None): 263 result = Node_xpath(self._node, expr, variables, namespaces) 264 if hasattr(result, "__len__"): 265 return NodeList([get_node(_node, self) for _node in result]) 266 else: 267 return result 268 269 # Convenience methods. 270 271 def toString(self, encoding=None): 272 return toString(self, encoding) 273 274 def toStream(self, stream, encoding=None): 275 toStream(self, stream, encoding) 276 277 def toFile(self, f, encoding=None): 278 toFile(self, f, encoding) 279 280 # Attribute nodes. 281 282 class Attribute(Node): 283 284 "A class providing attribute access." 285 286 def __init__(self, node, ownerDocument=None, ownerElement=None): 287 Node.__init__(self, node, ownerDocument) 288 self.ownerElement = ownerElement 289 290 def _parentNode(self): 291 return self.ownerElement 292 293 parentNode = property(_parentNode) 294 295 # Document housekeeping mechanisms. 296 297 class Document(Node): 298 299 "A class providing document-level housekeeping." 300 301 def __init__(self, node): 302 self._node = node 303 self.weakref_ownerDocument = weakref.ref(self) 304 305 def _ownerDocument(self): 306 return self.weakref_ownerDocument() 307 308 def __del__(self): 309 #print "Freeing document", self._node 310 libxml2mod.xmlFreeDoc(self._node) 311 312 ownerDocument = property(_ownerDocument) 313 314 # Factory functions. 315 316 def get_node(_node, context_node): 317 if Node_nodeType(_node) == context_node.DOCUMENT_NODE: 318 return context_node.ownerDocument 319 elif Node_nodeType(_node) == context_node.ATTRIBUTE_NODE: 320 return Attribute(_node, context_node.ownerDocument, context_node) 321 else: 322 return Node(_node, context_node.ownerDocument) 323 324 # Utility functions. 325 326 def createDocumentType(localName, publicId, systemId): 327 return None 328 329 def createDocument(namespaceURI, localName, doctype): 330 return Document(Node_createDocument(namespaceURI, localName, doctype)) 331 332 def parse(stream_or_string): 333 if hasattr(stream_or_string, "read"): 334 stream = stream_or_string 335 return parseString(stream.read()) 336 else: 337 return parseFile(stream_or_string) 338 339 def parseFile(s): 340 return Document(Node_parseFile(s)) 341 342 def parseString(s): 343 return Document(Node_parseString(s)) 344 345 def parseURI(uri): 346 return Document(Node_parseURI(uri)) 347 348 def toString(node, encoding=None): 349 return Node_toString(node.as_native_node(), encoding) 350 351 def toStream(node, stream, encoding=None): 352 Node_toStream(node.as_native_node(), stream, encoding) 353 354 def toFile(node, f, encoding=None): 355 Node_toFile(node.as_native_node(), f, encoding) 356 357 def adoptNodes(nodes): 358 if len(nodes) == 0: 359 return [] 360 doc = Document(libxml2mod.doc(nodes[0])) 361 results = [] 362 for node in nodes: 363 results.append(Node(node, doc)) 364 return results 365 366 # vim: tabstop=4 expandtab shiftwidth=4