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