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