1 #!/usr/bin/env python 2 3 """ 4 DOM wrapper around libxml2, specifically the libxml2mod Python extension module. 5 6 Copyright (C) 2003, 2004, 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 """ 22 23 __version__ = "0.4.3" 24 25 from libxml2dom.macrolib import * 26 from libxml2dom.macrolib import \ 27 createDocument as Node_createDocument, \ 28 parseString as Node_parseString, parseURI as Node_parseURI, \ 29 parseFile as Node_parseFile, \ 30 toString as Node_toString, toStream as Node_toStream, \ 31 toFile as Node_toFile 32 import urllib # for parseURI in HTML mode 33 import xml.dom # for getElementById 34 35 class Implementation(object): 36 37 "Contains an abstraction over the DOM implementation." 38 39 def createDocumentType(self, localName, publicId, systemId): 40 return DocumentType(localName, publicId, systemId) 41 42 def createDocument(self, namespaceURI, localName, doctype): 43 return Document(Node_createDocument(namespaceURI, localName, doctype), self) 44 45 # Wrapping of documents. 46 47 def adoptDocument(self, node): 48 return Document(node, self) 49 50 # Factory functions. 51 52 def get_node(self, _node, context_node): 53 if Node_nodeType(_node) == context_node.DOCUMENT_NODE: 54 return context_node.ownerDocument 55 elif Node_nodeType(_node) == context_node.ATTRIBUTE_NODE: 56 return Attribute(_node, self, context_node.ownerDocument, context_node) 57 else: 58 return Node(_node, self, context_node.ownerDocument) 59 60 def get_node_or_none(self, _node, context_node): 61 if _node is None: 62 return None 63 else: 64 return self.get_node(_node, context_node) 65 66 # Attribute and node list wrappers. 67 68 class NamedNodeMap(object): 69 70 """ 71 A wrapper around Node objects providing DOM and dictionary convenience 72 methods. 73 """ 74 75 def __init__(self, node, impl): 76 self.node = node 77 self.impl = impl 78 79 def getNamedItem(self, name): 80 return self.node.getAttributeNode(name) 81 82 def getNamedItemNS(self, ns, localName): 83 return self.node.getAttributeNodeNS(ns, localName) 84 85 def setNamedItem(self, node): 86 try: 87 old = self.getNamedItem(node.nodeName) 88 except KeyError: 89 old = None 90 self.node.setAttributeNode(node) 91 return old 92 93 def setNamedItemNS(self, node): 94 try: 95 old = self.getNamedItemNS(node.namespaceURI, node.localName) 96 except KeyError: 97 old = None 98 self.node.setAttributeNodeNS(node) 99 return old 100 101 def removeNamedItem(self, name): 102 try: 103 old = self.getNamedItem(name) 104 except KeyError: 105 old = None 106 self.node.removeAttribute(name) 107 return old 108 109 def removeNamedItemNS(self, ns, localName): 110 try: 111 old = self.getNamedItemNS(ns, localName) 112 except KeyError: 113 old = None 114 self.node.removeAttributeNS(ns, localName) 115 return old 116 117 # Iterator emulation. 118 119 def __iter__(self): 120 return NamedNodeMapIterator(self) 121 122 # Dictionary emulation methods. 123 124 def __getitem__(self, name): 125 return self.getNamedItem(name) 126 127 def __setitem__(self, name, node): 128 if name == node.nodeName: 129 self.setNamedItem(node) 130 else: 131 raise KeyError, name 132 133 def __delitem__(self, name): 134 # NOTE: To be implemented. 135 pass 136 137 def values(self): 138 return [Attribute(_node, self.impl, self.node.ownerDocument) for _node in Node_attributes(self.node.as_native_node()).values()] 139 140 def keys(self): 141 return [(attr.namespaceURI, attr.localName) for attr in self.values()] 142 143 def items(self): 144 return [((attr.namespaceURI, attr.localName), attr) for attr in self.values()] 145 146 def __repr__(self): 147 return str(self) 148 149 def __str__(self): 150 return "{%s}" % ",\n".join(["%s : %s" % (repr(key), repr(value)) for key, value in self.items()]) 151 152 def _length(self): 153 return len(self.values()) 154 155 length = property(_length) 156 157 class NamedNodeMapIterator(object): 158 159 "An iterator over a NamedNodeMap." 160 161 def __init__(self, nodemap): 162 self.nodemap = nodemap 163 self.items = self.nodemap.items() 164 165 def next(self): 166 if self.items: 167 current = self.items[0][1] 168 self.items = self.items[1:] 169 return current 170 else: 171 raise StopIteration 172 173 class NodeList(list): 174 175 "A wrapper around node lists." 176 177 def item(self, index): 178 return self[index] 179 180 def _length(self): 181 return len(self) 182 183 length = property(_length) 184 185 # Node classes. 186 187 class Node(object): 188 189 """ 190 A DOM-style wrapper around libxml2mod objects. 191 """ 192 193 ATTRIBUTE_NODE = xml.dom.Node.ATTRIBUTE_NODE 194 COMMENT_NODE = xml.dom.Node.COMMENT_NODE 195 DOCUMENT_NODE = xml.dom.Node.DOCUMENT_NODE 196 DOCUMENT_TYPE_NODE = xml.dom.Node.DOCUMENT_TYPE_NODE 197 ELEMENT_NODE = xml.dom.Node.ELEMENT_NODE 198 ENTITY_NODE = xml.dom.Node.ENTITY_NODE 199 ENTITY_REFERENCE_NODE = xml.dom.Node.ENTITY_REFERENCE_NODE 200 NOTATION_NODE = xml.dom.Node.NOTATION_NODE 201 PROCESSING_INSTRUCTION_NODE = xml.dom.Node.PROCESSING_INSTRUCTION_NODE 202 TEXT_NODE = xml.dom.Node.TEXT_NODE 203 204 def __init__(self, node, impl=None, ownerDocument=None): 205 self._node = node 206 self.impl = impl or default_impl 207 self.ownerDocument = ownerDocument 208 209 def as_native_node(self): 210 return self._node 211 212 def _nodeType(self): 213 return Node_nodeType(self._node) 214 215 def _childNodes(self): 216 217 # NOTE: Consider a generator instead. 218 219 return NodeList([self.impl.get_node(_node, self) for _node in Node_childNodes(self._node)]) 220 221 def _firstChild(self): 222 return (self.childNodes or [None])[0] 223 224 def _lastChild(self): 225 return (self.childNodes or [None])[-1] 226 227 def _attributes(self): 228 return NamedNodeMap(self, self.impl) 229 230 def _namespaceURI(self): 231 return Node_namespaceURI(self._node) 232 233 def _textContent(self): 234 return Node_textContent(self._node) 235 236 def _nodeValue(self): 237 if self.nodeType in null_value_node_types: 238 return None 239 return Node_nodeValue(self._node) 240 241 def _setNodeValue(self, value): 242 Node_setNodeValue(self._node, value) 243 244 def _prefix(self): 245 return Node_prefix(self._node) 246 247 def _nodeName(self): 248 return Node_nodeName(self._node) 249 250 def _tagName(self): 251 return Node_tagName(self._node) 252 253 def _localName(self): 254 return Node_localName(self._node) 255 256 def _parentNode(self): 257 return self.impl.get_node_or_none(Node_parentNode(self._node), self) 258 259 def _previousSibling(self): 260 return self.impl.get_node_or_none(Node_previousSibling(self._node), self) 261 262 def _nextSibling(self): 263 return self.impl.get_node_or_none(Node_nextSibling(self._node), self) 264 265 def _doctype(self): 266 _doctype = Node_doctype(self._node) 267 if _doctype is not None: 268 return self.impl.get_node(_doctype, self) 269 else: 270 return None 271 272 def _publicId(self): 273 # NOTE: To be fixed when the libxml2mod API has been figured out. 274 if self.nodeType != self.DOCUMENT_TYPE_NODE: 275 return None 276 declaration = self.toString() 277 return self._findId(declaration, "PUBLIC") 278 279 def _systemId(self): 280 # NOTE: To be fixed when the libxml2mod API has been figured out. 281 if self.nodeType != self.DOCUMENT_TYPE_NODE: 282 return None 283 declaration = self.toString() 284 if self._findId(declaration, "PUBLIC"): 285 return self._findIdValue(declaration, 0) 286 return self._findId(declaration, "SYSTEM") 287 288 # NOTE: To be removed when the libxml2mod API has been figured out. 289 290 def _findId(self, declaration, identifier): 291 i = declaration.find(identifier) 292 if i == -1: 293 return None 294 return self._findIdValue(declaration, i) 295 296 def _findIdValue(self, declaration, i): 297 q = declaration.find('"', i) 298 if q == -1: 299 return None 300 q2 = declaration.find('"', q + 1) 301 if q2 == -1: 302 return None 303 return declaration[q+1:q2] 304 305 def hasAttributeNS(self, ns, localName): 306 return Node_hasAttributeNS(self._node, ns, localName) 307 308 def hasAttribute(self, name): 309 return Node_hasAttribute(self._node, name) 310 311 def getAttributeNS(self, ns, localName): 312 return Node_getAttributeNS(self._node, ns, localName) 313 314 def getAttribute(self, name): 315 return Node_getAttribute(self._node, name) 316 317 def getAttributeNodeNS(self, ns, localName): 318 return Attribute(Node_getAttributeNodeNS(self._node, ns, localName), self.impl, self.ownerDocument, self) 319 320 def getAttributeNode(self, localName): 321 return Attribute(Node_getAttributeNode(self._node, localName), self.impl, self.ownerDocument, self) 322 323 def setAttributeNS(self, ns, name, value): 324 Node_setAttributeNS(self._node, ns, name, value) 325 326 def setAttribute(self, name, value): 327 Node_setAttribute(self._node, name, value) 328 329 def setAttributeNodeNS(self, node): 330 Node_setAttributeNodeNS(self._node, node._node) 331 332 def setAttributeNode(self, node): 333 Node_setAttributeNode(self._node, node._node) 334 335 def removeAttributeNS(self, ns, localName): 336 Node_removeAttributeNS(self._node, ns, localName) 337 338 def removeAttribute(self, name): 339 Node_removeAttribute(self._node, name) 340 341 def createElementNS(self, ns, name): 342 return self.impl.get_node(Node_createElementNS(self._node, ns, name), self) 343 344 def createElement(self, name): 345 return self.impl.get_node(Node_createElement(self._node, name), self) 346 347 def createAttributeNS(self, ns, name): 348 tmp = self.createElement("tmp") 349 return Attribute(Node_createAttributeNS(tmp._node, self.impl, ns, name)) 350 351 def createAttribute(self, name): 352 tmp = self.createElement("tmp") 353 return Attribute(Node_createAttribute(tmp._node, name), self.impl) 354 355 def createTextNode(self, value): 356 return self.impl.get_node(Node_createTextNode(self._node, value), self) 357 358 def createComment(self, value): 359 return self.impl.get_node(Node_createComment(self._node, value), self) 360 361 def createCDATASection(self, value): 362 return self.impl.get_node(Node_createCDATASection(self._node, value), self) 363 364 def importNode(self, node, deep): 365 if hasattr(node, "as_native_node"): 366 return self.impl.get_node(Node_importNode(self._node, node.as_native_node(), deep), self) 367 else: 368 return self.impl.get_node(Node_importNode_DOM(self._node, node, deep), self) 369 370 def cloneNode(self, deep): 371 # This takes advantage of the ubiquity of importNode (in spite of the DOM specification). 372 return self.importNode(self, deep) 373 374 def insertBefore(self, tmp, oldNode): 375 if tmp.ownerDocument != self.ownerDocument: 376 raise xml.dom.DOMException(xml.dom.WRONG_DOCUMENT_ERR) 377 if oldNode.parentNode != self: 378 raise xml.dom.DOMException(xml.dom.NOT_FOUND_ERR) 379 if hasattr(tmp, "as_native_node"): 380 return self.impl.get_node(Node_insertBefore(self._node, tmp.as_native_node(), oldNode.as_native_node()), self) 381 else: 382 return self.impl.get_node(Node_insertBefore(self._node, tmp, oldNode.as_native_node()), self) 383 384 def replaceChild(self, tmp, oldNode): 385 if tmp.ownerDocument != self.ownerDocument: 386 raise xml.dom.DOMException(xml.dom.WRONG_DOCUMENT_ERR) 387 if oldNode.parentNode != self: 388 raise xml.dom.DOMException(xml.dom.NOT_FOUND_ERR) 389 if hasattr(tmp, "as_native_node"): 390 return self.impl.get_node(Node_replaceChild(self._node, tmp.as_native_node(), oldNode.as_native_node()), self) 391 else: 392 return self.impl.get_node(Node_replaceChild(self._node, tmp, oldNode.as_native_node()), self) 393 394 def appendChild(self, tmp): 395 if tmp.ownerDocument != self.ownerDocument: 396 raise xml.dom.DOMException(xml.dom.WRONG_DOCUMENT_ERR) 397 if hasattr(tmp, "as_native_node"): 398 return self.impl.get_node(Node_appendChild(self._node, tmp.as_native_node()), self) 399 else: 400 return self.impl.get_node(Node_appendChild(self._node, tmp), self) 401 402 def removeChild(self, tmp): 403 if hasattr(tmp, "as_native_node"): 404 Node_removeChild(self._node, tmp.as_native_node()) 405 else: 406 Node_removeChild(self._node, tmp) 407 return tmp 408 409 def getElementById(self, identifier): 410 nodes = self.xpath(".//*[@xml:id='" + identifier.replace("'", "'") + "']", 411 namespaces={"xml" : xml.dom.XML_NAMESPACE}) 412 if nodes: 413 return nodes[0] 414 else: 415 return None 416 417 def getElementsByTagName(self, tagName): 418 return self.xpath(".//" + tagName) 419 420 def getElementsByTagNameNS(self, namespaceURI, localName): 421 return self.xpath(".//ns:" + localName, namespaces={"ns" : namespaceURI}) 422 423 def normalize(self): 424 text_nodes = [] 425 for node in self.childNodes: 426 if node.nodeType == node.TEXT_NODE: 427 text_nodes.append(node) 428 elif len(text_nodes) != 0: 429 self._normalize(text_nodes) 430 text_nodes = [] 431 if len(text_nodes) != 0: 432 self._normalize(text_nodes) 433 434 def _normalize(self, text_nodes): 435 texts = [] 436 for text_node in text_nodes[:-1]: 437 texts.append(text_node.nodeValue) 438 self.removeChild(text_node) 439 texts.append(text_nodes[-1].nodeValue) 440 self.replaceChild(self.ownerDocument.createTextNode("".join(texts)), text_nodes[-1]) 441 442 childNodes = property(_childNodes) 443 firstChild = property(_firstChild) 444 lastChild = property(_lastChild) 445 value = data = nodeValue = property(_nodeValue, _setNodeValue) 446 textContent = property(_textContent) 447 name = nodeName = property(_nodeName) 448 tagName = property(_tagName) 449 namespaceURI = property(_namespaceURI) 450 prefix = property(_prefix) 451 localName = property(_localName) 452 parentNode = property(_parentNode) 453 nodeType = property(_nodeType) 454 attributes = property(_attributes) 455 previousSibling = property(_previousSibling) 456 nextSibling = property(_nextSibling) 457 doctype = property(_doctype) 458 publicId = property(_publicId) 459 systemId = property(_systemId) 460 461 # NOTE: To be fixed - these being doctype-specific values. 462 463 entities = {} 464 notations = {} 465 466 def isSameNode(self, other): 467 return self == other 468 469 def __hash__(self): 470 return hash(self.localName) 471 472 def __eq__(self, other): 473 return isinstance(other, Node) and Node_equals(self._node, other._node) 474 475 def __ne__(self, other): 476 return not (self == other) 477 478 # 4DOM extensions to the usual PyXML API. 479 # NOTE: To be finished. 480 481 def xpath(self, expr, variables=None, namespaces=None): 482 result = Node_xpath(self._node, expr, variables, namespaces) 483 if isinstance(result, str): 484 return to_unicode(result) 485 elif hasattr(result, "__len__"): 486 return NodeList([self.impl.get_node(_node, self) for _node in result]) 487 else: 488 return result 489 490 # Convenience methods. 491 492 def toString(self, encoding=None, prettyprint=0): 493 return toString(self, encoding, prettyprint) 494 495 def toStream(self, stream, encoding=None, prettyprint=0): 496 toStream(self, stream, encoding, prettyprint) 497 498 def toFile(self, f, encoding=None, prettyprint=0): 499 toFile(self, f, encoding, prettyprint) 500 501 # Attribute nodes. 502 503 class Attribute(Node): 504 505 "A class providing attribute access." 506 507 def __init__(self, node, impl, ownerDocument=None, ownerElement=None): 508 Node.__init__(self, node, impl, ownerDocument) 509 self.ownerElement = ownerElement 510 511 def _parentNode(self): 512 return self.ownerElement 513 514 parentNode = property(_parentNode) 515 516 # Document housekeeping mechanisms. 517 518 class _Document: 519 520 """ 521 An abstract class providing document-level housekeeping and distinct 522 functionality. 523 """ 524 525 def __init__(self, node, impl): 526 self._node = node 527 self.implementation = self.impl = impl 528 529 def _documentElement(self): 530 return self.xpath("*")[0] 531 532 def _ownerDocument(self): 533 return self 534 535 def __del__(self): 536 #print "Freeing document", self._node 537 libxml2mod.xmlFreeDoc(self._node) 538 539 documentElement = property(_documentElement) 540 ownerDocument = property(_ownerDocument) 541 542 class Document(_Document, Node): 543 544 """ 545 A generic document class. Specialised document classes should inherit from 546 the _Document class and their own variation of Node. 547 """ 548 549 pass 550 551 class DocumentType(object): 552 553 "A class providing a container for document type information." 554 555 def __init__(self, localName, publicId, systemId): 556 self.name = self.localName = localName 557 self.publicId = publicId 558 self.systemId = systemId 559 560 # NOTE: Nothing is currently provided to support the following 561 # NOTE: attributes. 562 563 self.entities = {} 564 self.notations = {} 565 566 # Constants. 567 568 null_value_node_types = [ 569 Node.DOCUMENT_NODE, Node.DOCUMENT_TYPE_NODE, Node.ELEMENT_NODE, 570 Node.ENTITY_NODE, Node.ENTITY_REFERENCE_NODE, Node.NOTATION_NODE 571 ] 572 573 # Utility functions. 574 575 def createDocumentType(localName, publicId, systemId): 576 return default_impl.createDocumentType(localName, publicId, systemId) 577 578 def createDocument(namespaceURI, localName, doctype): 579 return default_impl.createDocument(namespaceURI, localName, doctype) 580 581 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None): 582 583 """ 584 Parse the given 'stream_or_string', where the supplied object can either be 585 a stream (such as a file or stream object), or a string (containing the 586 filename of a document). The optional parameters described below should be 587 provided as keyword arguments. 588 589 If the optional 'html' parameter is set to a true value, the content to be 590 parsed will be treated as being HTML rather than XML. If the optional 591 'htmlencoding' is specified, HTML parsing will be performed with the 592 document encoding assumed to that specified. 593 594 If the optional 'unfinished' parameter is set to a true value, unfinished 595 documents will be parsed, even though such documents may be missing content 596 such as closing tags. 597 598 A document object is returned by this function. 599 """ 600 601 impl = impl or default_impl 602 603 if hasattr(stream_or_string, "read"): 604 stream = stream_or_string 605 return parseString(stream.read(), html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=impl) 606 else: 607 return parseFile(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=impl) 608 609 def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None): 610 611 """ 612 Parse the file having the given 'filename'. The optional parameters 613 described below should be provided as keyword arguments. 614 615 If the optional 'html' parameter is set to a true value, the content to be 616 parsed will be treated as being HTML rather than XML. If the optional 617 'htmlencoding' is specified, HTML parsing will be performed with the 618 document encoding assumed to that specified. 619 620 If the optional 'unfinished' parameter is set to a true value, unfinished 621 documents will be parsed, even though such documents may be missing content 622 such as closing tags. 623 624 A document object is returned by this function. 625 """ 626 627 impl = impl or default_impl 628 return impl.adoptDocument(Node_parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished)) 629 630 def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None): 631 632 """ 633 Parse the content of the given string 's'. The optional parameters described 634 below should be provided as keyword arguments. 635 636 If the optional 'html' parameter is set to a true value, the content to be 637 parsed will be treated as being HTML rather than XML. If the optional 638 'htmlencoding' is specified, HTML parsing will be performed with the 639 document encoding assumed to that specified. 640 641 If the optional 'unfinished' parameter is set to a true value, unfinished 642 documents will be parsed, even though such documents may be missing content 643 such as closing tags. 644 645 A document object is returned by this function. 646 """ 647 648 impl = impl or default_impl 649 return impl.adoptDocument(Node_parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished)) 650 651 def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None): 652 653 """ 654 Parse the content found at the given 'uri'. The optional parameters 655 described below should be provided as keyword arguments. 656 657 If the optional 'html' parameter is set to a true value, the content to be 658 parsed will be treated as being HTML rather than XML. If the optional 659 'htmlencoding' is specified, HTML parsing will be performed with the 660 document encoding assumed to that specified. 661 662 If the optional 'unfinished' parameter is set to a true value, unfinished 663 documents will be parsed, even though such documents may be missing content 664 such as closing tags. 665 666 XML documents are retrieved using libxml2's own network capabilities; HTML 667 documents are retrieved using the urllib module provided by Python. To 668 retrieve either kind of document using Python's own modules for this purpose 669 (such as urllib), open a stream and pass it to the parse function: 670 671 f = urllib.urlopen(uri) 672 try: 673 doc = libxml2dom.parse(f, html) 674 finally: 675 f.close() 676 677 A document object is returned by this function. 678 """ 679 680 if html: 681 f = urllib.urlopen(uri) 682 try: 683 return parse(f, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=impl) 684 finally: 685 f.close() 686 else: 687 impl = impl or default_impl 688 return impl.adoptDocument(Node_parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished)) 689 690 def toString(node, encoding=None, prettyprint=0): 691 692 """ 693 Return a string containing the serialised form of the given 'node' and its 694 children. The optional 'encoding' can be used to override the default 695 character encoding used in the serialisation. The optional 'prettyprint' 696 indicates whether the serialised form is prettyprinted or not (the default 697 setting). 698 """ 699 700 return Node_toString(node.as_native_node(), encoding, prettyprint) 701 702 def toStream(node, stream, encoding=None, prettyprint=0): 703 704 """ 705 Write the serialised form of the given 'node' and its children to the given 706 'stream'. The optional 'encoding' can be used to override the default 707 character encoding used in the serialisation. The optional 'prettyprint' 708 indicates whether the serialised form is prettyprinted or not (the default 709 setting). 710 """ 711 712 Node_toStream(node.as_native_node(), stream, encoding, prettyprint) 713 714 def toFile(node, filename, encoding=None, prettyprint=0): 715 716 """ 717 Write the serialised form of the given 'node' and its children to a file 718 having the given 'filename'. The optional 'encoding' can be used to override 719 the default character encoding used in the serialisation. The optional 720 'prettyprint' indicates whether the serialised form is prettyprinted or not 721 (the default setting). 722 """ 723 724 Node_toFile(node.as_native_node(), filename, encoding, prettyprint) 725 726 def adoptNodes(nodes, impl=None): 727 728 """ 729 A special utility method which adopts the given low-level 'nodes' and which 730 returns a list of high-level equivalents. This is currently experimental and 731 should not be casually used. 732 """ 733 734 impl = impl or default_impl 735 736 if len(nodes) == 0: 737 return [] 738 doc = impl.adoptDocument(libxml2mod.doc(nodes[0])) 739 results = [] 740 for node in nodes: 741 results.append(Node(node, impl, doc)) 742 return results 743 744 def getDOMImplementation(): 745 746 "Return the default DOM implementation." 747 748 return default_impl 749 750 # Single instance of the implementation. 751 752 default_impl = Implementation() 753 754 # vim: tabstop=4 expandtab shiftwidth=4