1# test for xml.dom.minidom 2 3import copy 4import pickle 5import io 6from test import support 7import unittest 8 9import pyexpat 10import xml.dom.minidom 11 12from xml.dom.minidom import parse, Attr, Node, Document, parseString 13from xml.dom.minidom import getDOMImplementation 14from xml.parsers.expat import ExpatError 15 16 17tstfile = support.findfile("test.xml", subdir="xmltestdata") 18sample = ("<?xml version='1.0' encoding='us-ascii'?>\n" 19 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'" 20 " 'http://xml.python.org/system' [\n" 21 " <!ELEMENT e EMPTY>\n" 22 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n" 23 "]><doc attr='value'> text\n" 24 "<?pi sample?> <!-- comment --> <e/> </doc>") 25 26# The tests of DocumentType importing use these helpers to construct 27# the documents to work with, since not all DOM builders actually 28# create the DocumentType nodes. 29def create_doc_without_doctype(doctype=None): 30 return getDOMImplementation().createDocument(None, "doc", doctype) 31 32def create_nonempty_doctype(): 33 doctype = getDOMImplementation().createDocumentType("doc", None, None) 34 doctype.entities._seq = [] 35 doctype.notations._seq = [] 36 notation = xml.dom.minidom.Notation("my-notation", None, 37 "http://xml.python.org/notations/my") 38 doctype.notations._seq.append(notation) 39 entity = xml.dom.minidom.Entity("my-entity", None, 40 "http://xml.python.org/entities/my", 41 "my-notation") 42 entity.version = "1.0" 43 entity.encoding = "utf-8" 44 entity.actualEncoding = "us-ascii" 45 doctype.entities._seq.append(entity) 46 return doctype 47 48def create_doc_with_doctype(): 49 doctype = create_nonempty_doctype() 50 doc = create_doc_without_doctype(doctype) 51 doctype.entities.item(0).ownerDocument = doc 52 doctype.notations.item(0).ownerDocument = doc 53 return doc 54 55class MinidomTest(unittest.TestCase): 56 def confirm(self, test, testname = "Test"): 57 self.assertTrue(test, testname) 58 59 def checkWholeText(self, node, s): 60 t = node.wholeText 61 self.confirm(t == s, "looking for %r, found %r" % (s, t)) 62 63 def testDocumentAsyncAttr(self): 64 doc = Document() 65 self.assertFalse(doc.async_) 66 self.assertFalse(Document.async_) 67 68 def testParseFromBinaryFile(self): 69 with open(tstfile, 'rb') as file: 70 dom = parse(file) 71 dom.unlink() 72 self.confirm(isinstance(dom, Document)) 73 74 def testParseFromTextFile(self): 75 with open(tstfile, 'r', encoding='iso-8859-1') as file: 76 dom = parse(file) 77 dom.unlink() 78 self.confirm(isinstance(dom, Document)) 79 80 def testAttrModeSetsParamsAsAttrs(self): 81 attr = Attr("qName", "namespaceURI", "localName", "prefix") 82 self.assertEqual(attr.name, "qName") 83 self.assertEqual(attr.namespaceURI, "namespaceURI") 84 self.assertEqual(attr.prefix, "prefix") 85 self.assertEqual(attr.localName, "localName") 86 87 def testAttrModeSetsNonOptionalAttrs(self): 88 attr = Attr("qName", "namespaceURI", None, "prefix") 89 self.assertEqual(attr.name, "qName") 90 self.assertEqual(attr.namespaceURI, "namespaceURI") 91 self.assertEqual(attr.prefix, "prefix") 92 self.assertEqual(attr.localName, attr.name) 93 94 def testGetElementsByTagName(self): 95 dom = parse(tstfile) 96 self.confirm(dom.getElementsByTagName("LI") == \ 97 dom.documentElement.getElementsByTagName("LI")) 98 dom.unlink() 99 100 def testInsertBefore(self): 101 dom = parseString("<doc><foo/></doc>") 102 root = dom.documentElement 103 elem = root.childNodes[0] 104 nelem = dom.createElement("element") 105 root.insertBefore(nelem, elem) 106 self.confirm(len(root.childNodes) == 2 107 and root.childNodes.length == 2 108 and root.childNodes[0] is nelem 109 and root.childNodes.item(0) is nelem 110 and root.childNodes[1] is elem 111 and root.childNodes.item(1) is elem 112 and root.firstChild is nelem 113 and root.lastChild is elem 114 and root.toxml() == "<doc><element/><foo/></doc>" 115 , "testInsertBefore -- node properly placed in tree") 116 nelem = dom.createElement("element") 117 root.insertBefore(nelem, None) 118 self.confirm(len(root.childNodes) == 3 119 and root.childNodes.length == 3 120 and root.childNodes[1] is elem 121 and root.childNodes.item(1) is elem 122 and root.childNodes[2] is nelem 123 and root.childNodes.item(2) is nelem 124 and root.lastChild is nelem 125 and nelem.previousSibling is elem 126 and root.toxml() == "<doc><element/><foo/><element/></doc>" 127 , "testInsertBefore -- node properly placed in tree") 128 nelem2 = dom.createElement("bar") 129 root.insertBefore(nelem2, nelem) 130 self.confirm(len(root.childNodes) == 4 131 and root.childNodes.length == 4 132 and root.childNodes[2] is nelem2 133 and root.childNodes.item(2) is nelem2 134 and root.childNodes[3] is nelem 135 and root.childNodes.item(3) is nelem 136 and nelem2.nextSibling is nelem 137 and nelem.previousSibling is nelem2 138 and root.toxml() == 139 "<doc><element/><foo/><bar/><element/></doc>" 140 , "testInsertBefore -- node properly placed in tree") 141 dom.unlink() 142 143 def _create_fragment_test_nodes(self): 144 dom = parseString("<doc/>") 145 orig = dom.createTextNode("original") 146 c1 = dom.createTextNode("foo") 147 c2 = dom.createTextNode("bar") 148 c3 = dom.createTextNode("bat") 149 dom.documentElement.appendChild(orig) 150 frag = dom.createDocumentFragment() 151 frag.appendChild(c1) 152 frag.appendChild(c2) 153 frag.appendChild(c3) 154 return dom, orig, c1, c2, c3, frag 155 156 def testInsertBeforeFragment(self): 157 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() 158 dom.documentElement.insertBefore(frag, None) 159 self.confirm(tuple(dom.documentElement.childNodes) == 160 (orig, c1, c2, c3), 161 "insertBefore(<fragment>, None)") 162 frag.unlink() 163 dom.unlink() 164 165 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() 166 dom.documentElement.insertBefore(frag, orig) 167 self.confirm(tuple(dom.documentElement.childNodes) == 168 (c1, c2, c3, orig), 169 "insertBefore(<fragment>, orig)") 170 frag.unlink() 171 dom.unlink() 172 173 def testAppendChild(self): 174 dom = parse(tstfile) 175 dom.documentElement.appendChild(dom.createComment("Hello")) 176 self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment") 177 self.confirm(dom.documentElement.childNodes[-1].data == "Hello") 178 dom.unlink() 179 180 def testAppendChildFragment(self): 181 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() 182 dom.documentElement.appendChild(frag) 183 self.confirm(tuple(dom.documentElement.childNodes) == 184 (orig, c1, c2, c3), 185 "appendChild(<fragment>)") 186 frag.unlink() 187 dom.unlink() 188 189 def testReplaceChildFragment(self): 190 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() 191 dom.documentElement.replaceChild(frag, orig) 192 orig.unlink() 193 self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3), 194 "replaceChild(<fragment>)") 195 frag.unlink() 196 dom.unlink() 197 198 def testLegalChildren(self): 199 dom = Document() 200 elem = dom.createElement('element') 201 text = dom.createTextNode('text') 202 self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text) 203 204 dom.appendChild(elem) 205 self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text, 206 elem) 207 self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text, 208 elem) 209 210 nodemap = elem.attributes 211 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem, 212 text) 213 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS, 214 text) 215 216 elem.appendChild(text) 217 dom.unlink() 218 219 def testNamedNodeMapSetItem(self): 220 dom = Document() 221 elem = dom.createElement('element') 222 attrs = elem.attributes 223 attrs["foo"] = "bar" 224 a = attrs.item(0) 225 self.confirm(a.ownerDocument is dom, 226 "NamedNodeMap.__setitem__() sets ownerDocument") 227 self.confirm(a.ownerElement is elem, 228 "NamedNodeMap.__setitem__() sets ownerElement") 229 self.confirm(a.value == "bar", 230 "NamedNodeMap.__setitem__() sets value") 231 self.confirm(a.nodeValue == "bar", 232 "NamedNodeMap.__setitem__() sets nodeValue") 233 elem.unlink() 234 dom.unlink() 235 236 def testNonZero(self): 237 dom = parse(tstfile) 238 self.confirm(dom)# should not be zero 239 dom.appendChild(dom.createComment("foo")) 240 self.confirm(not dom.childNodes[-1].childNodes) 241 dom.unlink() 242 243 def testUnlink(self): 244 dom = parse(tstfile) 245 self.assertTrue(dom.childNodes) 246 dom.unlink() 247 self.assertFalse(dom.childNodes) 248 249 def testContext(self): 250 with parse(tstfile) as dom: 251 self.assertTrue(dom.childNodes) 252 self.assertFalse(dom.childNodes) 253 254 def testElement(self): 255 dom = Document() 256 dom.appendChild(dom.createElement("abc")) 257 self.confirm(dom.documentElement) 258 dom.unlink() 259 260 def testAAA(self): 261 dom = parseString("<abc/>") 262 el = dom.documentElement 263 el.setAttribute("spam", "jam2") 264 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA") 265 a = el.getAttributeNode("spam") 266 self.confirm(a.ownerDocument is dom, 267 "setAttribute() sets ownerDocument") 268 self.confirm(a.ownerElement is dom.documentElement, 269 "setAttribute() sets ownerElement") 270 dom.unlink() 271 272 def testAAB(self): 273 dom = parseString("<abc/>") 274 el = dom.documentElement 275 el.setAttribute("spam", "jam") 276 el.setAttribute("spam", "jam2") 277 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB") 278 dom.unlink() 279 280 def testAddAttr(self): 281 dom = Document() 282 child = dom.appendChild(dom.createElement("abc")) 283 284 child.setAttribute("def", "ghi") 285 self.confirm(child.getAttribute("def") == "ghi") 286 self.confirm(child.attributes["def"].value == "ghi") 287 288 child.setAttribute("jkl", "mno") 289 self.confirm(child.getAttribute("jkl") == "mno") 290 self.confirm(child.attributes["jkl"].value == "mno") 291 292 self.confirm(len(child.attributes) == 2) 293 294 child.setAttribute("def", "newval") 295 self.confirm(child.getAttribute("def") == "newval") 296 self.confirm(child.attributes["def"].value == "newval") 297 298 self.confirm(len(child.attributes) == 2) 299 dom.unlink() 300 301 def testDeleteAttr(self): 302 dom = Document() 303 child = dom.appendChild(dom.createElement("abc")) 304 305 self.confirm(len(child.attributes) == 0) 306 child.setAttribute("def", "ghi") 307 self.confirm(len(child.attributes) == 1) 308 del child.attributes["def"] 309 self.confirm(len(child.attributes) == 0) 310 dom.unlink() 311 312 def testRemoveAttr(self): 313 dom = Document() 314 child = dom.appendChild(dom.createElement("abc")) 315 316 child.setAttribute("def", "ghi") 317 self.confirm(len(child.attributes) == 1) 318 self.assertRaises(xml.dom.NotFoundErr, child.removeAttribute, "foo") 319 child.removeAttribute("def") 320 self.confirm(len(child.attributes) == 0) 321 dom.unlink() 322 323 def testRemoveAttrNS(self): 324 dom = Document() 325 child = dom.appendChild( 326 dom.createElementNS("http://www.python.org", "python:abc")) 327 child.setAttributeNS("http://www.w3.org", "xmlns:python", 328 "http://www.python.org") 329 child.setAttributeNS("http://www.python.org", "python:abcattr", "foo") 330 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNS, 331 "foo", "http://www.python.org") 332 self.confirm(len(child.attributes) == 2) 333 child.removeAttributeNS("http://www.python.org", "abcattr") 334 self.confirm(len(child.attributes) == 1) 335 dom.unlink() 336 337 def testRemoveAttributeNode(self): 338 dom = Document() 339 child = dom.appendChild(dom.createElement("foo")) 340 child.setAttribute("spam", "jam") 341 self.confirm(len(child.attributes) == 1) 342 node = child.getAttributeNode("spam") 343 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNode, 344 None) 345 self.assertIs(node, child.removeAttributeNode(node)) 346 self.confirm(len(child.attributes) == 0 347 and child.getAttributeNode("spam") is None) 348 dom2 = Document() 349 child2 = dom2.appendChild(dom2.createElement("foo")) 350 node2 = child2.getAttributeNode("spam") 351 self.assertRaises(xml.dom.NotFoundErr, child2.removeAttributeNode, 352 node2) 353 dom.unlink() 354 355 def testHasAttribute(self): 356 dom = Document() 357 child = dom.appendChild(dom.createElement("foo")) 358 child.setAttribute("spam", "jam") 359 self.confirm(child.hasAttribute("spam")) 360 361 def testChangeAttr(self): 362 dom = parseString("<abc/>") 363 el = dom.documentElement 364 el.setAttribute("spam", "jam") 365 self.confirm(len(el.attributes) == 1) 366 el.setAttribute("spam", "bam") 367 # Set this attribute to be an ID and make sure that doesn't change 368 # when changing the value: 369 el.setIdAttribute("spam") 370 self.confirm(len(el.attributes) == 1 371 and el.attributes["spam"].value == "bam" 372 and el.attributes["spam"].nodeValue == "bam" 373 and el.getAttribute("spam") == "bam" 374 and el.getAttributeNode("spam").isId) 375 el.attributes["spam"] = "ham" 376 self.confirm(len(el.attributes) == 1 377 and el.attributes["spam"].value == "ham" 378 and el.attributes["spam"].nodeValue == "ham" 379 and el.getAttribute("spam") == "ham" 380 and el.attributes["spam"].isId) 381 el.setAttribute("spam2", "bam") 382 self.confirm(len(el.attributes) == 2 383 and el.attributes["spam"].value == "ham" 384 and el.attributes["spam"].nodeValue == "ham" 385 and el.getAttribute("spam") == "ham" 386 and el.attributes["spam2"].value == "bam" 387 and el.attributes["spam2"].nodeValue == "bam" 388 and el.getAttribute("spam2") == "bam") 389 el.attributes["spam2"] = "bam2" 390 self.confirm(len(el.attributes) == 2 391 and el.attributes["spam"].value == "ham" 392 and el.attributes["spam"].nodeValue == "ham" 393 and el.getAttribute("spam") == "ham" 394 and el.attributes["spam2"].value == "bam2" 395 and el.attributes["spam2"].nodeValue == "bam2" 396 and el.getAttribute("spam2") == "bam2") 397 dom.unlink() 398 399 def testGetAttrList(self): 400 pass 401 402 def testGetAttrValues(self): 403 pass 404 405 def testGetAttrLength(self): 406 pass 407 408 def testGetAttribute(self): 409 dom = Document() 410 child = dom.appendChild( 411 dom.createElementNS("http://www.python.org", "python:abc")) 412 self.assertEqual(child.getAttribute('missing'), '') 413 414 def testGetAttributeNS(self): 415 dom = Document() 416 child = dom.appendChild( 417 dom.createElementNS("http://www.python.org", "python:abc")) 418 child.setAttributeNS("http://www.w3.org", "xmlns:python", 419 "http://www.python.org") 420 self.assertEqual(child.getAttributeNS("http://www.w3.org", "python"), 421 'http://www.python.org') 422 self.assertEqual(child.getAttributeNS("http://www.w3.org", "other"), 423 '') 424 child2 = child.appendChild(dom.createElement('abc')) 425 self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"), 426 '') 427 428 def testGetAttributeNode(self): pass 429 430 def testGetElementsByTagNameNS(self): 431 d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'> 432 <minidom:myelem/> 433 </foo>""" 434 dom = parseString(d) 435 elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom", 436 "myelem") 437 self.confirm(len(elems) == 1 438 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom" 439 and elems[0].localName == "myelem" 440 and elems[0].prefix == "minidom" 441 and elems[0].tagName == "minidom:myelem" 442 and elems[0].nodeName == "minidom:myelem") 443 dom.unlink() 444 445 def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri, 446 lname): 447 nodelist = doc.getElementsByTagNameNS(nsuri, lname) 448 self.confirm(len(nodelist) == 0) 449 450 def testGetEmptyNodeListFromElementsByTagNameNS(self): 451 doc = parseString('<doc/>') 452 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 453 doc, 'http://xml.python.org/namespaces/a', 'localname') 454 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 455 doc, '*', 'splat') 456 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 457 doc, 'http://xml.python.org/namespaces/a', '*') 458 459 doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>') 460 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 461 doc, "http://xml.python.org/splat", "not-there") 462 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 463 doc, "*", "not-there") 464 self.get_empty_nodelist_from_elements_by_tagName_ns_helper( 465 doc, "http://somewhere.else.net/not-there", "e") 466 467 def testElementReprAndStr(self): 468 dom = Document() 469 el = dom.appendChild(dom.createElement("abc")) 470 string1 = repr(el) 471 string2 = str(el) 472 self.confirm(string1 == string2) 473 dom.unlink() 474 475 def testElementReprAndStrUnicode(self): 476 dom = Document() 477 el = dom.appendChild(dom.createElement("abc")) 478 string1 = repr(el) 479 string2 = str(el) 480 self.confirm(string1 == string2) 481 dom.unlink() 482 483 def testElementReprAndStrUnicodeNS(self): 484 dom = Document() 485 el = dom.appendChild( 486 dom.createElementNS("http://www.slashdot.org", "slash:abc")) 487 string1 = repr(el) 488 string2 = str(el) 489 self.confirm(string1 == string2) 490 self.confirm("slash:abc" in string1) 491 dom.unlink() 492 493 def testAttributeRepr(self): 494 dom = Document() 495 el = dom.appendChild(dom.createElement("abc")) 496 node = el.setAttribute("abc", "def") 497 self.confirm(str(node) == repr(node)) 498 dom.unlink() 499 500 def testTextNodeRepr(self): pass 501 502 def testWriteXML(self): 503 str = '<?xml version="1.0" ?><a b="c"/>' 504 dom = parseString(str) 505 domstr = dom.toxml() 506 dom.unlink() 507 self.confirm(str == domstr) 508 509 def testAltNewline(self): 510 str = '<?xml version="1.0" ?>\n<a b="c"/>\n' 511 dom = parseString(str) 512 domstr = dom.toprettyxml(newl="\r\n") 513 dom.unlink() 514 self.confirm(domstr == str.replace("\n", "\r\n")) 515 516 def test_toprettyxml_with_text_nodes(self): 517 # see issue #4147, text nodes are not indented 518 decl = '<?xml version="1.0" ?>\n' 519 self.assertEqual(parseString('<B>A</B>').toprettyxml(), 520 decl + '<B>A</B>\n') 521 self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(), 522 decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n') 523 self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(), 524 decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n') 525 self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(), 526 decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n') 527 self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(), 528 decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n') 529 530 def test_toprettyxml_with_adjacent_text_nodes(self): 531 # see issue #4147, adjacent text nodes are indented normally 532 dom = Document() 533 elem = dom.createElement('elem') 534 elem.appendChild(dom.createTextNode('TEXT')) 535 elem.appendChild(dom.createTextNode('TEXT')) 536 dom.appendChild(elem) 537 decl = '<?xml version="1.0" ?>\n' 538 self.assertEqual(dom.toprettyxml(), 539 decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n') 540 541 def test_toprettyxml_preserves_content_of_text_node(self): 542 # see issue #4147 543 for str in ('<B>A</B>', '<A><B>C</B></A>'): 544 dom = parseString(str) 545 dom2 = parseString(dom.toprettyxml()) 546 self.assertEqual( 547 dom.getElementsByTagName('B')[0].childNodes[0].toxml(), 548 dom2.getElementsByTagName('B')[0].childNodes[0].toxml()) 549 550 def testProcessingInstruction(self): 551 dom = parseString('<e><?mypi \t\n data \t\n ?></e>') 552 pi = dom.documentElement.firstChild 553 self.confirm(pi.target == "mypi" 554 and pi.data == "data \t\n " 555 and pi.nodeName == "mypi" 556 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE 557 and pi.attributes is None 558 and not pi.hasChildNodes() 559 and len(pi.childNodes) == 0 560 and pi.firstChild is None 561 and pi.lastChild is None 562 and pi.localName is None 563 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE) 564 565 def testProcessingInstructionRepr(self): pass 566 567 def testTextRepr(self): pass 568 569 def testWriteText(self): pass 570 571 def testDocumentElement(self): pass 572 573 def testTooManyDocumentElements(self): 574 doc = parseString("<doc/>") 575 elem = doc.createElement("extra") 576 # Should raise an exception when adding an extra document element. 577 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem) 578 elem.unlink() 579 doc.unlink() 580 581 def testCreateElementNS(self): pass 582 583 def testCreateAttributeNS(self): pass 584 585 def testParse(self): pass 586 587 def testParseString(self): pass 588 589 def testComment(self): pass 590 591 def testAttrListItem(self): pass 592 593 def testAttrListItems(self): pass 594 595 def testAttrListItemNS(self): pass 596 597 def testAttrListKeys(self): pass 598 599 def testAttrListKeysNS(self): pass 600 601 def testRemoveNamedItem(self): 602 doc = parseString("<doc a=''/>") 603 e = doc.documentElement 604 attrs = e.attributes 605 a1 = e.getAttributeNode("a") 606 a2 = attrs.removeNamedItem("a") 607 self.confirm(a1.isSameNode(a2)) 608 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a") 609 610 def testRemoveNamedItemNS(self): 611 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>") 612 e = doc.documentElement 613 attrs = e.attributes 614 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b") 615 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b") 616 self.confirm(a1.isSameNode(a2)) 617 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS, 618 "http://xml.python.org/", "b") 619 620 def testAttrListValues(self): pass 621 622 def testAttrListLength(self): pass 623 624 def testAttrList__getitem__(self): pass 625 626 def testAttrList__setitem__(self): pass 627 628 def testSetAttrValueandNodeValue(self): pass 629 630 def testParseElement(self): pass 631 632 def testParseAttributes(self): pass 633 634 def testParseElementNamespaces(self): pass 635 636 def testParseAttributeNamespaces(self): pass 637 638 def testParseProcessingInstructions(self): pass 639 640 def testChildNodes(self): pass 641 642 def testFirstChild(self): pass 643 644 def testHasChildNodes(self): 645 dom = parseString("<doc><foo/></doc>") 646 doc = dom.documentElement 647 self.assertTrue(doc.hasChildNodes()) 648 dom2 = parseString("<doc/>") 649 doc2 = dom2.documentElement 650 self.assertFalse(doc2.hasChildNodes()) 651 652 def _testCloneElementCopiesAttributes(self, e1, e2, test): 653 attrs1 = e1.attributes 654 attrs2 = e2.attributes 655 keys1 = list(attrs1.keys()) 656 keys2 = list(attrs2.keys()) 657 keys1.sort() 658 keys2.sort() 659 self.confirm(keys1 == keys2, "clone of element has same attribute keys") 660 for i in range(len(keys1)): 661 a1 = attrs1.item(i) 662 a2 = attrs2.item(i) 663 self.confirm(a1 is not a2 664 and a1.value == a2.value 665 and a1.nodeValue == a2.nodeValue 666 and a1.namespaceURI == a2.namespaceURI 667 and a1.localName == a2.localName 668 , "clone of attribute node has proper attribute values") 669 self.confirm(a2.ownerElement is e2, 670 "clone of attribute node correctly owned") 671 672 def _setupCloneElement(self, deep): 673 dom = parseString("<doc attr='value'><foo/></doc>") 674 root = dom.documentElement 675 clone = root.cloneNode(deep) 676 self._testCloneElementCopiesAttributes( 677 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow")) 678 # mutilate the original so shared data is detected 679 root.tagName = root.nodeName = "MODIFIED" 680 root.setAttribute("attr", "NEW VALUE") 681 root.setAttribute("added", "VALUE") 682 return dom, clone 683 684 def testCloneElementShallow(self): 685 dom, clone = self._setupCloneElement(0) 686 self.confirm(len(clone.childNodes) == 0 687 and clone.childNodes.length == 0 688 and clone.parentNode is None 689 and clone.toxml() == '<doc attr="value"/>' 690 , "testCloneElementShallow") 691 dom.unlink() 692 693 def testCloneElementDeep(self): 694 dom, clone = self._setupCloneElement(1) 695 self.confirm(len(clone.childNodes) == 1 696 and clone.childNodes.length == 1 697 and clone.parentNode is None 698 and clone.toxml() == '<doc attr="value"><foo/></doc>' 699 , "testCloneElementDeep") 700 dom.unlink() 701 702 def testCloneDocumentShallow(self): 703 doc = parseString("<?xml version='1.0'?>\n" 704 "<!-- comment -->" 705 "<!DOCTYPE doc [\n" 706 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" 707 "]>\n" 708 "<doc attr='value'/>") 709 doc2 = doc.cloneNode(0) 710 self.confirm(doc2 is None, 711 "testCloneDocumentShallow:" 712 " shallow cloning of documents makes no sense!") 713 714 def testCloneDocumentDeep(self): 715 doc = parseString("<?xml version='1.0'?>\n" 716 "<!-- comment -->" 717 "<!DOCTYPE doc [\n" 718 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" 719 "]>\n" 720 "<doc attr='value'/>") 721 doc2 = doc.cloneNode(1) 722 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)), 723 "testCloneDocumentDeep: document objects not distinct") 724 self.confirm(len(doc.childNodes) == len(doc2.childNodes), 725 "testCloneDocumentDeep: wrong number of Document children") 726 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE, 727 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE") 728 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2), 729 "testCloneDocumentDeep: documentElement owner is not new document") 730 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement), 731 "testCloneDocumentDeep: documentElement should not be shared") 732 if doc.doctype is not None: 733 # check the doctype iff the original DOM maintained it 734 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE, 735 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE") 736 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2)) 737 self.confirm(not doc.doctype.isSameNode(doc2.doctype)) 738 739 def testCloneDocumentTypeDeepOk(self): 740 doctype = create_nonempty_doctype() 741 clone = doctype.cloneNode(1) 742 self.confirm(clone is not None 743 and clone.nodeName == doctype.nodeName 744 and clone.name == doctype.name 745 and clone.publicId == doctype.publicId 746 and clone.systemId == doctype.systemId 747 and len(clone.entities) == len(doctype.entities) 748 and clone.entities.item(len(clone.entities)) is None 749 and len(clone.notations) == len(doctype.notations) 750 and clone.notations.item(len(clone.notations)) is None 751 and len(clone.childNodes) == 0) 752 for i in range(len(doctype.entities)): 753 se = doctype.entities.item(i) 754 ce = clone.entities.item(i) 755 self.confirm((not se.isSameNode(ce)) 756 and (not ce.isSameNode(se)) 757 and ce.nodeName == se.nodeName 758 and ce.notationName == se.notationName 759 and ce.publicId == se.publicId 760 and ce.systemId == se.systemId 761 and ce.encoding == se.encoding 762 and ce.actualEncoding == se.actualEncoding 763 and ce.version == se.version) 764 for i in range(len(doctype.notations)): 765 sn = doctype.notations.item(i) 766 cn = clone.notations.item(i) 767 self.confirm((not sn.isSameNode(cn)) 768 and (not cn.isSameNode(sn)) 769 and cn.nodeName == sn.nodeName 770 and cn.publicId == sn.publicId 771 and cn.systemId == sn.systemId) 772 773 def testCloneDocumentTypeDeepNotOk(self): 774 doc = create_doc_with_doctype() 775 clone = doc.doctype.cloneNode(1) 776 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk") 777 778 def testCloneDocumentTypeShallowOk(self): 779 doctype = create_nonempty_doctype() 780 clone = doctype.cloneNode(0) 781 self.confirm(clone is not None 782 and clone.nodeName == doctype.nodeName 783 and clone.name == doctype.name 784 and clone.publicId == doctype.publicId 785 and clone.systemId == doctype.systemId 786 and len(clone.entities) == 0 787 and clone.entities.item(0) is None 788 and len(clone.notations) == 0 789 and clone.notations.item(0) is None 790 and len(clone.childNodes) == 0) 791 792 def testCloneDocumentTypeShallowNotOk(self): 793 doc = create_doc_with_doctype() 794 clone = doc.doctype.cloneNode(0) 795 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk") 796 797 def check_import_document(self, deep, testName): 798 doc1 = parseString("<doc/>") 799 doc2 = parseString("<doc/>") 800 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep) 801 802 def testImportDocumentShallow(self): 803 self.check_import_document(0, "testImportDocumentShallow") 804 805 def testImportDocumentDeep(self): 806 self.check_import_document(1, "testImportDocumentDeep") 807 808 def testImportDocumentTypeShallow(self): 809 src = create_doc_with_doctype() 810 target = create_doc_without_doctype() 811 self.assertRaises(xml.dom.NotSupportedErr, target.importNode, 812 src.doctype, 0) 813 814 def testImportDocumentTypeDeep(self): 815 src = create_doc_with_doctype() 816 target = create_doc_without_doctype() 817 self.assertRaises(xml.dom.NotSupportedErr, target.importNode, 818 src.doctype, 1) 819 820 # Testing attribute clones uses a helper, and should always be deep, 821 # even if the argument to cloneNode is false. 822 def check_clone_attribute(self, deep, testName): 823 doc = parseString("<doc attr='value'/>") 824 attr = doc.documentElement.getAttributeNode("attr") 825 self.assertNotEqual(attr, None) 826 clone = attr.cloneNode(deep) 827 self.confirm(not clone.isSameNode(attr)) 828 self.confirm(not attr.isSameNode(clone)) 829 self.confirm(clone.ownerElement is None, 830 testName + ": ownerElement should be None") 831 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument), 832 testName + ": ownerDocument does not match") 833 self.confirm(clone.specified, 834 testName + ": cloned attribute must have specified == True") 835 836 def testCloneAttributeShallow(self): 837 self.check_clone_attribute(0, "testCloneAttributeShallow") 838 839 def testCloneAttributeDeep(self): 840 self.check_clone_attribute(1, "testCloneAttributeDeep") 841 842 def check_clone_pi(self, deep, testName): 843 doc = parseString("<?target data?><doc/>") 844 pi = doc.firstChild 845 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) 846 clone = pi.cloneNode(deep) 847 self.confirm(clone.target == pi.target 848 and clone.data == pi.data) 849 850 def testClonePIShallow(self): 851 self.check_clone_pi(0, "testClonePIShallow") 852 853 def testClonePIDeep(self): 854 self.check_clone_pi(1, "testClonePIDeep") 855 856 def check_clone_node_entity(self, clone_document): 857 # bpo-35052: Test user data handler in cloneNode() on a document with 858 # an entity 859 document = xml.dom.minidom.parseString(""" 860 <?xml version="1.0" ?> 861 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 862 "http://www.w3.org/TR/html4/strict.dtd" 863 [ <!ENTITY smile "☺"> ] 864 > 865 <doc>Don't let entities make you frown ⌣</doc> 866 """.strip()) 867 868 class Handler: 869 def handle(self, operation, key, data, src, dst): 870 self.operation = operation 871 self.key = key 872 self.data = data 873 self.src = src 874 self.dst = dst 875 876 handler = Handler() 877 doctype = document.doctype 878 entity = doctype.entities['smile'] 879 entity.setUserData("key", "data", handler) 880 881 if clone_document: 882 # clone Document 883 clone = document.cloneNode(deep=True) 884 885 self.assertEqual(clone.documentElement.firstChild.wholeText, 886 "Don't let entities make you frown ☺") 887 operation = xml.dom.UserDataHandler.NODE_IMPORTED 888 dst = clone.doctype.entities['smile'] 889 else: 890 # clone DocumentType 891 with support.swap_attr(doctype, 'ownerDocument', None): 892 clone = doctype.cloneNode(deep=True) 893 894 operation = xml.dom.UserDataHandler.NODE_CLONED 895 dst = clone.entities['smile'] 896 897 self.assertEqual(handler.operation, operation) 898 self.assertEqual(handler.key, "key") 899 self.assertEqual(handler.data, "data") 900 self.assertIs(handler.src, entity) 901 self.assertIs(handler.dst, dst) 902 903 def testCloneNodeEntity(self): 904 self.check_clone_node_entity(False) 905 self.check_clone_node_entity(True) 906 907 def testNormalize(self): 908 doc = parseString("<doc/>") 909 root = doc.documentElement 910 root.appendChild(doc.createTextNode("first")) 911 root.appendChild(doc.createTextNode("second")) 912 self.confirm(len(root.childNodes) == 2 913 and root.childNodes.length == 2, 914 "testNormalize -- preparation") 915 doc.normalize() 916 self.confirm(len(root.childNodes) == 1 917 and root.childNodes.length == 1 918 and root.firstChild is root.lastChild 919 and root.firstChild.data == "firstsecond" 920 , "testNormalize -- result") 921 doc.unlink() 922 923 doc = parseString("<doc/>") 924 root = doc.documentElement 925 root.appendChild(doc.createTextNode("")) 926 doc.normalize() 927 self.confirm(len(root.childNodes) == 0 928 and root.childNodes.length == 0, 929 "testNormalize -- single empty node removed") 930 doc.unlink() 931 932 def testNormalizeCombineAndNextSibling(self): 933 doc = parseString("<doc/>") 934 root = doc.documentElement 935 root.appendChild(doc.createTextNode("first")) 936 root.appendChild(doc.createTextNode("second")) 937 root.appendChild(doc.createElement("i")) 938 self.confirm(len(root.childNodes) == 3 939 and root.childNodes.length == 3, 940 "testNormalizeCombineAndNextSibling -- preparation") 941 doc.normalize() 942 self.confirm(len(root.childNodes) == 2 943 and root.childNodes.length == 2 944 and root.firstChild.data == "firstsecond" 945 and root.firstChild is not root.lastChild 946 and root.firstChild.nextSibling is root.lastChild 947 and root.firstChild.previousSibling is None 948 and root.lastChild.previousSibling is root.firstChild 949 and root.lastChild.nextSibling is None 950 , "testNormalizeCombinedAndNextSibling -- result") 951 doc.unlink() 952 953 def testNormalizeDeleteWithPrevSibling(self): 954 doc = parseString("<doc/>") 955 root = doc.documentElement 956 root.appendChild(doc.createTextNode("first")) 957 root.appendChild(doc.createTextNode("")) 958 self.confirm(len(root.childNodes) == 2 959 and root.childNodes.length == 2, 960 "testNormalizeDeleteWithPrevSibling -- preparation") 961 doc.normalize() 962 self.confirm(len(root.childNodes) == 1 963 and root.childNodes.length == 1 964 and root.firstChild.data == "first" 965 and root.firstChild is root.lastChild 966 and root.firstChild.nextSibling is None 967 and root.firstChild.previousSibling is None 968 , "testNormalizeDeleteWithPrevSibling -- result") 969 doc.unlink() 970 971 def testNormalizeDeleteWithNextSibling(self): 972 doc = parseString("<doc/>") 973 root = doc.documentElement 974 root.appendChild(doc.createTextNode("")) 975 root.appendChild(doc.createTextNode("second")) 976 self.confirm(len(root.childNodes) == 2 977 and root.childNodes.length == 2, 978 "testNormalizeDeleteWithNextSibling -- preparation") 979 doc.normalize() 980 self.confirm(len(root.childNodes) == 1 981 and root.childNodes.length == 1 982 and root.firstChild.data == "second" 983 and root.firstChild is root.lastChild 984 and root.firstChild.nextSibling is None 985 and root.firstChild.previousSibling is None 986 , "testNormalizeDeleteWithNextSibling -- result") 987 doc.unlink() 988 989 def testNormalizeDeleteWithTwoNonTextSiblings(self): 990 doc = parseString("<doc/>") 991 root = doc.documentElement 992 root.appendChild(doc.createElement("i")) 993 root.appendChild(doc.createTextNode("")) 994 root.appendChild(doc.createElement("i")) 995 self.confirm(len(root.childNodes) == 3 996 and root.childNodes.length == 3, 997 "testNormalizeDeleteWithTwoSiblings -- preparation") 998 doc.normalize() 999 self.confirm(len(root.childNodes) == 2 1000 and root.childNodes.length == 2 1001 and root.firstChild is not root.lastChild 1002 and root.firstChild.nextSibling is root.lastChild 1003 and root.firstChild.previousSibling is None 1004 and root.lastChild.previousSibling is root.firstChild 1005 and root.lastChild.nextSibling is None 1006 , "testNormalizeDeleteWithTwoSiblings -- result") 1007 doc.unlink() 1008 1009 def testNormalizeDeleteAndCombine(self): 1010 doc = parseString("<doc/>") 1011 root = doc.documentElement 1012 root.appendChild(doc.createTextNode("")) 1013 root.appendChild(doc.createTextNode("second")) 1014 root.appendChild(doc.createTextNode("")) 1015 root.appendChild(doc.createTextNode("fourth")) 1016 root.appendChild(doc.createTextNode("")) 1017 self.confirm(len(root.childNodes) == 5 1018 and root.childNodes.length == 5, 1019 "testNormalizeDeleteAndCombine -- preparation") 1020 doc.normalize() 1021 self.confirm(len(root.childNodes) == 1 1022 and root.childNodes.length == 1 1023 and root.firstChild is root.lastChild 1024 and root.firstChild.data == "secondfourth" 1025 and root.firstChild.previousSibling is None 1026 and root.firstChild.nextSibling is None 1027 , "testNormalizeDeleteAndCombine -- result") 1028 doc.unlink() 1029 1030 def testNormalizeRecursion(self): 1031 doc = parseString("<doc>" 1032 "<o>" 1033 "<i/>" 1034 "t" 1035 # 1036 #x 1037 "</o>" 1038 "<o>" 1039 "<o>" 1040 "t2" 1041 #x2 1042 "</o>" 1043 "t3" 1044 #x3 1045 "</o>" 1046 # 1047 "</doc>") 1048 root = doc.documentElement 1049 root.childNodes[0].appendChild(doc.createTextNode("")) 1050 root.childNodes[0].appendChild(doc.createTextNode("x")) 1051 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2")) 1052 root.childNodes[1].appendChild(doc.createTextNode("x3")) 1053 root.appendChild(doc.createTextNode("")) 1054 self.confirm(len(root.childNodes) == 3 1055 and root.childNodes.length == 3 1056 and len(root.childNodes[0].childNodes) == 4 1057 and root.childNodes[0].childNodes.length == 4 1058 and len(root.childNodes[1].childNodes) == 3 1059 and root.childNodes[1].childNodes.length == 3 1060 and len(root.childNodes[1].childNodes[0].childNodes) == 2 1061 and root.childNodes[1].childNodes[0].childNodes.length == 2 1062 , "testNormalize2 -- preparation") 1063 doc.normalize() 1064 self.confirm(len(root.childNodes) == 2 1065 and root.childNodes.length == 2 1066 and len(root.childNodes[0].childNodes) == 2 1067 and root.childNodes[0].childNodes.length == 2 1068 and len(root.childNodes[1].childNodes) == 2 1069 and root.childNodes[1].childNodes.length == 2 1070 and len(root.childNodes[1].childNodes[0].childNodes) == 1 1071 and root.childNodes[1].childNodes[0].childNodes.length == 1 1072 , "testNormalize2 -- childNodes lengths") 1073 self.confirm(root.childNodes[0].childNodes[1].data == "tx" 1074 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2" 1075 and root.childNodes[1].childNodes[1].data == "t3x3" 1076 , "testNormalize2 -- joined text fields") 1077 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None 1078 and root.childNodes[0].childNodes[1].previousSibling 1079 is root.childNodes[0].childNodes[0] 1080 and root.childNodes[0].childNodes[0].previousSibling is None 1081 and root.childNodes[0].childNodes[0].nextSibling 1082 is root.childNodes[0].childNodes[1] 1083 and root.childNodes[1].childNodes[1].nextSibling is None 1084 and root.childNodes[1].childNodes[1].previousSibling 1085 is root.childNodes[1].childNodes[0] 1086 and root.childNodes[1].childNodes[0].previousSibling is None 1087 and root.childNodes[1].childNodes[0].nextSibling 1088 is root.childNodes[1].childNodes[1] 1089 , "testNormalize2 -- sibling pointers") 1090 doc.unlink() 1091 1092 1093 def testBug0777884(self): 1094 doc = parseString("<o>text</o>") 1095 text = doc.documentElement.childNodes[0] 1096 self.assertEqual(text.nodeType, Node.TEXT_NODE) 1097 # Should run quietly, doing nothing. 1098 text.normalize() 1099 doc.unlink() 1100 1101 def testBug1433694(self): 1102 doc = parseString("<o><i/>t</o>") 1103 node = doc.documentElement 1104 node.childNodes[1].nodeValue = "" 1105 node.normalize() 1106 self.confirm(node.childNodes[-1].nextSibling is None, 1107 "Final child's .nextSibling should be None") 1108 1109 def testSiblings(self): 1110 doc = parseString("<doc><?pi?>text?<elm/></doc>") 1111 root = doc.documentElement 1112 (pi, text, elm) = root.childNodes 1113 1114 self.confirm(pi.nextSibling is text and 1115 pi.previousSibling is None and 1116 text.nextSibling is elm and 1117 text.previousSibling is pi and 1118 elm.nextSibling is None and 1119 elm.previousSibling is text, "testSiblings") 1120 1121 doc.unlink() 1122 1123 def testParents(self): 1124 doc = parseString( 1125 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>") 1126 root = doc.documentElement 1127 elm1 = root.childNodes[0] 1128 (elm2a, elm2b) = elm1.childNodes 1129 elm3 = elm2b.childNodes[0] 1130 1131 self.confirm(root.parentNode is doc and 1132 elm1.parentNode is root and 1133 elm2a.parentNode is elm1 and 1134 elm2b.parentNode is elm1 and 1135 elm3.parentNode is elm2b, "testParents") 1136 doc.unlink() 1137 1138 def testNodeListItem(self): 1139 doc = parseString("<doc><e/><e/></doc>") 1140 children = doc.childNodes 1141 docelem = children[0] 1142 self.confirm(children[0] is children.item(0) 1143 and children.item(1) is None 1144 and docelem.childNodes.item(0) is docelem.childNodes[0] 1145 and docelem.childNodes.item(1) is docelem.childNodes[1] 1146 and docelem.childNodes.item(0).childNodes.item(0) is None, 1147 "test NodeList.item()") 1148 doc.unlink() 1149 1150 def testEncodings(self): 1151 doc = parseString('<foo>€</foo>') 1152 self.assertEqual(doc.toxml(), 1153 '<?xml version="1.0" ?><foo>\u20ac</foo>') 1154 self.assertEqual(doc.toxml('utf-8'), 1155 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>') 1156 self.assertEqual(doc.toxml('iso-8859-15'), 1157 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>') 1158 self.assertEqual(doc.toxml('us-ascii'), 1159 b'<?xml version="1.0" encoding="us-ascii"?><foo>€</foo>') 1160 self.assertEqual(doc.toxml('utf-16'), 1161 '<?xml version="1.0" encoding="utf-16"?>' 1162 '<foo>\u20ac</foo>'.encode('utf-16')) 1163 1164 # Verify that character decoding errors raise exceptions instead 1165 # of crashing 1166 if pyexpat.version_info >= (2, 4, 5): 1167 self.assertRaises(ExpatError, parseString, 1168 b'<fran\xe7ais></fran\xe7ais>') 1169 self.assertRaises(ExpatError, parseString, 1170 b'<franais>Comment \xe7a va ? Tr\xe8s bien ?</franais>') 1171 else: 1172 self.assertRaises(UnicodeDecodeError, parseString, 1173 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>') 1174 1175 doc.unlink() 1176 1177 def testStandalone(self): 1178 doc = parseString('<foo>€</foo>') 1179 self.assertEqual(doc.toxml(), 1180 '<?xml version="1.0" ?><foo>\u20ac</foo>') 1181 self.assertEqual(doc.toxml(standalone=None), 1182 '<?xml version="1.0" ?><foo>\u20ac</foo>') 1183 self.assertEqual(doc.toxml(standalone=True), 1184 '<?xml version="1.0" standalone="yes"?><foo>\u20ac</foo>') 1185 self.assertEqual(doc.toxml(standalone=False), 1186 '<?xml version="1.0" standalone="no"?><foo>\u20ac</foo>') 1187 self.assertEqual(doc.toxml('utf-8', True), 1188 b'<?xml version="1.0" encoding="utf-8" standalone="yes"?>' 1189 b'<foo>\xe2\x82\xac</foo>') 1190 1191 doc.unlink() 1192 1193 class UserDataHandler: 1194 called = 0 1195 def handle(self, operation, key, data, src, dst): 1196 dst.setUserData(key, data + 1, self) 1197 src.setUserData(key, None, None) 1198 self.called = 1 1199 1200 def testUserData(self): 1201 dom = Document() 1202 n = dom.createElement('e') 1203 self.confirm(n.getUserData("foo") is None) 1204 n.setUserData("foo", None, None) 1205 self.confirm(n.getUserData("foo") is None) 1206 n.setUserData("foo", 12, 12) 1207 n.setUserData("bar", 13, 13) 1208 self.confirm(n.getUserData("foo") == 12) 1209 self.confirm(n.getUserData("bar") == 13) 1210 n.setUserData("foo", None, None) 1211 self.confirm(n.getUserData("foo") is None) 1212 self.confirm(n.getUserData("bar") == 13) 1213 1214 handler = self.UserDataHandler() 1215 n.setUserData("bar", 12, handler) 1216 c = n.cloneNode(1) 1217 self.confirm(handler.called 1218 and n.getUserData("bar") is None 1219 and c.getUserData("bar") == 13) 1220 n.unlink() 1221 c.unlink() 1222 dom.unlink() 1223 1224 def checkRenameNodeSharedConstraints(self, doc, node): 1225 # Make sure illegal NS usage is detected: 1226 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, 1227 "http://xml.python.org/ns", "xmlns:foo") 1228 doc2 = parseString("<doc/>") 1229 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, 1230 xml.dom.EMPTY_NAMESPACE, "foo") 1231 1232 def testRenameAttribute(self): 1233 doc = parseString("<doc a='v'/>") 1234 elem = doc.documentElement 1235 attrmap = elem.attributes 1236 attr = elem.attributes['a'] 1237 1238 # Simple renaming 1239 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") 1240 self.confirm(attr.name == "b" 1241 and attr.nodeName == "b" 1242 and attr.localName is None 1243 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1244 and attr.prefix is None 1245 and attr.value == "v" 1246 and elem.getAttributeNode("a") is None 1247 and elem.getAttributeNode("b").isSameNode(attr) 1248 and attrmap["b"].isSameNode(attr) 1249 and attr.ownerDocument.isSameNode(doc) 1250 and attr.ownerElement.isSameNode(elem)) 1251 1252 # Rename to have a namespace, no prefix 1253 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") 1254 self.confirm(attr.name == "c" 1255 and attr.nodeName == "c" 1256 and attr.localName == "c" 1257 and attr.namespaceURI == "http://xml.python.org/ns" 1258 and attr.prefix is None 1259 and attr.value == "v" 1260 and elem.getAttributeNode("a") is None 1261 and elem.getAttributeNode("b") is None 1262 and elem.getAttributeNode("c").isSameNode(attr) 1263 and elem.getAttributeNodeNS( 1264 "http://xml.python.org/ns", "c").isSameNode(attr) 1265 and attrmap["c"].isSameNode(attr) 1266 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) 1267 1268 # Rename to have a namespace, with prefix 1269 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") 1270 self.confirm(attr.name == "p:d" 1271 and attr.nodeName == "p:d" 1272 and attr.localName == "d" 1273 and attr.namespaceURI == "http://xml.python.org/ns2" 1274 and attr.prefix == "p" 1275 and attr.value == "v" 1276 and elem.getAttributeNode("a") is None 1277 and elem.getAttributeNode("b") is None 1278 and elem.getAttributeNode("c") is None 1279 and elem.getAttributeNodeNS( 1280 "http://xml.python.org/ns", "c") is None 1281 and elem.getAttributeNode("p:d").isSameNode(attr) 1282 and elem.getAttributeNodeNS( 1283 "http://xml.python.org/ns2", "d").isSameNode(attr) 1284 and attrmap["p:d"].isSameNode(attr) 1285 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) 1286 1287 # Rename back to a simple non-NS node 1288 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") 1289 self.confirm(attr.name == "e" 1290 and attr.nodeName == "e" 1291 and attr.localName is None 1292 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1293 and attr.prefix is None 1294 and attr.value == "v" 1295 and elem.getAttributeNode("a") is None 1296 and elem.getAttributeNode("b") is None 1297 and elem.getAttributeNode("c") is None 1298 and elem.getAttributeNode("p:d") is None 1299 and elem.getAttributeNodeNS( 1300 "http://xml.python.org/ns", "c") is None 1301 and elem.getAttributeNode("e").isSameNode(attr) 1302 and attrmap["e"].isSameNode(attr)) 1303 1304 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, 1305 "http://xml.python.org/ns", "xmlns") 1306 self.checkRenameNodeSharedConstraints(doc, attr) 1307 doc.unlink() 1308 1309 def testRenameElement(self): 1310 doc = parseString("<doc/>") 1311 elem = doc.documentElement 1312 1313 # Simple renaming 1314 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") 1315 self.confirm(elem.tagName == "a" 1316 and elem.nodeName == "a" 1317 and elem.localName is None 1318 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1319 and elem.prefix is None 1320 and elem.ownerDocument.isSameNode(doc)) 1321 1322 # Rename to have a namespace, no prefix 1323 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") 1324 self.confirm(elem.tagName == "b" 1325 and elem.nodeName == "b" 1326 and elem.localName == "b" 1327 and elem.namespaceURI == "http://xml.python.org/ns" 1328 and elem.prefix is None 1329 and elem.ownerDocument.isSameNode(doc)) 1330 1331 # Rename to have a namespace, with prefix 1332 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") 1333 self.confirm(elem.tagName == "p:c" 1334 and elem.nodeName == "p:c" 1335 and elem.localName == "c" 1336 and elem.namespaceURI == "http://xml.python.org/ns2" 1337 and elem.prefix == "p" 1338 and elem.ownerDocument.isSameNode(doc)) 1339 1340 # Rename back to a simple non-NS node 1341 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") 1342 self.confirm(elem.tagName == "d" 1343 and elem.nodeName == "d" 1344 and elem.localName is None 1345 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1346 and elem.prefix is None 1347 and elem.ownerDocument.isSameNode(doc)) 1348 1349 self.checkRenameNodeSharedConstraints(doc, elem) 1350 doc.unlink() 1351 1352 def testRenameOther(self): 1353 # We have to create a comment node explicitly since not all DOM 1354 # builders used with minidom add comments to the DOM. 1355 doc = xml.dom.minidom.getDOMImplementation().createDocument( 1356 xml.dom.EMPTY_NAMESPACE, "e", None) 1357 node = doc.createComment("comment") 1358 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, 1359 xml.dom.EMPTY_NAMESPACE, "foo") 1360 doc.unlink() 1361 1362 def testWholeText(self): 1363 doc = parseString("<doc>a</doc>") 1364 elem = doc.documentElement 1365 text = elem.childNodes[0] 1366 self.assertEqual(text.nodeType, Node.TEXT_NODE) 1367 1368 self.checkWholeText(text, "a") 1369 elem.appendChild(doc.createTextNode("b")) 1370 self.checkWholeText(text, "ab") 1371 elem.insertBefore(doc.createCDATASection("c"), text) 1372 self.checkWholeText(text, "cab") 1373 1374 # make sure we don't cross other nodes 1375 splitter = doc.createComment("comment") 1376 elem.appendChild(splitter) 1377 text2 = doc.createTextNode("d") 1378 elem.appendChild(text2) 1379 self.checkWholeText(text, "cab") 1380 self.checkWholeText(text2, "d") 1381 1382 x = doc.createElement("x") 1383 elem.replaceChild(x, splitter) 1384 splitter = x 1385 self.checkWholeText(text, "cab") 1386 self.checkWholeText(text2, "d") 1387 1388 x = doc.createProcessingInstruction("y", "z") 1389 elem.replaceChild(x, splitter) 1390 splitter = x 1391 self.checkWholeText(text, "cab") 1392 self.checkWholeText(text2, "d") 1393 1394 elem.removeChild(splitter) 1395 self.checkWholeText(text, "cabd") 1396 self.checkWholeText(text2, "cabd") 1397 1398 def testPatch1094164(self): 1399 doc = parseString("<doc><e/></doc>") 1400 elem = doc.documentElement 1401 e = elem.firstChild 1402 self.confirm(e.parentNode is elem, "Before replaceChild()") 1403 # Check that replacing a child with itself leaves the tree unchanged 1404 elem.replaceChild(e, e) 1405 self.confirm(e.parentNode is elem, "After replaceChild()") 1406 1407 def testReplaceWholeText(self): 1408 def setup(): 1409 doc = parseString("<doc>a<e/>d</doc>") 1410 elem = doc.documentElement 1411 text1 = elem.firstChild 1412 text2 = elem.lastChild 1413 splitter = text1.nextSibling 1414 elem.insertBefore(doc.createTextNode("b"), splitter) 1415 elem.insertBefore(doc.createCDATASection("c"), text1) 1416 return doc, elem, text1, splitter, text2 1417 1418 doc, elem, text1, splitter, text2 = setup() 1419 text = text1.replaceWholeText("new content") 1420 self.checkWholeText(text, "new content") 1421 self.checkWholeText(text2, "d") 1422 self.confirm(len(elem.childNodes) == 3) 1423 1424 doc, elem, text1, splitter, text2 = setup() 1425 text = text2.replaceWholeText("new content") 1426 self.checkWholeText(text, "new content") 1427 self.checkWholeText(text1, "cab") 1428 self.confirm(len(elem.childNodes) == 5) 1429 1430 doc, elem, text1, splitter, text2 = setup() 1431 text = text1.replaceWholeText("") 1432 self.checkWholeText(text2, "d") 1433 self.confirm(text is None 1434 and len(elem.childNodes) == 2) 1435 1436 def testSchemaType(self): 1437 doc = parseString( 1438 "<!DOCTYPE doc [\n" 1439 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" 1440 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" 1441 " <!ATTLIST doc id ID #IMPLIED \n" 1442 " ref IDREF #IMPLIED \n" 1443 " refs IDREFS #IMPLIED \n" 1444 " enum (a|b) #IMPLIED \n" 1445 " ent ENTITY #IMPLIED \n" 1446 " ents ENTITIES #IMPLIED \n" 1447 " nm NMTOKEN #IMPLIED \n" 1448 " nms NMTOKENS #IMPLIED \n" 1449 " text CDATA #IMPLIED \n" 1450 " >\n" 1451 "]><doc id='name' notid='name' text='splat!' enum='b'" 1452 " ref='name' refs='name name' ent='e1' ents='e1 e2'" 1453 " nm='123' nms='123 abc' />") 1454 elem = doc.documentElement 1455 # We don't want to rely on any specific loader at this point, so 1456 # just make sure we can get to all the names, and that the 1457 # DTD-based namespace is right. The names can vary by loader 1458 # since each supports a different level of DTD information. 1459 t = elem.schemaType 1460 self.confirm(t.name is None 1461 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1462 names = "id notid text enum ref refs ent ents nm nms".split() 1463 for name in names: 1464 a = elem.getAttributeNode(name) 1465 t = a.schemaType 1466 self.confirm(hasattr(t, "name") 1467 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1468 1469 def testSetIdAttribute(self): 1470 doc = parseString("<doc a1='v' a2='w'/>") 1471 e = doc.documentElement 1472 a1 = e.getAttributeNode("a1") 1473 a2 = e.getAttributeNode("a2") 1474 self.confirm(doc.getElementById("v") is None 1475 and not a1.isId 1476 and not a2.isId) 1477 e.setIdAttribute("a1") 1478 self.confirm(e.isSameNode(doc.getElementById("v")) 1479 and a1.isId 1480 and not a2.isId) 1481 e.setIdAttribute("a2") 1482 self.confirm(e.isSameNode(doc.getElementById("v")) 1483 and e.isSameNode(doc.getElementById("w")) 1484 and a1.isId 1485 and a2.isId) 1486 # replace the a1 node; the new node should *not* be an ID 1487 a3 = doc.createAttribute("a1") 1488 a3.value = "v" 1489 e.setAttributeNode(a3) 1490 self.confirm(doc.getElementById("v") is None 1491 and e.isSameNode(doc.getElementById("w")) 1492 and not a1.isId 1493 and a2.isId 1494 and not a3.isId) 1495 # renaming an attribute should not affect its ID-ness: 1496 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1497 self.confirm(e.isSameNode(doc.getElementById("w")) 1498 and a2.isId) 1499 1500 def testSetIdAttributeNS(self): 1501 NS1 = "http://xml.python.org/ns1" 1502 NS2 = "http://xml.python.org/ns2" 1503 doc = parseString("<doc" 1504 " xmlns:ns1='" + NS1 + "'" 1505 " xmlns:ns2='" + NS2 + "'" 1506 " ns1:a1='v' ns2:a2='w'/>") 1507 e = doc.documentElement 1508 a1 = e.getAttributeNodeNS(NS1, "a1") 1509 a2 = e.getAttributeNodeNS(NS2, "a2") 1510 self.confirm(doc.getElementById("v") is None 1511 and not a1.isId 1512 and not a2.isId) 1513 e.setIdAttributeNS(NS1, "a1") 1514 self.confirm(e.isSameNode(doc.getElementById("v")) 1515 and a1.isId 1516 and not a2.isId) 1517 e.setIdAttributeNS(NS2, "a2") 1518 self.confirm(e.isSameNode(doc.getElementById("v")) 1519 and e.isSameNode(doc.getElementById("w")) 1520 and a1.isId 1521 and a2.isId) 1522 # replace the a1 node; the new node should *not* be an ID 1523 a3 = doc.createAttributeNS(NS1, "a1") 1524 a3.value = "v" 1525 e.setAttributeNode(a3) 1526 self.confirm(e.isSameNode(doc.getElementById("w"))) 1527 self.confirm(not a1.isId) 1528 self.confirm(a2.isId) 1529 self.confirm(not a3.isId) 1530 self.confirm(doc.getElementById("v") is None) 1531 # renaming an attribute should not affect its ID-ness: 1532 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1533 self.confirm(e.isSameNode(doc.getElementById("w")) 1534 and a2.isId) 1535 1536 def testSetIdAttributeNode(self): 1537 NS1 = "http://xml.python.org/ns1" 1538 NS2 = "http://xml.python.org/ns2" 1539 doc = parseString("<doc" 1540 " xmlns:ns1='" + NS1 + "'" 1541 " xmlns:ns2='" + NS2 + "'" 1542 " ns1:a1='v' ns2:a2='w'/>") 1543 e = doc.documentElement 1544 a1 = e.getAttributeNodeNS(NS1, "a1") 1545 a2 = e.getAttributeNodeNS(NS2, "a2") 1546 self.confirm(doc.getElementById("v") is None 1547 and not a1.isId 1548 and not a2.isId) 1549 e.setIdAttributeNode(a1) 1550 self.confirm(e.isSameNode(doc.getElementById("v")) 1551 and a1.isId 1552 and not a2.isId) 1553 e.setIdAttributeNode(a2) 1554 self.confirm(e.isSameNode(doc.getElementById("v")) 1555 and e.isSameNode(doc.getElementById("w")) 1556 and a1.isId 1557 and a2.isId) 1558 # replace the a1 node; the new node should *not* be an ID 1559 a3 = doc.createAttributeNS(NS1, "a1") 1560 a3.value = "v" 1561 e.setAttributeNode(a3) 1562 self.confirm(e.isSameNode(doc.getElementById("w"))) 1563 self.confirm(not a1.isId) 1564 self.confirm(a2.isId) 1565 self.confirm(not a3.isId) 1566 self.confirm(doc.getElementById("v") is None) 1567 # renaming an attribute should not affect its ID-ness: 1568 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1569 self.confirm(e.isSameNode(doc.getElementById("w")) 1570 and a2.isId) 1571 1572 def assert_recursive_equal(self, doc, doc2): 1573 stack = [(doc, doc2)] 1574 while stack: 1575 n1, n2 = stack.pop() 1576 self.assertEqual(n1.nodeType, n2.nodeType) 1577 self.assertEqual(len(n1.childNodes), len(n2.childNodes)) 1578 self.assertEqual(n1.nodeName, n2.nodeName) 1579 self.assertFalse(n1.isSameNode(n2)) 1580 self.assertFalse(n2.isSameNode(n1)) 1581 if n1.nodeType == Node.DOCUMENT_TYPE_NODE: 1582 len(n1.entities) 1583 len(n2.entities) 1584 len(n1.notations) 1585 len(n2.notations) 1586 self.assertEqual(len(n1.entities), len(n2.entities)) 1587 self.assertEqual(len(n1.notations), len(n2.notations)) 1588 for i in range(len(n1.notations)): 1589 # XXX this loop body doesn't seem to be executed? 1590 no1 = n1.notations.item(i) 1591 no2 = n1.notations.item(i) 1592 self.assertEqual(no1.name, no2.name) 1593 self.assertEqual(no1.publicId, no2.publicId) 1594 self.assertEqual(no1.systemId, no2.systemId) 1595 stack.append((no1, no2)) 1596 for i in range(len(n1.entities)): 1597 e1 = n1.entities.item(i) 1598 e2 = n2.entities.item(i) 1599 self.assertEqual(e1.notationName, e2.notationName) 1600 self.assertEqual(e1.publicId, e2.publicId) 1601 self.assertEqual(e1.systemId, e2.systemId) 1602 stack.append((e1, e2)) 1603 if n1.nodeType != Node.DOCUMENT_NODE: 1604 self.assertTrue(n1.ownerDocument.isSameNode(doc)) 1605 self.assertTrue(n2.ownerDocument.isSameNode(doc2)) 1606 for i in range(len(n1.childNodes)): 1607 stack.append((n1.childNodes[i], n2.childNodes[i])) 1608 1609 def testPickledDocument(self): 1610 doc = parseString(sample) 1611 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): 1612 s = pickle.dumps(doc, proto) 1613 doc2 = pickle.loads(s) 1614 self.assert_recursive_equal(doc, doc2) 1615 1616 def testDeepcopiedDocument(self): 1617 doc = parseString(sample) 1618 doc2 = copy.deepcopy(doc) 1619 self.assert_recursive_equal(doc, doc2) 1620 1621 def testSerializeCommentNodeWithDoubleHyphen(self): 1622 doc = create_doc_without_doctype() 1623 doc.appendChild(doc.createComment("foo--bar")) 1624 self.assertRaises(ValueError, doc.toxml) 1625 1626 1627 def testEmptyXMLNSValue(self): 1628 doc = parseString("<element xmlns=''>\n" 1629 "<foo/>\n</element>") 1630 doc2 = parseString(doc.toxml()) 1631 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) 1632 1633 def testExceptionOnSpacesInXMLNSValue(self): 1634 if pyexpat.version_info >= (2, 4, 5): 1635 context = self.assertRaisesRegex(ExpatError, 'syntax error') 1636 else: 1637 context = self.assertRaisesRegex(ValueError, 'Unsupported syntax') 1638 1639 with context: 1640 parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>') 1641 1642 def testDocRemoveChild(self): 1643 doc = parse(tstfile) 1644 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0] 1645 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag) 1646 num_children_before = len(doc.childNodes) 1647 doc.removeChild(doc.childNodes[0]) 1648 num_children_after = len(doc.childNodes) 1649 self.assertTrue(num_children_after == num_children_before - 1) 1650 1651 def testProcessingInstructionNameError(self): 1652 # wrong variable in .nodeValue property will 1653 # lead to "NameError: name 'data' is not defined" 1654 doc = parse(tstfile) 1655 pi = doc.createProcessingInstruction("y", "z") 1656 pi.nodeValue = "crash" 1657 1658 def test_minidom_attribute_order(self): 1659 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1660 doc = parseString(xml_str) 1661 output = io.StringIO() 1662 doc.writexml(output) 1663 self.assertEqual(output.getvalue(), xml_str) 1664 1665 def test_toxml_with_attributes_ordered(self): 1666 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1667 doc = parseString(xml_str) 1668 self.assertEqual(doc.toxml(), xml_str) 1669 1670 def test_toprettyxml_with_attributes_ordered(self): 1671 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1672 doc = parseString(xml_str) 1673 self.assertEqual(doc.toprettyxml(), 1674 '<?xml version="1.0" ?>\n' 1675 '<curriculum status="public" company="example"/>\n') 1676 1677 def test_toprettyxml_with_cdata(self): 1678 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' 1679 doc = parseString(xml_str) 1680 self.assertEqual(doc.toprettyxml(), 1681 '<?xml version="1.0" ?>\n' 1682 '<root>\n' 1683 '\t<node><![CDATA[</data>]]></node>\n' 1684 '</root>\n') 1685 1686 def test_cdata_parsing(self): 1687 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' 1688 dom1 = parseString(xml_str) 1689 self.checkWholeText(dom1.getElementsByTagName('node')[0].firstChild, '</data>') 1690 dom2 = parseString(dom1.toprettyxml()) 1691 self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '</data>') 1692 1693if __name__ == "__main__": 1694 unittest.main() 1695