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