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