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