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