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