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 def testStandalone(self): 1156 doc = parseString('<foo>€</foo>') 1157 self.assertEqual(doc.toxml(), 1158 '<?xml version="1.0" ?><foo>\u20ac</foo>') 1159 self.assertEqual(doc.toxml(standalone=None), 1160 '<?xml version="1.0" ?><foo>\u20ac</foo>') 1161 self.assertEqual(doc.toxml(standalone=True), 1162 '<?xml version="1.0" standalone="yes"?><foo>\u20ac</foo>') 1163 self.assertEqual(doc.toxml(standalone=False), 1164 '<?xml version="1.0" standalone="no"?><foo>\u20ac</foo>') 1165 self.assertEqual(doc.toxml('utf-8', True), 1166 b'<?xml version="1.0" encoding="utf-8" standalone="yes"?>' 1167 b'<foo>\xe2\x82\xac</foo>') 1168 1169 doc.unlink() 1170 1171 class UserDataHandler: 1172 called = 0 1173 def handle(self, operation, key, data, src, dst): 1174 dst.setUserData(key, data + 1, self) 1175 src.setUserData(key, None, None) 1176 self.called = 1 1177 1178 def testUserData(self): 1179 dom = Document() 1180 n = dom.createElement('e') 1181 self.confirm(n.getUserData("foo") is None) 1182 n.setUserData("foo", None, None) 1183 self.confirm(n.getUserData("foo") is None) 1184 n.setUserData("foo", 12, 12) 1185 n.setUserData("bar", 13, 13) 1186 self.confirm(n.getUserData("foo") == 12) 1187 self.confirm(n.getUserData("bar") == 13) 1188 n.setUserData("foo", None, None) 1189 self.confirm(n.getUserData("foo") is None) 1190 self.confirm(n.getUserData("bar") == 13) 1191 1192 handler = self.UserDataHandler() 1193 n.setUserData("bar", 12, handler) 1194 c = n.cloneNode(1) 1195 self.confirm(handler.called 1196 and n.getUserData("bar") is None 1197 and c.getUserData("bar") == 13) 1198 n.unlink() 1199 c.unlink() 1200 dom.unlink() 1201 1202 def checkRenameNodeSharedConstraints(self, doc, node): 1203 # Make sure illegal NS usage is detected: 1204 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, 1205 "http://xml.python.org/ns", "xmlns:foo") 1206 doc2 = parseString("<doc/>") 1207 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, 1208 xml.dom.EMPTY_NAMESPACE, "foo") 1209 1210 def testRenameAttribute(self): 1211 doc = parseString("<doc a='v'/>") 1212 elem = doc.documentElement 1213 attrmap = elem.attributes 1214 attr = elem.attributes['a'] 1215 1216 # Simple renaming 1217 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") 1218 self.confirm(attr.name == "b" 1219 and attr.nodeName == "b" 1220 and attr.localName is None 1221 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1222 and attr.prefix is None 1223 and attr.value == "v" 1224 and elem.getAttributeNode("a") is None 1225 and elem.getAttributeNode("b").isSameNode(attr) 1226 and attrmap["b"].isSameNode(attr) 1227 and attr.ownerDocument.isSameNode(doc) 1228 and attr.ownerElement.isSameNode(elem)) 1229 1230 # Rename to have a namespace, no prefix 1231 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") 1232 self.confirm(attr.name == "c" 1233 and attr.nodeName == "c" 1234 and attr.localName == "c" 1235 and attr.namespaceURI == "http://xml.python.org/ns" 1236 and attr.prefix is None 1237 and attr.value == "v" 1238 and elem.getAttributeNode("a") is None 1239 and elem.getAttributeNode("b") is None 1240 and elem.getAttributeNode("c").isSameNode(attr) 1241 and elem.getAttributeNodeNS( 1242 "http://xml.python.org/ns", "c").isSameNode(attr) 1243 and attrmap["c"].isSameNode(attr) 1244 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) 1245 1246 # Rename to have a namespace, with prefix 1247 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") 1248 self.confirm(attr.name == "p:d" 1249 and attr.nodeName == "p:d" 1250 and attr.localName == "d" 1251 and attr.namespaceURI == "http://xml.python.org/ns2" 1252 and attr.prefix == "p" 1253 and attr.value == "v" 1254 and elem.getAttributeNode("a") is None 1255 and elem.getAttributeNode("b") is None 1256 and elem.getAttributeNode("c") is None 1257 and elem.getAttributeNodeNS( 1258 "http://xml.python.org/ns", "c") is None 1259 and elem.getAttributeNode("p:d").isSameNode(attr) 1260 and elem.getAttributeNodeNS( 1261 "http://xml.python.org/ns2", "d").isSameNode(attr) 1262 and attrmap["p:d"].isSameNode(attr) 1263 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) 1264 1265 # Rename back to a simple non-NS node 1266 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") 1267 self.confirm(attr.name == "e" 1268 and attr.nodeName == "e" 1269 and attr.localName is None 1270 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1271 and attr.prefix is None 1272 and attr.value == "v" 1273 and elem.getAttributeNode("a") is None 1274 and elem.getAttributeNode("b") is None 1275 and elem.getAttributeNode("c") is None 1276 and elem.getAttributeNode("p:d") is None 1277 and elem.getAttributeNodeNS( 1278 "http://xml.python.org/ns", "c") is None 1279 and elem.getAttributeNode("e").isSameNode(attr) 1280 and attrmap["e"].isSameNode(attr)) 1281 1282 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, 1283 "http://xml.python.org/ns", "xmlns") 1284 self.checkRenameNodeSharedConstraints(doc, attr) 1285 doc.unlink() 1286 1287 def testRenameElement(self): 1288 doc = parseString("<doc/>") 1289 elem = doc.documentElement 1290 1291 # Simple renaming 1292 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") 1293 self.confirm(elem.tagName == "a" 1294 and elem.nodeName == "a" 1295 and elem.localName is None 1296 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1297 and elem.prefix is None 1298 and elem.ownerDocument.isSameNode(doc)) 1299 1300 # Rename to have a namespace, no prefix 1301 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") 1302 self.confirm(elem.tagName == "b" 1303 and elem.nodeName == "b" 1304 and elem.localName == "b" 1305 and elem.namespaceURI == "http://xml.python.org/ns" 1306 and elem.prefix is None 1307 and elem.ownerDocument.isSameNode(doc)) 1308 1309 # Rename to have a namespace, with prefix 1310 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") 1311 self.confirm(elem.tagName == "p:c" 1312 and elem.nodeName == "p:c" 1313 and elem.localName == "c" 1314 and elem.namespaceURI == "http://xml.python.org/ns2" 1315 and elem.prefix == "p" 1316 and elem.ownerDocument.isSameNode(doc)) 1317 1318 # Rename back to a simple non-NS node 1319 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") 1320 self.confirm(elem.tagName == "d" 1321 and elem.nodeName == "d" 1322 and elem.localName is None 1323 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1324 and elem.prefix is None 1325 and elem.ownerDocument.isSameNode(doc)) 1326 1327 self.checkRenameNodeSharedConstraints(doc, elem) 1328 doc.unlink() 1329 1330 def testRenameOther(self): 1331 # We have to create a comment node explicitly since not all DOM 1332 # builders used with minidom add comments to the DOM. 1333 doc = xml.dom.minidom.getDOMImplementation().createDocument( 1334 xml.dom.EMPTY_NAMESPACE, "e", None) 1335 node = doc.createComment("comment") 1336 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, 1337 xml.dom.EMPTY_NAMESPACE, "foo") 1338 doc.unlink() 1339 1340 def testWholeText(self): 1341 doc = parseString("<doc>a</doc>") 1342 elem = doc.documentElement 1343 text = elem.childNodes[0] 1344 self.assertEqual(text.nodeType, Node.TEXT_NODE) 1345 1346 self.checkWholeText(text, "a") 1347 elem.appendChild(doc.createTextNode("b")) 1348 self.checkWholeText(text, "ab") 1349 elem.insertBefore(doc.createCDATASection("c"), text) 1350 self.checkWholeText(text, "cab") 1351 1352 # make sure we don't cross other nodes 1353 splitter = doc.createComment("comment") 1354 elem.appendChild(splitter) 1355 text2 = doc.createTextNode("d") 1356 elem.appendChild(text2) 1357 self.checkWholeText(text, "cab") 1358 self.checkWholeText(text2, "d") 1359 1360 x = doc.createElement("x") 1361 elem.replaceChild(x, splitter) 1362 splitter = x 1363 self.checkWholeText(text, "cab") 1364 self.checkWholeText(text2, "d") 1365 1366 x = doc.createProcessingInstruction("y", "z") 1367 elem.replaceChild(x, splitter) 1368 splitter = x 1369 self.checkWholeText(text, "cab") 1370 self.checkWholeText(text2, "d") 1371 1372 elem.removeChild(splitter) 1373 self.checkWholeText(text, "cabd") 1374 self.checkWholeText(text2, "cabd") 1375 1376 def testPatch1094164(self): 1377 doc = parseString("<doc><e/></doc>") 1378 elem = doc.documentElement 1379 e = elem.firstChild 1380 self.confirm(e.parentNode is elem, "Before replaceChild()") 1381 # Check that replacing a child with itself leaves the tree unchanged 1382 elem.replaceChild(e, e) 1383 self.confirm(e.parentNode is elem, "After replaceChild()") 1384 1385 def testReplaceWholeText(self): 1386 def setup(): 1387 doc = parseString("<doc>a<e/>d</doc>") 1388 elem = doc.documentElement 1389 text1 = elem.firstChild 1390 text2 = elem.lastChild 1391 splitter = text1.nextSibling 1392 elem.insertBefore(doc.createTextNode("b"), splitter) 1393 elem.insertBefore(doc.createCDATASection("c"), text1) 1394 return doc, elem, text1, splitter, text2 1395 1396 doc, elem, text1, splitter, text2 = setup() 1397 text = text1.replaceWholeText("new content") 1398 self.checkWholeText(text, "new content") 1399 self.checkWholeText(text2, "d") 1400 self.confirm(len(elem.childNodes) == 3) 1401 1402 doc, elem, text1, splitter, text2 = setup() 1403 text = text2.replaceWholeText("new content") 1404 self.checkWholeText(text, "new content") 1405 self.checkWholeText(text1, "cab") 1406 self.confirm(len(elem.childNodes) == 5) 1407 1408 doc, elem, text1, splitter, text2 = setup() 1409 text = text1.replaceWholeText("") 1410 self.checkWholeText(text2, "d") 1411 self.confirm(text is None 1412 and len(elem.childNodes) == 2) 1413 1414 def testSchemaType(self): 1415 doc = parseString( 1416 "<!DOCTYPE doc [\n" 1417 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" 1418 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" 1419 " <!ATTLIST doc id ID #IMPLIED \n" 1420 " ref IDREF #IMPLIED \n" 1421 " refs IDREFS #IMPLIED \n" 1422 " enum (a|b) #IMPLIED \n" 1423 " ent ENTITY #IMPLIED \n" 1424 " ents ENTITIES #IMPLIED \n" 1425 " nm NMTOKEN #IMPLIED \n" 1426 " nms NMTOKENS #IMPLIED \n" 1427 " text CDATA #IMPLIED \n" 1428 " >\n" 1429 "]><doc id='name' notid='name' text='splat!' enum='b'" 1430 " ref='name' refs='name name' ent='e1' ents='e1 e2'" 1431 " nm='123' nms='123 abc' />") 1432 elem = doc.documentElement 1433 # We don't want to rely on any specific loader at this point, so 1434 # just make sure we can get to all the names, and that the 1435 # DTD-based namespace is right. The names can vary by loader 1436 # since each supports a different level of DTD information. 1437 t = elem.schemaType 1438 self.confirm(t.name is None 1439 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1440 names = "id notid text enum ref refs ent ents nm nms".split() 1441 for name in names: 1442 a = elem.getAttributeNode(name) 1443 t = a.schemaType 1444 self.confirm(hasattr(t, "name") 1445 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1446 1447 def testSetIdAttribute(self): 1448 doc = parseString("<doc a1='v' a2='w'/>") 1449 e = doc.documentElement 1450 a1 = e.getAttributeNode("a1") 1451 a2 = e.getAttributeNode("a2") 1452 self.confirm(doc.getElementById("v") is None 1453 and not a1.isId 1454 and not a2.isId) 1455 e.setIdAttribute("a1") 1456 self.confirm(e.isSameNode(doc.getElementById("v")) 1457 and a1.isId 1458 and not a2.isId) 1459 e.setIdAttribute("a2") 1460 self.confirm(e.isSameNode(doc.getElementById("v")) 1461 and e.isSameNode(doc.getElementById("w")) 1462 and a1.isId 1463 and a2.isId) 1464 # replace the a1 node; the new node should *not* be an ID 1465 a3 = doc.createAttribute("a1") 1466 a3.value = "v" 1467 e.setAttributeNode(a3) 1468 self.confirm(doc.getElementById("v") is None 1469 and e.isSameNode(doc.getElementById("w")) 1470 and not a1.isId 1471 and a2.isId 1472 and not a3.isId) 1473 # renaming an attribute should not affect its ID-ness: 1474 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1475 self.confirm(e.isSameNode(doc.getElementById("w")) 1476 and a2.isId) 1477 1478 def testSetIdAttributeNS(self): 1479 NS1 = "http://xml.python.org/ns1" 1480 NS2 = "http://xml.python.org/ns2" 1481 doc = parseString("<doc" 1482 " xmlns:ns1='" + NS1 + "'" 1483 " xmlns:ns2='" + NS2 + "'" 1484 " ns1:a1='v' ns2:a2='w'/>") 1485 e = doc.documentElement 1486 a1 = e.getAttributeNodeNS(NS1, "a1") 1487 a2 = e.getAttributeNodeNS(NS2, "a2") 1488 self.confirm(doc.getElementById("v") is None 1489 and not a1.isId 1490 and not a2.isId) 1491 e.setIdAttributeNS(NS1, "a1") 1492 self.confirm(e.isSameNode(doc.getElementById("v")) 1493 and a1.isId 1494 and not a2.isId) 1495 e.setIdAttributeNS(NS2, "a2") 1496 self.confirm(e.isSameNode(doc.getElementById("v")) 1497 and e.isSameNode(doc.getElementById("w")) 1498 and a1.isId 1499 and a2.isId) 1500 # replace the a1 node; the new node should *not* be an ID 1501 a3 = doc.createAttributeNS(NS1, "a1") 1502 a3.value = "v" 1503 e.setAttributeNode(a3) 1504 self.confirm(e.isSameNode(doc.getElementById("w"))) 1505 self.confirm(not a1.isId) 1506 self.confirm(a2.isId) 1507 self.confirm(not a3.isId) 1508 self.confirm(doc.getElementById("v") is None) 1509 # renaming an attribute should not affect its ID-ness: 1510 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1511 self.confirm(e.isSameNode(doc.getElementById("w")) 1512 and a2.isId) 1513 1514 def testSetIdAttributeNode(self): 1515 NS1 = "http://xml.python.org/ns1" 1516 NS2 = "http://xml.python.org/ns2" 1517 doc = parseString("<doc" 1518 " xmlns:ns1='" + NS1 + "'" 1519 " xmlns:ns2='" + NS2 + "'" 1520 " ns1:a1='v' ns2:a2='w'/>") 1521 e = doc.documentElement 1522 a1 = e.getAttributeNodeNS(NS1, "a1") 1523 a2 = e.getAttributeNodeNS(NS2, "a2") 1524 self.confirm(doc.getElementById("v") is None 1525 and not a1.isId 1526 and not a2.isId) 1527 e.setIdAttributeNode(a1) 1528 self.confirm(e.isSameNode(doc.getElementById("v")) 1529 and a1.isId 1530 and not a2.isId) 1531 e.setIdAttributeNode(a2) 1532 self.confirm(e.isSameNode(doc.getElementById("v")) 1533 and e.isSameNode(doc.getElementById("w")) 1534 and a1.isId 1535 and a2.isId) 1536 # replace the a1 node; the new node should *not* be an ID 1537 a3 = doc.createAttributeNS(NS1, "a1") 1538 a3.value = "v" 1539 e.setAttributeNode(a3) 1540 self.confirm(e.isSameNode(doc.getElementById("w"))) 1541 self.confirm(not a1.isId) 1542 self.confirm(a2.isId) 1543 self.confirm(not a3.isId) 1544 self.confirm(doc.getElementById("v") is None) 1545 # renaming an attribute should not affect its ID-ness: 1546 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1547 self.confirm(e.isSameNode(doc.getElementById("w")) 1548 and a2.isId) 1549 1550 def assert_recursive_equal(self, doc, doc2): 1551 stack = [(doc, doc2)] 1552 while stack: 1553 n1, n2 = stack.pop() 1554 self.assertEqual(n1.nodeType, n2.nodeType) 1555 self.assertEqual(len(n1.childNodes), len(n2.childNodes)) 1556 self.assertEqual(n1.nodeName, n2.nodeName) 1557 self.assertFalse(n1.isSameNode(n2)) 1558 self.assertFalse(n2.isSameNode(n1)) 1559 if n1.nodeType == Node.DOCUMENT_TYPE_NODE: 1560 len(n1.entities) 1561 len(n2.entities) 1562 len(n1.notations) 1563 len(n2.notations) 1564 self.assertEqual(len(n1.entities), len(n2.entities)) 1565 self.assertEqual(len(n1.notations), len(n2.notations)) 1566 for i in range(len(n1.notations)): 1567 # XXX this loop body doesn't seem to be executed? 1568 no1 = n1.notations.item(i) 1569 no2 = n1.notations.item(i) 1570 self.assertEqual(no1.name, no2.name) 1571 self.assertEqual(no1.publicId, no2.publicId) 1572 self.assertEqual(no1.systemId, no2.systemId) 1573 stack.append((no1, no2)) 1574 for i in range(len(n1.entities)): 1575 e1 = n1.entities.item(i) 1576 e2 = n2.entities.item(i) 1577 self.assertEqual(e1.notationName, e2.notationName) 1578 self.assertEqual(e1.publicId, e2.publicId) 1579 self.assertEqual(e1.systemId, e2.systemId) 1580 stack.append((e1, e2)) 1581 if n1.nodeType != Node.DOCUMENT_NODE: 1582 self.assertTrue(n1.ownerDocument.isSameNode(doc)) 1583 self.assertTrue(n2.ownerDocument.isSameNode(doc2)) 1584 for i in range(len(n1.childNodes)): 1585 stack.append((n1.childNodes[i], n2.childNodes[i])) 1586 1587 def testPickledDocument(self): 1588 doc = parseString(sample) 1589 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): 1590 s = pickle.dumps(doc, proto) 1591 doc2 = pickle.loads(s) 1592 self.assert_recursive_equal(doc, doc2) 1593 1594 def testDeepcopiedDocument(self): 1595 doc = parseString(sample) 1596 doc2 = copy.deepcopy(doc) 1597 self.assert_recursive_equal(doc, doc2) 1598 1599 def testSerializeCommentNodeWithDoubleHyphen(self): 1600 doc = create_doc_without_doctype() 1601 doc.appendChild(doc.createComment("foo--bar")) 1602 self.assertRaises(ValueError, doc.toxml) 1603 1604 1605 def testEmptyXMLNSValue(self): 1606 doc = parseString("<element xmlns=''>\n" 1607 "<foo/>\n</element>") 1608 doc2 = parseString(doc.toxml()) 1609 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) 1610 1611 def testExceptionOnSpacesInXMLNSValue(self): 1612 with self.assertRaisesRegex(ValueError, 'Unsupported syntax'): 1613 parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>') 1614 1615 def testDocRemoveChild(self): 1616 doc = parse(tstfile) 1617 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0] 1618 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag) 1619 num_children_before = len(doc.childNodes) 1620 doc.removeChild(doc.childNodes[0]) 1621 num_children_after = len(doc.childNodes) 1622 self.assertTrue(num_children_after == num_children_before - 1) 1623 1624 def testProcessingInstructionNameError(self): 1625 # wrong variable in .nodeValue property will 1626 # lead to "NameError: name 'data' is not defined" 1627 doc = parse(tstfile) 1628 pi = doc.createProcessingInstruction("y", "z") 1629 pi.nodeValue = "crash" 1630 1631 def test_minidom_attribute_order(self): 1632 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1633 doc = parseString(xml_str) 1634 output = io.StringIO() 1635 doc.writexml(output) 1636 self.assertEqual(output.getvalue(), xml_str) 1637 1638 def test_toxml_with_attributes_ordered(self): 1639 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1640 doc = parseString(xml_str) 1641 self.assertEqual(doc.toxml(), xml_str) 1642 1643 def test_toprettyxml_with_attributes_ordered(self): 1644 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' 1645 doc = parseString(xml_str) 1646 self.assertEqual(doc.toprettyxml(), 1647 '<?xml version="1.0" ?>\n' 1648 '<curriculum status="public" company="example"/>\n') 1649 1650 def test_toprettyxml_with_cdata(self): 1651 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' 1652 doc = parseString(xml_str) 1653 self.assertEqual(doc.toprettyxml(), 1654 '<?xml version="1.0" ?>\n' 1655 '<root>\n' 1656 '\t<node><![CDATA[</data>]]></node>\n' 1657 '</root>\n') 1658 1659 def test_cdata_parsing(self): 1660 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' 1661 dom1 = parseString(xml_str) 1662 self.checkWholeText(dom1.getElementsByTagName('node')[0].firstChild, '</data>') 1663 dom2 = parseString(dom1.toprettyxml()) 1664 self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '</data>') 1665 1666if __name__ == "__main__": 1667 unittest.main() 1668