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