| # test for xml.dom.minidom | |
| import copy | |
| import pickle | |
| import io | |
| from test import support | |
| import unittest | |
| import xml.dom.minidom | |
| from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString | |
| from xml.dom.minidom import getDOMImplementation | |
| from xml.parsers.expat import ExpatError | |
| tstfile = support.findfile("test.xml", subdir="xmltestdata") | |
| sample = ("<?xml version='1.0' encoding='us-ascii'?>\n" | |
| "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'" | |
| " 'http://xml.python.org/system' [\n" | |
| " <!ELEMENT e EMPTY>\n" | |
| " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n" | |
| "]><doc attr='value'> text\n" | |
| "<?pi sample?> <!-- comment --> <e/> </doc>") | |
| # The tests of DocumentType importing use these helpers to construct | |
| # the documents to work with, since not all DOM builders actually | |
| # create the DocumentType nodes. | |
| def create_doc_without_doctype(doctype=None): | |
| return getDOMImplementation().createDocument(None, "doc", doctype) | |
| def create_nonempty_doctype(): | |
| doctype = getDOMImplementation().createDocumentType("doc", None, None) | |
| doctype.entities._seq = [] | |
| doctype.notations._seq = [] | |
| notation = xml.dom.minidom.Notation("my-notation", None, | |
| "http://xml.python.org/notations/my") | |
| doctype.notations._seq.append(notation) | |
| entity = xml.dom.minidom.Entity("my-entity", None, | |
| "http://xml.python.org/entities/my", | |
| "my-notation") | |
| entity.version = "1.0" | |
| entity.encoding = "utf-8" | |
| entity.actualEncoding = "us-ascii" | |
| doctype.entities._seq.append(entity) | |
| return doctype | |
| def create_doc_with_doctype(): | |
| doctype = create_nonempty_doctype() | |
| doc = create_doc_without_doctype(doctype) | |
| doctype.entities.item(0).ownerDocument = doc | |
| doctype.notations.item(0).ownerDocument = doc | |
| return doc | |
| class MinidomTest(unittest.TestCase): | |
| def confirm(self, test, testname = "Test"): | |
| self.assertTrue(test, testname) | |
| def checkWholeText(self, node, s): | |
| t = node.wholeText | |
| self.assertEqual(t, s, "looking for %r, found %r" % (s, t)) | |
| def testDocumentAsyncAttr(self): | |
| doc = Document() | |
| self.assertFalse(doc.async_) | |
| self.assertFalse(Document.async_) | |
| def testParseFromBinaryFile(self): | |
| with open(tstfile, 'rb') as file: | |
| dom = parse(file) | |
| dom.unlink() | |
| self.assertIsInstance(dom, Document) | |
| def testParseFromTextFile(self): | |
| with open(tstfile, 'r', encoding='iso-8859-1') as file: | |
| dom = parse(file) | |
| dom.unlink() | |
| self.assertIsInstance(dom, Document) | |
| def testAttrModeSetsParamsAsAttrs(self): | |
| attr = Attr("qName", "namespaceURI", "localName", "prefix") | |
| self.assertEqual(attr.name, "qName") | |
| self.assertEqual(attr.namespaceURI, "namespaceURI") | |
| self.assertEqual(attr.prefix, "prefix") | |
| self.assertEqual(attr.localName, "localName") | |
| def testAttrModeSetsNonOptionalAttrs(self): | |
| attr = Attr("qName", "namespaceURI", None, "prefix") | |
| self.assertEqual(attr.name, "qName") | |
| self.assertEqual(attr.namespaceURI, "namespaceURI") | |
| self.assertEqual(attr.prefix, "prefix") | |
| self.assertEqual(attr.localName, attr.name) | |
| def testGetElementsByTagName(self): | |
| dom = parse(tstfile) | |
| self.assertEqual(dom.getElementsByTagName("LI"), | |
| dom.documentElement.getElementsByTagName("LI")) | |
| dom.unlink() | |
| def testInsertBefore(self): | |
| dom = parseString("<doc><foo/></doc>") | |
| root = dom.documentElement | |
| elem = root.childNodes[0] | |
| nelem = dom.createElement("element") | |
| root.insertBefore(nelem, elem) | |
| self.assertEqual(len(root.childNodes), 2) | |
| self.assertEqual(root.childNodes.length, 2) | |
| self.assertIs(root.childNodes[0], nelem) | |
| self.assertIs(root.childNodes.item(0), nelem) | |
| self.assertIs(root.childNodes[1], elem) | |
| self.assertIs(root.childNodes.item(1), elem) | |
| self.assertIs(root.firstChild, nelem) | |
| self.assertIs(root.lastChild, elem) | |
| self.assertEqual(root.toxml(), "<doc><element/><foo/></doc>") | |
| nelem = dom.createElement("element") | |
| root.insertBefore(nelem, None) | |
| self.assertEqual(len(root.childNodes), 3) | |
| self.assertEqual(root.childNodes.length, 3) | |
| self.assertIs(root.childNodes[1], elem) | |
| self.assertIs(root.childNodes.item(1), elem) | |
| self.assertIs(root.childNodes[2], nelem) | |
| self.assertIs(root.childNodes.item(2), nelem) | |
| self.assertIs(root.lastChild, nelem) | |
| self.assertIs(nelem.previousSibling, elem) | |
| self.assertEqual(root.toxml(), "<doc><element/><foo/><element/></doc>") | |
| nelem2 = dom.createElement("bar") | |
| root.insertBefore(nelem2, nelem) | |
| self.assertEqual(len(root.childNodes), 4) | |
| self.assertEqual(root.childNodes.length, 4) | |
| self.assertIs(root.childNodes[2], nelem2) | |
| self.assertIs(root.childNodes.item(2), nelem2) | |
| self.assertIs(root.childNodes[3], nelem) | |
| self.assertIs(root.childNodes.item(3), nelem) | |
| self.assertIs(nelem2.nextSibling, nelem) | |
| self.assertIs(nelem.previousSibling, nelem2) | |
| self.assertEqual(root.toxml(), | |
| "<doc><element/><foo/><bar/><element/></doc>") | |
| dom.unlink() | |
| def _create_fragment_test_nodes(self): | |
| dom = parseString("<doc/>") | |
| orig = dom.createTextNode("original") | |
| c1 = dom.createTextNode("foo") | |
| c2 = dom.createTextNode("bar") | |
| c3 = dom.createTextNode("bat") | |
| dom.documentElement.appendChild(orig) | |
| frag = dom.createDocumentFragment() | |
| frag.appendChild(c1) | |
| frag.appendChild(c2) | |
| frag.appendChild(c3) | |
| return dom, orig, c1, c2, c3, frag | |
| def testInsertBeforeFragment(self): | |
| dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() | |
| dom.documentElement.insertBefore(frag, None) | |
| self.assertTupleEqual(tuple(dom.documentElement.childNodes), | |
| (orig, c1, c2, c3), | |
| "insertBefore(<fragment>, None)") | |
| frag.unlink() | |
| dom.unlink() | |
| dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() | |
| dom.documentElement.insertBefore(frag, orig) | |
| self.assertTupleEqual(tuple(dom.documentElement.childNodes), | |
| (c1, c2, c3, orig), | |
| "insertBefore(<fragment>, orig)") | |
| frag.unlink() | |
| dom.unlink() | |
| def testAppendChild(self): | |
| dom = parse(tstfile) | |
| dom.documentElement.appendChild(dom.createComment("Hello")) | |
| self.assertEqual(dom.documentElement.childNodes[-1].nodeName, "#comment") | |
| self.assertEqual(dom.documentElement.childNodes[-1].data, "Hello") | |
| dom.unlink() | |
| @support.requires_resource('cpu') | |
| def testAppendChildNoQuadraticComplexity(self): | |
| impl = getDOMImplementation() | |
| def work(n): | |
| doc = impl.createDocument(None, "some_tag", None) | |
| element = doc.documentElement | |
| total_calls = 0 | |
| # Count attribute accesses as a proxy for work done | |
| def getattribute_counter(self, attr): | |
| nonlocal total_calls | |
| total_calls += 1 | |
| return object.__getattribute__(self, attr) | |
| with support.swap_attr(Element, "__getattribute__", getattribute_counter): | |
| for _ in range(n): | |
| child = doc.createElement("child") | |
| element.appendChild(child) | |
| element = child | |
| return total_calls | |
| # Doubling N should not ~quadruple the work. | |
| w1 = work(1024) | |
| w2 = work(2048) | |
| w3 = work(4096) | |
| self.assertGreater(w1, 0) | |
| r1 = w2 / w1 | |
| r2 = w3 / w2 | |
| self.assertLess( | |
| max(r1, r2), 3.2, | |
| msg=f"Possible quadratic behavior: work={w1,w2,w3} ratios={r1,r2}" | |
| ) | |
| def testSetAttributeNodeWithoutOwnerDocument(self): | |
| # regression test for gh-142754 | |
| elem = Element("test") | |
| attr = Attr("id") | |
| attr.value = "test-id" | |
| elem.setAttributeNode(attr) | |
| self.assertEqual(elem.getAttribute("id"), "test-id") | |
| def testAppendChildFragment(self): | |
| dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() | |
| dom.documentElement.appendChild(frag) | |
| self.assertTupleEqual(tuple(dom.documentElement.childNodes), | |
| (orig, c1, c2, c3), | |
| "appendChild(<fragment>)") | |
| frag.unlink() | |
| dom.unlink() | |
| def testReplaceChildFragment(self): | |
| dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() | |
| dom.documentElement.replaceChild(frag, orig) | |
| orig.unlink() | |
| self.assertTupleEqual(tuple(dom.documentElement.childNodes), (c1, c2, c3), | |
| "replaceChild(<fragment>)") | |
| frag.unlink() | |
| dom.unlink() | |
| def testLegalChildren(self): | |
| dom = Document() | |
| elem = dom.createElement('element') | |
| text = dom.createTextNode('text') | |
| self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text) | |
| dom.appendChild(elem) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text, | |
| elem) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text, | |
| elem) | |
| nodemap = elem.attributes | |
| self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem, | |
| text) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS, | |
| text) | |
| elem.appendChild(text) | |
| dom.unlink() | |
| def testNamedNodeMapSetItem(self): | |
| dom = Document() | |
| elem = dom.createElement('element') | |
| attrs = elem.attributes | |
| attrs["foo"] = "bar" | |
| a = attrs.item(0) | |
| self.assertIs(a.ownerDocument, dom, | |
| "NamedNodeMap.__setitem__() sets ownerDocument") | |
| self.assertIs(a.ownerElement, elem, | |
| "NamedNodeMap.__setitem__() sets ownerElement") | |
| self.assertEqual(a.value, "bar", | |
| "NamedNodeMap.__setitem__() sets value") | |
| self.assertEqual(a.nodeValue, "bar", | |
| "NamedNodeMap.__setitem__() sets nodeValue") | |
| elem.unlink() | |
| dom.unlink() | |
| def testNonZero(self): | |
| dom = parse(tstfile) | |
| self.assertTrue(dom) # should not be zero | |
| dom.appendChild(dom.createComment("foo")) | |
| self.assertFalse(dom.childNodes[-1].childNodes) | |
| dom.unlink() | |
| def testUnlink(self): | |
| dom = parse(tstfile) | |
| self.assertTrue(dom.childNodes) | |
| dom.unlink() | |
| self.assertFalse(dom.childNodes) | |
| def testContext(self): | |
| with parse(tstfile) as dom: | |
| self.assertTrue(dom.childNodes) | |
| self.assertFalse(dom.childNodes) | |
| def testElement(self): | |
| dom = Document() | |
| dom.appendChild(dom.createElement("abc")) | |
| self.assertTrue(dom.documentElement) | |
| dom.unlink() | |
| def testAAA(self): | |
| dom = parseString("<abc/>") | |
| el = dom.documentElement | |
| el.setAttribute("spam", "jam2") | |
| self.assertEqual(el.toxml(), '<abc spam="jam2"/>', "testAAA") | |
| a = el.getAttributeNode("spam") | |
| self.assertIs(a.ownerDocument, dom, | |
| "setAttribute() sets ownerDocument") | |
| self.assertIs(a.ownerElement, dom.documentElement, | |
| "setAttribute() sets ownerElement") | |
| dom.unlink() | |
| def testAAB(self): | |
| dom = parseString("<abc/>") | |
| el = dom.documentElement | |
| el.setAttribute("spam", "jam") | |
| el.setAttribute("spam", "jam2") | |
| self.assertEqual(el.toxml(), '<abc spam="jam2"/>', "testAAB") | |
| dom.unlink() | |
| def testAddAttr(self): | |
| dom = Document() | |
| child = dom.appendChild(dom.createElement("abc")) | |
| child.setAttribute("def", "ghi") | |
| self.assertEqual(child.getAttribute("def"), "ghi") | |
| self.assertEqual(child.attributes["def"].value, "ghi") | |
| child.setAttribute("jkl", "mno") | |
| self.assertEqual(child.getAttribute("jkl"), "mno") | |
| self.assertEqual(child.attributes["jkl"].value, "mno") | |
| self.assertEqual(len(child.attributes), 2) | |
| child.setAttribute("def", "newval") | |
| self.assertEqual(child.getAttribute("def"), "newval") | |
| self.assertEqual(child.attributes["def"].value, "newval") | |
| self.assertEqual(len(child.attributes), 2) | |
| dom.unlink() | |
| def testDeleteAttr(self): | |
| dom = Document() | |
| child = dom.appendChild(dom.createElement("abc")) | |
| self.assertEqual(len(child.attributes), 0) | |
| child.setAttribute("def", "ghi") | |
| self.assertEqual(len(child.attributes), 1) | |
| del child.attributes["def"] | |
| self.assertEqual(len(child.attributes), 0) | |
| dom.unlink() | |
| def testRemoveAttr(self): | |
| dom = Document() | |
| child = dom.appendChild(dom.createElement("abc")) | |
| child.setAttribute("def", "ghi") | |
| self.assertEqual(len(child.attributes), 1) | |
| # removing an absent attribute has no effect | |
| child.removeAttribute("foo") | |
| self.assertEqual(len(child.attributes), 1) | |
| child.removeAttribute("def") | |
| self.assertEqual(len(child.attributes), 0) | |
| dom.unlink() | |
| def testRemoveAttrNS(self): | |
| dom = Document() | |
| child = dom.appendChild( | |
| dom.createElementNS("http://www.python.org", "python:abc")) | |
| child.setAttributeNS(xml.dom.XMLNS_NAMESPACE, "xmlns:python", | |
| "http://www.python.org") | |
| child.setAttributeNS("http://www.python.org", "python:abcattr", "foo") | |
| # removing an absent attribute has no effect | |
| child.removeAttributeNS("foo", "http://www.python.org") | |
| self.assertEqual(len(child.attributes), 2) | |
| child.removeAttributeNS("http://www.python.org", "abcattr") | |
| self.assertEqual(len(child.attributes), 1) | |
| dom.unlink() | |
| def testRemoveAttributeNode(self): | |
| dom = Document() | |
| child = dom.appendChild(dom.createElement("foo")) | |
| child.setAttribute("spam", "jam") | |
| self.assertEqual(len(child.attributes), 1) | |
| node = child.getAttributeNode("spam") | |
| self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNode, | |
| None) | |
| self.assertIs(node, child.removeAttributeNode(node)) | |
| self.assertEqual(len(child.attributes), 0) | |
| self.assertIsNone(child.getAttributeNode("spam")) | |
| dom2 = Document() | |
| child2 = dom2.appendChild(dom2.createElement("foo")) | |
| node2 = child2.getAttributeNode("spam") | |
| self.assertRaises(xml.dom.NotFoundErr, child2.removeAttributeNode, | |
| node2) | |
| dom.unlink() | |
| def testHasAttribute(self): | |
| dom = Document() | |
| child = dom.appendChild(dom.createElement("foo")) | |
| child.setAttribute("spam", "jam") | |
| self.assertTrue(child.hasAttribute("spam")) | |
| def testChangeAttr(self): | |
| dom = parseString("<abc/>") | |
| el = dom.documentElement | |
| el.setAttribute("spam", "jam") | |
| self.assertEqual(len(el.attributes), 1) | |
| el.setAttribute("spam", "bam") | |
| # Set this attribute to be an ID and make sure that doesn't change | |
| # when changing the value: | |
| el.setIdAttribute("spam") | |
| self.assertEqual(len(el.attributes), 1) | |
| self.assertEqual(el.attributes["spam"].value, "bam") | |
| self.assertEqual(el.attributes["spam"].nodeValue, "bam") | |
| self.assertEqual(el.getAttribute("spam"), "bam") | |
| self.assertTrue(el.getAttributeNode("spam").isId) | |
| el.attributes["spam"] = "ham" | |
| self.assertEqual(len(el.attributes), 1) | |
| self.assertEqual(el.attributes["spam"].value, "ham") | |
| self.assertEqual(el.attributes["spam"].nodeValue, "ham") | |
| self.assertEqual(el.getAttribute("spam"), "ham") | |
| self.assertTrue(el.attributes["spam"].isId) | |
| el.setAttribute("spam2", "bam") | |
| self.assertEqual(len(el.attributes), 2) | |
| self.assertEqual(el.attributes["spam"].value, "ham") | |
| self.assertEqual(el.attributes["spam"].nodeValue, "ham") | |
| self.assertEqual(el.getAttribute("spam"), "ham") | |
| self.assertEqual(el.attributes["spam2"].value, "bam") | |
| self.assertEqual(el.attributes["spam2"].nodeValue, "bam") | |
| self.assertEqual(el.getAttribute("spam2"), "bam") | |
| el.attributes["spam2"] = "bam2" | |
| self.assertEqual(len(el.attributes), 2) | |
| self.assertEqual(el.attributes["spam"].value, "ham") | |
| self.assertEqual(el.attributes["spam"].nodeValue, "ham") | |
| self.assertEqual(el.getAttribute("spam"), "ham") | |
| self.assertEqual(el.attributes["spam2"].value, "bam2") | |
| self.assertEqual(el.attributes["spam2"].nodeValue, "bam2") | |
| self.assertEqual(el.getAttribute("spam2"), "bam2") | |
| dom.unlink() | |
| def testGetAttrList(self): | |
| dom = parseString("<abc/>") | |
| self.addCleanup(dom.unlink) | |
| el = dom.documentElement | |
| el.setAttribute("spam", "jam") | |
| self.assertEqual(len(el.attributes.items()), 1) | |
| el.setAttribute("foo", "bar") | |
| items = el.attributes.items() | |
| self.assertEqual(len(items), 2) | |
| self.assertIn(('spam', 'jam'), items) | |
| self.assertIn(('foo', 'bar'), items) | |
| def testGetAttrValues(self): | |
| dom = parseString("<abc/>") | |
| self.addCleanup(dom.unlink) | |
| el = dom.documentElement | |
| el.setAttribute("spam", "jam") | |
| values = [x.value for x in el.attributes.values()] | |
| self.assertIn("jam", values) | |
| el.setAttribute("foo", "bar") | |
| values = [x.value for x in el.attributes.values()] | |
| self.assertIn("bar", values) | |
| self.assertIn("jam", values) | |
| def testGetAttribute(self): | |
| dom = Document() | |
| child = dom.appendChild( | |
| dom.createElementNS("http://www.python.org", "python:abc")) | |
| self.assertEqual(child.getAttribute('missing'), '') | |
| def testGetAttributeNS(self): | |
| dom = Document() | |
| child = dom.appendChild( | |
| dom.createElementNS("http://www.python.org", "python:abc")) | |
| child.setAttributeNS(xml.dom.XMLNS_NAMESPACE, "xmlns:python", | |
| "http://www.python.org") | |
| self.assertEqual( | |
| child.getAttributeNS(xml.dom.XMLNS_NAMESPACE, "python"), | |
| 'http://www.python.org') | |
| self.assertEqual( | |
| child.getAttributeNS(xml.dom.XMLNS_NAMESPACE, "other"), | |
| '') | |
| child2 = child.appendChild(dom.createElement('abc')) | |
| self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"), | |
| '') | |
| def testGetAttributeNode(self): | |
| dom = parseString("<doc a='1'/>") | |
| elem = dom.documentElement | |
| attr = elem.getAttributeNode("a") | |
| self.assertEqual(attr.name, "a") | |
| self.assertEqual(attr.value, "1") | |
| self.assertIs(attr.ownerElement, elem) | |
| self.assertIsNone(elem.getAttributeNode("b")) | |
| dom.unlink() | |
| def testGetElementsByTagNameNS(self): | |
| d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'> | |
| <minidom:myelem/> | |
| </foo>""" | |
| dom = parseString(d) | |
| elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom", | |
| "myelem") | |
| self.assertEqual(len(elems), 1) | |
| self.assertEqual(elems[0].namespaceURI, "http://pyxml.sf.net/minidom") | |
| self.assertEqual(elems[0].localName, "myelem") | |
| self.assertEqual(elems[0].prefix, "minidom") | |
| self.assertEqual(elems[0].tagName, "minidom:myelem") | |
| self.assertEqual(elems[0].nodeName, "minidom:myelem") | |
| dom.unlink() | |
| def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri, | |
| lname): | |
| nodelist = doc.getElementsByTagNameNS(nsuri, lname) | |
| self.assertEqual(len(nodelist), 0) | |
| def testGetEmptyNodeListFromElementsByTagNameNS(self): | |
| doc = parseString('<doc/>') | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, 'http://xml.python.org/namespaces/a', 'localname') | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, '*', 'splat') | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, 'http://xml.python.org/namespaces/a', '*') | |
| doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>') | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, "http://xml.python.org/splat", "not-there") | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, "*", "not-there") | |
| self.get_empty_nodelist_from_elements_by_tagName_ns_helper( | |
| doc, "http://somewhere.else.net/not-there", "e") | |
| def testElementReprAndStr(self): | |
| dom = Document() | |
| el = dom.appendChild(dom.createElement("abc")) | |
| string1 = repr(el) | |
| string2 = str(el) | |
| self.assertEqual(string1, string2) | |
| dom.unlink() | |
| def testElementReprAndStrUnicode(self): | |
| dom = Document() | |
| el = dom.appendChild(dom.createElement("abc")) | |
| string1 = repr(el) | |
| string2 = str(el) | |
| self.assertEqual(string1, string2) | |
| dom.unlink() | |
| def testElementReprAndStrUnicodeNS(self): | |
| dom = Document() | |
| el = dom.appendChild( | |
| dom.createElementNS("http://www.slashdot.org", "slash:abc")) | |
| string1 = repr(el) | |
| string2 = str(el) | |
| self.assertEqual(string1, string2) | |
| self.assertIn("slash:abc", string1) | |
| dom.unlink() | |
| def testAttributeRepr(self): | |
| dom = Document() | |
| el = dom.appendChild(dom.createElement("abc")) | |
| node = el.setAttribute("abc", "def") | |
| self.assertEqual(str(node), repr(node)) | |
| dom.unlink() | |
| def testWriteXML(self): | |
| str = '<?xml version="1.0" ?><a b="c"/>' | |
| dom = parseString(str) | |
| domstr = dom.toxml() | |
| dom.unlink() | |
| self.assertEqual(str, domstr) | |
| def testWriteXMLDocumentFragment(self): | |
| dom = parseString('<doc><a b="c"/>text<!--comment--></doc>') | |
| frag = dom.createDocumentFragment() | |
| for node in list(dom.documentElement.childNodes): | |
| frag.appendChild(node) | |
| self.assertEqual(frag.toxml(), '<a b="c"/>text<!--comment-->') | |
| self.assertEqual(frag.toprettyxml(), | |
| '<a b="c"/>\ntext\n<!--comment-->\n') | |
| # the fragment itself does not add a level of indentation | |
| writer = io.StringIO() | |
| frag.writexml(writer, " ", " ", "\n") | |
| self.assertEqual(writer.getvalue(), | |
| ' <a b="c"/>\n text\n <!--comment-->\n') | |
| self.assertEqual(dom.createDocumentFragment().toxml(), '') | |
| def testWriteXMLNamespaceDeclarations(self): | |
| dom = Document() | |
| root = dom.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "p:root")) | |
| child = root.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "p:child")) | |
| child.setAttributeNS("http://xml.python.org/ns2", "q:attr", "value") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<p:root xmlns:p="http://xml.python.org/ns">' | |
| '<p:child xmlns:q="http://xml.python.org/ns2" ' | |
| 'q:attr="value"/></p:root>') | |
| dom.unlink() | |
| def testWriteXMLDefaultNamespace(self): | |
| dom = Document() | |
| root = dom.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "root")) | |
| root.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "child")) | |
| # An element in no namespace undeclares the default namespace. | |
| root.appendChild(dom.createElement("nons")) | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<root xmlns="http://xml.python.org/ns">' | |
| '<child/><nons xmlns=""/></root>') | |
| dom.unlink() | |
| def testWriteXMLAttributeNamespacePrefix(self): | |
| dom = Document() | |
| root = dom.appendChild(dom.createElement("root")) | |
| # Attributes cannot use the default namespace, a prefix is invented. | |
| root.setAttributeNS("http://xml.python.org/ns", "attr", "value") | |
| root.setAttributeNS("http://xml.python.org/ns2", "attr2", "value2") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<root xmlns:ns0="http://xml.python.org/ns" ' | |
| 'xmlns:ns1="http://xml.python.org/ns2" ' | |
| 'ns0:attr="value" ns1:attr2="value2"/>') | |
| # The same namespace gets the same prefix. | |
| root.setAttributeNS("http://xml.python.org/ns", "attr3", "value3") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<root xmlns:ns0="http://xml.python.org/ns" ' | |
| 'xmlns:ns1="http://xml.python.org/ns2" ' | |
| 'ns0:attr="value" ns1:attr2="value2" ns0:attr3="value3"/>') | |
| dom.unlink() | |
| def testWriteXMLAttributeNamespacePrefixReused(self): | |
| # A prefix already bound to the namespace of the attribute is used. | |
| dom = Document() | |
| root = dom.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "p:root")) | |
| root.setAttributeNS("http://xml.python.org/ns", "attr", "value") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<p:root xmlns:p="http://xml.python.org/ns" p:attr="value"/>') | |
| # The prefix can be bound for an ancestor. | |
| child = root.appendChild(dom.createElement("child")) | |
| child.setAttributeNS("http://xml.python.org/ns", "attr", "value") | |
| self.assertEqual(child.toxml(), '<child p:attr="value"/>') | |
| # The prefix bound for a preceding attribute is reused. | |
| root.setAttributeNS("http://xml.python.org/ns3", "q:attr3", "value3") | |
| root.setAttributeNS("http://xml.python.org/ns3", "attr4", "value4") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<p:root xmlns:p="http://xml.python.org/ns" ' | |
| 'xmlns:q="http://xml.python.org/ns3" ' | |
| 'p:attr="value" q:attr3="value3" q:attr4="value4">' | |
| '<child p:attr="value"/></p:root>') | |
| root.removeAttributeNS("http://xml.python.org/ns3", "attr3") | |
| root.removeAttributeNS("http://xml.python.org/ns3", "attr4") | |
| # The prefix must not be taken by an explicit declaration. | |
| root.setAttributeNS(xml.dom.XMLNS_NAMESPACE, "xmlns:ns0", "other") | |
| root.setAttributeNS("http://xml.python.org/ns2", "attr2", "value2") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<p:root xmlns:p="http://xml.python.org/ns" ' | |
| 'xmlns:ns1="http://xml.python.org/ns2" ' | |
| 'p:attr="value" xmlns:ns0="other" ns1:attr2="value2">' | |
| '<child p:attr="value"/></p:root>') | |
| dom.unlink() | |
| def testWriteXMLXMLPrefix(self): | |
| dom = Document() | |
| root = dom.appendChild(dom.createElement("root")) | |
| # The "xml" prefix is bound by definition and is never declared. | |
| root.setAttributeNS(xml.dom.XML_NAMESPACE, "xml:lang", "en") | |
| self.assertEqual(dom.documentElement.toxml(), '<root xml:lang="en"/>') | |
| dom.unlink() | |
| def testWriteXMLExistingNamespaceDeclarations(self): | |
| for str in [ | |
| '<p:root xmlns:p="http://xml.python.org/ns"><p:child/></p:root>', | |
| '<root xmlns="http://xml.python.org/ns"><child xmlns=""/></root>', | |
| '<p:root xmlns:p="http://xml.python.org/ns">' | |
| '<p:child xmlns:p="http://xml.python.org/ns2"/></p:root>', | |
| '<root xmlns:p="http://xml.python.org/ns" p:attr="value"/>', | |
| ]: | |
| with self.subTest(str=str): | |
| dom = parseString(str) | |
| self.assertEqual(dom.documentElement.toxml(), str) | |
| dom.unlink() | |
| def testWriteXMLNotANamespaceDeclaration(self): | |
| # an attribute whose name only starts with "xmlns" is not one | |
| dom = parseString('<root xmlns="http://xml.python.org/ns">' | |
| '<child xmlnsabc="v"><g/></child></root>') | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<root xmlns="http://xml.python.org/ns">' | |
| '<child xmlnsabc="v"><g/></child></root>') | |
| dom.unlink() | |
| dom = Document() | |
| root = dom.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "root")) | |
| child = root.appendChild(dom.createElement("child")) | |
| child.setAttribute("xmlnsabc", "v") | |
| self.assertEqual(dom.documentElement.toxml(), | |
| '<root xmlns="http://xml.python.org/ns">' | |
| '<child xmlns="" xmlnsabc="v"/></root>') | |
| dom.unlink() | |
| def testWriteXMLDoesNotModifyDocument(self): | |
| dom = Document() | |
| root = dom.appendChild( | |
| dom.createElementNS("http://xml.python.org/ns", "p:root")) | |
| root.toxml() | |
| self.assertEqual(root.attributes.length, 0) | |
| dom.unlink() | |
| def test_toxml_quote_text(self): | |
| dom = Document() | |
| elem = dom.appendChild(dom.createElement('elem')) | |
| elem.appendChild(dom.createTextNode('&<>"')) | |
| cr = elem.appendChild(dom.createElement('cr')) | |
| cr.appendChild(dom.createTextNode('\r')) | |
| crlf = elem.appendChild(dom.createElement('crlf')) | |
| crlf.appendChild(dom.createTextNode('\r\n')) | |
| lflf = elem.appendChild(dom.createElement('lflf')) | |
| lflf.appendChild(dom.createTextNode('\n\n')) | |
| ws = elem.appendChild(dom.createElement('ws')) | |
| ws.appendChild(dom.createTextNode('\t\n\r ')) | |
| domstr = dom.toxml() | |
| dom.unlink() | |
| self.assertEqual(domstr, '<?xml version="1.0" ?>' | |
| '<elem>&<>"' | |
| '<cr>\r</cr>' | |
| '<crlf>\r\n</crlf>' | |
| '<lflf>\n\n</lflf>' | |
| '<ws>\t\n\r </ws></elem>') | |
| def test_toxml_quote_attrib(self): | |
| dom = Document() | |
| elem = dom.appendChild(dom.createElement('elem')) | |
| elem.setAttribute("a", '&<>"') | |
| elem.setAttribute("cr", "\r") | |
| elem.setAttribute("lf", "\n") | |
| elem.setAttribute("crlf", "\r\n") | |
| elem.setAttribute("lflf", "\n\n") | |
| elem.setAttribute("ws", "\t\n\r ") | |
| domstr = dom.toxml() | |
| dom.unlink() | |
| self.assertEqual(domstr, '<?xml version="1.0" ?>' | |
| '<elem a="&<>"" ' | |
| 'cr=" " ' | |
| 'lf=" " ' | |
| 'crlf=" " ' | |
| 'lflf=" " ' | |
| 'ws="	 "/>') | |
| def testAltNewline(self): | |
| str = '<?xml version="1.0" ?>\n<a b="c"/>\n' | |
| dom = parseString(str) | |
| domstr = dom.toprettyxml(newl="\r\n") | |
| dom.unlink() | |
| self.assertEqual(domstr, str.replace("\n", "\r\n")) | |
| def test_toprettyxml_with_text_nodes(self): | |
| # see gh-48397 and gh-81623, | |
| # the content of an element with text is not changed | |
| decl = '<?xml version="1.0" ?>\n' | |
| self.assertEqual(parseString('<B>A</B>').toprettyxml(), | |
| decl + '<B>A</B>\n') | |
| self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(), | |
| decl + '<C>A<B>A</B></C>\n') | |
| self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(), | |
| decl + '<C><B>A</B>A</C>\n') | |
| self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(), | |
| decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n') | |
| self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(), | |
| decl + '<C><B>A</B>A<B>A</B></C>\n') | |
| # toprettyxml treats whitespace between elements as insignificant | |
| self.assertEqual(parseString('<C> <B>A</B> </C>').toprettyxml(), | |
| decl + '<C>\n\t \n\t<B>A</B>\n\t \n</C>\n') | |
| def test_toprettyxml_with_adjacent_text_nodes(self): | |
| # see gh-81623, adjacent text nodes are not separated | |
| dom = Document() | |
| elem = dom.createElement('elem') | |
| elem.appendChild(dom.createTextNode('TEXT')) | |
| elem.appendChild(dom.createTextNode('TEXT')) | |
| dom.appendChild(elem) | |
| decl = '<?xml version="1.0" ?>\n' | |
| self.assertEqual(dom.toprettyxml(), decl + '<elem>TEXTTEXT</elem>\n') | |
| def test_toprettyxml_preserve(self): | |
| decl = '<?xml version="1.0" ?>\n' | |
| # xml:space="preserve" applies to the whole subtree | |
| self.assertEqual( | |
| parseString('<C xml:space="preserve"><B>A</B><B>A</B></C>' | |
| ).toprettyxml(), | |
| decl + '<C xml:space="preserve"><B>A</B><B>A</B></C>\n') | |
| self.assertEqual( | |
| parseString('<C xml:space="preserve"><B><D/></B></C>' | |
| ).toprettyxml(), | |
| decl + '<C xml:space="preserve"><B><D/></B></C>\n') | |
| # other values do not preserve whitespace | |
| self.assertEqual( | |
| parseString('<C xml:space="default"><B>A</B></C>').toprettyxml(), | |
| decl + '<C xml:space="default">\n\t<B>A</B>\n</C>\n') | |
| def test_toprettyxml_with_non_xml_whitespace(self): | |
| # only " \t\r\n" are whitespace in XML (see XML 1.0, 2.3) | |
| decl = '<?xml version="1.0" ?>\n' | |
| self.assertEqual(parseString('<C>\xa0<B>A</B></C>').toprettyxml(), | |
| decl + '<C>\xa0<B>A</B></C>\n') | |
| def test_toprettyxml_with_dtd(self): | |
| decl = '<?xml version="1.0" ?>\n' | |
| # only whitespace in element content is ignorable | |
| doctype = ('<!DOCTYPE C [<!ELEMENT C (#PCDATA|B)*>' | |
| '<!ELEMENT B (#PCDATA)>]>') | |
| self.assertEqual( | |
| parseString(doctype + '<C><B>A</B><B>A</B></C>').toprettyxml(), | |
| decl + doctype + '\n<C><B>A</B><B>A</B></C>\n') | |
| doctype = '<!DOCTYPE C [<!ELEMENT C (B)*><!ELEMENT B (#PCDATA)>]>' | |
| self.assertEqual( | |
| parseString(doctype + '<C><B>A</B><B>A</B></C>').toprettyxml(), | |
| decl + doctype + '\n<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n') | |
| def test_toprettyxml_with_cdata_section(self): | |
| decl = '<?xml version="1.0" ?>\n' | |
| self.assertEqual( | |
| parseString('<C><![CDATA[A]]><B>A</B></C>').toprettyxml(), | |
| decl + '<C><![CDATA[A]]><B>A</B></C>\n') | |
| def test_toprettyxml_preserves_content_of_text_node(self): | |
| # see gh-48397 | |
| for str in ('<B>A</B>', '<A><B>C</B></A>'): | |
| dom = parseString(str) | |
| dom2 = parseString(dom.toprettyxml()) | |
| self.assertEqual( | |
| dom.getElementsByTagName('B')[0].childNodes[0].toxml(), | |
| dom2.getElementsByTagName('B')[0].childNodes[0].toxml()) | |
| def test_isWhitespaceInElementContent(self): | |
| # only " \t\r\n" are whitespace in XML (see XML 1.0, 2.3) | |
| dom = parseString('<!DOCTYPE a [<!ELEMENT a (b)*><!ELEMENT b (#PCDATA)>]>' | |
| '<a> <b>x</b>\xa0</a>') | |
| children = dom.documentElement.childNodes | |
| self.assertTrue(children[0].isWhitespaceInElementContent) | |
| self.assertFalse(children[2].isWhitespaceInElementContent) | |
| dom.unlink() | |
| def test_remove_whitespace_in_element_content(self): | |
| from xml.dom.xmlbuilder import DOMBuilder, DOMInputSource | |
| builder = DOMBuilder() | |
| builder.setFeature("whitespace-in-element-content", False) | |
| source = DOMInputSource() | |
| source.byteStream = io.BytesIO( | |
| b'<!DOCTYPE a [<!ELEMENT a (b)*><!ELEMENT b (#PCDATA)>]>' | |
| b'<a> <b>x</b>\xc2\xa0</a>') | |
| dom = builder.parse(source) | |
| children = dom.documentElement.childNodes | |
| # ignorable whitespace is removed, other characters are not | |
| self.assertEqual([node.nodeName for node in children], ['b', '#text']) | |
| self.assertEqual(children[1].data, '\xa0') | |
| dom.unlink() | |
| def testProcessingInstruction(self): | |
| dom = parseString('<e><?mypi \t\n data \t\n ?></e>') | |
| pi = dom.documentElement.firstChild | |
| self.assertEqual(pi.target, "mypi") | |
| self.assertEqual(pi.data, "data \t\n ") | |
| self.assertEqual(pi.nodeName, "mypi") | |
| self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) | |
| self.assertIsNone(pi.attributes) | |
| self.assertFalse(pi.hasChildNodes()) | |
| self.assertEqual(len(pi.childNodes), 0) | |
| self.assertIsNone(pi.firstChild) | |
| self.assertIsNone(pi.lastChild) | |
| self.assertIsNone(pi.localName) | |
| self.assertEqual(pi.namespaceURI, xml.dom.EMPTY_NAMESPACE) | |
| def testProcessingInstructionRepr(self): | |
| dom = parseString('<e><?mypi \t\n data \t\n ?></e>') | |
| pi = dom.documentElement.firstChild | |
| self.assertEqual(str(pi.nodeType), repr(pi.nodeType)) | |
| def testTextRepr(self): | |
| dom = Document() | |
| self.addCleanup(dom.unlink) | |
| elem = dom.createElement("elem") | |
| elem.appendChild(dom.createTextNode("foo")) | |
| el = elem.firstChild | |
| self.assertEqual(str(el), repr(el)) | |
| self.assertEqual('<DOM Text node "\'foo\'">', str(el)) | |
| def testWriteText(self): | |
| dom = parseString("<doc><a>text</a><b><&></b></doc>") | |
| elem = dom.documentElement | |
| writer = io.StringIO() | |
| elem.writexml(writer) | |
| self.assertEqual(writer.getvalue(), | |
| "<doc><a>text</a><b><&></b></doc>") | |
| writer = io.StringIO() | |
| elem.writexml(writer, indent=" ", addindent=" ", newl="\n") | |
| self.assertEqual(writer.getvalue(), | |
| " <doc>\n" | |
| " <a>text</a>\n" | |
| " <b><&></b>\n" | |
| " </doc>\n") | |
| dom.unlink() | |
| def testDocumentElement(self): | |
| dom = parseString("<!-- comment --><doc/><?pi data?>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.tagName, "doc") | |
| self.assertIs(elem, dom.childNodes[1]) | |
| dom.unlink() | |
| dom = Document() | |
| self.assertIsNone(dom.documentElement) | |
| elem = dom.appendChild(dom.createElement("doc")) | |
| self.assertIs(dom.documentElement, elem) | |
| dom.unlink() | |
| def testTooManyDocumentElements(self): | |
| doc = parseString("<doc/>") | |
| elem = doc.createElement("extra") | |
| # Should raise an exception when adding an extra document element. | |
| self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem) | |
| elem.unlink() | |
| doc.unlink() | |
| def testCreateElementNS(self): | |
| dom = Document() | |
| elem = dom.createElementNS("http://xml.python.org/ns", "p:elem") | |
| self.assertEqual(elem.nodeType, Node.ELEMENT_NODE) | |
| self.assertEqual(elem.tagName, "p:elem") | |
| self.assertEqual(elem.nodeName, "p:elem") | |
| self.assertEqual(elem.namespaceURI, "http://xml.python.org/ns") | |
| self.assertEqual(elem.prefix, "p") | |
| self.assertEqual(elem.localName, "elem") | |
| self.assertIs(elem.ownerDocument, dom) | |
| self.assertIsNone(elem.parentNode) | |
| elem = dom.createElementNS("http://xml.python.org/ns", "elem") | |
| self.assertEqual(elem.tagName, "elem") | |
| self.assertIsNone(elem.prefix) | |
| self.assertEqual(elem.localName, "elem") | |
| dom.unlink() | |
| def testCreateAttributeNS(self): | |
| dom = Document() | |
| attr = dom.createAttributeNS("http://xml.python.org/ns", "p:attr") | |
| self.assertEqual(attr.nodeType, Node.ATTRIBUTE_NODE) | |
| self.assertEqual(attr.name, "p:attr") | |
| self.assertEqual(attr.nodeName, "p:attr") | |
| self.assertEqual(attr.namespaceURI, "http://xml.python.org/ns") | |
| self.assertEqual(attr.prefix, "p") | |
| self.assertEqual(attr.localName, "attr") | |
| self.assertEqual(attr.value, "") | |
| self.assertIs(attr.ownerDocument, dom) | |
| self.assertIsNone(attr.ownerElement) | |
| elem = dom.appendChild(dom.createElement("doc")) | |
| elem.setAttributeNode(attr) | |
| self.assertIs(attr.ownerElement, elem) | |
| self.assertIs(elem.getAttributeNodeNS("http://xml.python.org/ns", | |
| "attr"), attr) | |
| dom.unlink() | |
| def testParse(self): | |
| # parsing from a file object is tested in testParseFromBinaryFile | |
| # and testParseFromTextFile | |
| dom = parse(tstfile) | |
| self.assertEqual(dom.nodeType, Node.DOCUMENT_NODE) | |
| self.assertEqual(dom.documentElement.tagName, "HTML") | |
| dom.unlink() | |
| self.assertRaises(ExpatError, parseString, "<doc>") | |
| def testParseString(self): | |
| dom = parseString("<doc>text</doc>") | |
| self.assertEqual(dom.nodeType, Node.DOCUMENT_NODE) | |
| self.assertEqual(dom.documentElement.tagName, "doc") | |
| self.assertEqual(dom.documentElement.firstChild.data, "text") | |
| dom.unlink() | |
| dom = parseString(b"<?xml version='1.0' encoding='utf-8'?>" | |
| b"<doc>\xc3\xa9</doc>") | |
| self.assertEqual(dom.documentElement.firstChild.data, "\xe9") | |
| dom.unlink() | |
| def testComment(self): | |
| dom = Document() | |
| comment = dom.createComment("comment") | |
| self.assertEqual(comment.nodeType, Node.COMMENT_NODE) | |
| self.assertEqual(comment.nodeName, "#comment") | |
| self.assertEqual(comment.data, "comment") | |
| self.assertEqual(comment.nodeValue, "comment") | |
| self.assertIsNone(comment.attributes) | |
| dom.appendChild(comment) | |
| self.assertEqual(dom.toxml(), | |
| '<?xml version="1.0" ?><!--comment-->') | |
| dom.unlink() | |
| dom = parseString("<doc><!--comment--></doc>") | |
| comment = dom.documentElement.firstChild | |
| self.assertEqual(comment.nodeType, Node.COMMENT_NODE) | |
| self.assertEqual(comment.data, "comment") | |
| dom.unlink() | |
| def testAttrListItem(self): | |
| dom = parseString("<doc a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(attrs.item(0).name, "a") | |
| self.assertEqual(attrs.item(1).name, "b") | |
| self.assertIsNone(attrs.item(2)) | |
| dom.unlink() | |
| def testAttrListItems(self): | |
| dom = parseString("<doc a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(attrs.items(), [("a", "1"), ("b", "2")]) | |
| dom.unlink() | |
| def testAttrListItemNS(self): | |
| dom = parseString("<doc xmlns:p='http://xml.python.org/ns' " | |
| "p:a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(attrs.itemsNS(), [ | |
| ((xml.dom.XMLNS_NAMESPACE, "p"), "http://xml.python.org/ns"), | |
| (("http://xml.python.org/ns", "a"), "1"), | |
| ((None, "b"), "2"), | |
| ]) | |
| dom.unlink() | |
| def testAttrListKeys(self): | |
| dom = parseString("<doc a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(list(attrs.keys()), ["a", "b"]) | |
| dom.unlink() | |
| def testAttrListKeysNS(self): | |
| dom = parseString("<doc xmlns:p='http://xml.python.org/ns' " | |
| "p:a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(list(attrs.keysNS()), [ | |
| (xml.dom.XMLNS_NAMESPACE, "p"), | |
| ("http://xml.python.org/ns", "a"), | |
| (None, "b"), | |
| ]) | |
| dom.unlink() | |
| def testRemoveNamedItem(self): | |
| doc = parseString("<doc a=''/>") | |
| e = doc.documentElement | |
| attrs = e.attributes | |
| a1 = e.getAttributeNode("a") | |
| a2 = attrs.removeNamedItem("a") | |
| self.assertTrue(a1.isSameNode(a2)) | |
| self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a") | |
| def testRemoveNamedItemNS(self): | |
| doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>") | |
| e = doc.documentElement | |
| attrs = e.attributes | |
| a1 = e.getAttributeNodeNS("http://xml.python.org/", "b") | |
| a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b") | |
| self.assertTrue(a1.isSameNode(a2)) | |
| self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS, | |
| "http://xml.python.org/", "b") | |
| def testAttrListValues(self): | |
| dom = parseString("<doc a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual([attr.name for attr in attrs.values()], ["a", "b"]) | |
| self.assertEqual([attr.value for attr in attrs.values()], ["1", "2"]) | |
| dom.unlink() | |
| def testAttrListLength(self): | |
| dom = parseString("<doc a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(attrs.length, 2) | |
| self.assertEqual(len(attrs), 2) | |
| dom.unlink() | |
| dom = parseString("<doc/>") | |
| self.assertEqual(dom.documentElement.attributes.length, 0) | |
| dom.unlink() | |
| def testAttrList__getitem__(self): | |
| dom = parseString("<doc xmlns:p='http://xml.python.org/ns' " | |
| "p:a='1' b='2'/>") | |
| attrs = dom.documentElement.attributes | |
| self.assertEqual(attrs["b"].value, "2") | |
| self.assertEqual(attrs[("http://xml.python.org/ns", "a")].value, "1") | |
| self.assertRaises(KeyError, attrs.__getitem__, "missing") | |
| self.assertRaises(KeyError, attrs.__getitem__, (None, "missing")) | |
| dom.unlink() | |
| def testAttrList__setitem__(self): | |
| dom = parseString("<doc a='1'/>") | |
| elem = dom.documentElement | |
| attrs = elem.attributes | |
| attrs["a"] = "2" | |
| self.assertEqual(elem.getAttribute("a"), "2") | |
| attrs["b"] = "3" | |
| self.assertEqual(elem.getAttribute("b"), "3") | |
| self.assertEqual(attrs.length, 2) | |
| attr = dom.createAttribute("c") | |
| attr.value = "4" | |
| attrs["c"] = attr | |
| self.assertIs(elem.getAttributeNode("c"), attr) | |
| self.assertEqual(elem.getAttribute("c"), "4") | |
| dom.unlink() | |
| def testSetAttrValueandNodeValue(self): | |
| dom = parseString("<doc a='1'/>") | |
| attr = dom.documentElement.getAttributeNode("a") | |
| self.assertEqual(attr.value, "1") | |
| self.assertEqual(attr.nodeValue, "1") | |
| attr.value = "2" | |
| self.assertEqual(attr.nodeValue, "2") | |
| attr.nodeValue = "3" | |
| self.assertEqual(attr.value, "3") | |
| self.assertEqual(dom.documentElement.getAttribute("a"), "3") | |
| dom.unlink() | |
| def testParseElement(self): | |
| dom = parseString("<doc><child/><child/></doc>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.nodeType, Node.ELEMENT_NODE) | |
| self.assertEqual(elem.tagName, "doc") | |
| self.assertIsNone(elem.namespaceURI) | |
| self.assertIs(elem.parentNode, dom) | |
| self.assertIs(elem.ownerDocument, dom) | |
| self.assertEqual([child.tagName for child in elem.childNodes], | |
| ["child", "child"]) | |
| dom.unlink() | |
| def testParseAttributes(self): | |
| dom = parseString("<doc a='1' b='&'/>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.getAttribute("a"), "1") | |
| self.assertEqual(elem.getAttribute("b"), "&") | |
| self.assertEqual(elem.getAttribute("missing"), "") | |
| self.assertTrue(elem.hasAttribute("a")) | |
| self.assertFalse(elem.hasAttribute("missing")) | |
| attr = elem.getAttributeNode("a") | |
| self.assertTrue(attr.specified) | |
| self.assertIsNone(attr.namespaceURI) | |
| dom.unlink() | |
| def testParseElementNamespaces(self): | |
| dom = parseString("<p:doc xmlns:p='http://xml.python.org/ns'" | |
| " xmlns='http://xml.python.org/default'>" | |
| "<child/></p:doc>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.tagName, "p:doc") | |
| self.assertEqual(elem.namespaceURI, "http://xml.python.org/ns") | |
| self.assertEqual(elem.prefix, "p") | |
| self.assertEqual(elem.localName, "doc") | |
| child = elem.getElementsByTagName("child")[0] | |
| self.assertEqual(child.namespaceURI, "http://xml.python.org/default") | |
| self.assertIsNone(child.prefix) | |
| self.assertEqual(child.localName, "child") | |
| dom.unlink() | |
| def testParseAttributeNamespaces(self): | |
| dom = parseString("<doc xmlns:p='http://xml.python.org/ns'" | |
| " p:a='1' b='2'/>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.getAttributeNS("http://xml.python.org/ns", "a"), | |
| "1") | |
| self.assertEqual(elem.getAttributeNS(None, "b"), "2") | |
| attr = elem.getAttributeNodeNS("http://xml.python.org/ns", "a") | |
| self.assertEqual(attr.name, "p:a") | |
| self.assertEqual(attr.prefix, "p") | |
| self.assertEqual(attr.localName, "a") | |
| declaration = elem.getAttributeNode("xmlns:p") | |
| self.assertEqual(declaration.namespaceURI, xml.dom.XMLNS_NAMESPACE) | |
| self.assertEqual(declaration.value, "http://xml.python.org/ns") | |
| dom.unlink() | |
| def testParseProcessingInstructions(self): | |
| # the content of a processing instruction is tested | |
| # in testProcessingInstruction | |
| dom = parseString("<?before data?><doc/><?after?>") | |
| pi = dom.childNodes[0] | |
| self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) | |
| self.assertEqual(pi.target, "before") | |
| self.assertEqual(pi.data, "data") | |
| self.assertIs(pi.parentNode, dom) | |
| pi = dom.childNodes[2] | |
| self.assertEqual(pi.target, "after") | |
| self.assertEqual(pi.data, "") | |
| dom.unlink() | |
| def testChildNodes(self): | |
| dom = parseString("<doc>text<child/><!--comment--></doc>") | |
| children = dom.documentElement.childNodes | |
| self.assertEqual(len(children), 3) | |
| self.assertEqual([child.nodeType for child in children], | |
| [Node.TEXT_NODE, Node.ELEMENT_NODE, Node.COMMENT_NODE]) | |
| for child in children: | |
| self.assertIs(child.parentNode, dom.documentElement) | |
| dom.unlink() | |
| dom = parseString("<doc/>") | |
| self.assertEqual(len(dom.documentElement.childNodes), 0) | |
| dom.unlink() | |
| def testFirstChild(self): | |
| dom = parseString("<doc><a/><b/></doc>") | |
| elem = dom.documentElement | |
| self.assertEqual(elem.firstChild.tagName, "a") | |
| self.assertEqual(elem.lastChild.tagName, "b") | |
| self.assertIs(elem.firstChild, elem.childNodes[0]) | |
| self.assertIs(elem.lastChild, elem.childNodes[-1]) | |
| self.assertIsNone(elem.firstChild.previousSibling) | |
| self.assertIs(elem.firstChild.nextSibling, elem.lastChild) | |
| dom.unlink() | |
| dom = parseString("<doc/>") | |
| self.assertIsNone(dom.documentElement.firstChild) | |
| self.assertIsNone(dom.documentElement.lastChild) | |
| dom.unlink() | |
| def testHasChildNodes(self): | |
| dom = parseString("<doc><foo/></doc>") | |
| doc = dom.documentElement | |
| self.assertTrue(doc.hasChildNodes()) | |
| dom2 = parseString("<doc/>") | |
| doc2 = dom2.documentElement | |
| self.assertFalse(doc2.hasChildNodes()) | |
| def _testCloneElementCopiesAttributes(self, e1, e2, test): | |
| attrs1 = e1.attributes | |
| attrs2 = e2.attributes | |
| keys1 = list(attrs1.keys()) | |
| keys2 = list(attrs2.keys()) | |
| keys1.sort() | |
| keys2.sort() | |
| self.assertEqual(keys1, keys2) | |
| for i in range(len(keys1)): | |
| a1 = attrs1.item(i) | |
| a2 = attrs2.item(i) | |
| self.assertIsNot(a1, a2) | |
| self.assertEqual(a1.value, a2.value) | |
| self.assertEqual(a1.nodeValue, a2.nodeValue) | |
| self.assertEqual(a1.namespaceURI,a2.namespaceURI) | |
| self.assertEqual(a1.localName, a2.localName) | |
| self.assertIs(a2.ownerElement, e2) | |
| def _setupCloneElement(self, deep): | |
| dom = parseString("<doc attr='value'><foo/></doc>") | |
| root = dom.documentElement | |
| clone = root.cloneNode(deep) | |
| self._testCloneElementCopiesAttributes( | |
| root, clone, "testCloneElement" + (deep and "Deep" or "Shallow")) | |
| # mutilate the original so shared data is detected | |
| root.tagName = root.nodeName = "MODIFIED" | |
| root.setAttribute("attr", "NEW VALUE") | |
| root.setAttribute("added", "VALUE") | |
| return dom, clone | |
| def testCloneElementShallow(self): | |
| dom, clone = self._setupCloneElement(0) | |
| self.assertEqual(len(clone.childNodes), 0) | |
| self.assertEqual(clone.childNodes.length, 0) | |
| self.assertIsNone(clone.parentNode) | |
| self.assertEqual(clone.toxml(), '<doc attr="value"/>') | |
| dom.unlink() | |
| def testCloneElementDeep(self): | |
| dom, clone = self._setupCloneElement(1) | |
| self.assertEqual(len(clone.childNodes), 1) | |
| self.assertEqual(clone.childNodes.length, 1) | |
| self.assertIsNone(clone.parentNode) | |
| self.assertTrue(clone.toxml(), '<doc attr="value"><foo/></doc>') | |
| dom.unlink() | |
| def testCloneDocumentShallow(self): | |
| doc = parseString("<?xml version='1.0'?>\n" | |
| "<!-- comment -->" | |
| "<!DOCTYPE doc [\n" | |
| "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" | |
| "]>\n" | |
| "<doc attr='value'/>") | |
| doc2 = doc.cloneNode(0) | |
| self.assertIsNone(doc2, | |
| "testCloneDocumentShallow:" | |
| " shallow cloning of documents makes no sense!") | |
| def testCloneDocumentDeep(self): | |
| doc = parseString("<?xml version='1.0'?>\n" | |
| "<!-- comment -->" | |
| "<!DOCTYPE doc [\n" | |
| "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n" | |
| "]>\n" | |
| "<doc attr='value'/>") | |
| doc2 = doc.cloneNode(1) | |
| self.assertFalse((doc.isSameNode(doc2) or doc2.isSameNode(doc)), | |
| "testCloneDocumentDeep: document objects not distinct") | |
| self.assertEqual(len(doc.childNodes), len(doc2.childNodes), | |
| "testCloneDocumentDeep: wrong number of Document children") | |
| self.assertEqual(doc2.documentElement.nodeType, Node.ELEMENT_NODE, | |
| "testCloneDocumentDeep: documentElement not an ELEMENT_NODE") | |
| self.assertTrue(doc2.documentElement.ownerDocument.isSameNode(doc2), | |
| "testCloneDocumentDeep: documentElement owner is not new document") | |
| self.assertFalse(doc.documentElement.isSameNode(doc2.documentElement), | |
| "testCloneDocumentDeep: documentElement should not be shared") | |
| if doc.doctype is not None: | |
| # check the doctype iff the original DOM maintained it | |
| self.assertEqual(doc2.doctype.nodeType, Node.DOCUMENT_TYPE_NODE, | |
| "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE") | |
| self.assertTrue(doc2.doctype.ownerDocument.isSameNode(doc2)) | |
| self.assertFalse(doc.doctype.isSameNode(doc2.doctype)) | |
| def testCloneDocumentTypeDeepOk(self): | |
| doctype = create_nonempty_doctype() | |
| clone = doctype.cloneNode(1) | |
| self.confirm(clone is not None | |
| and clone.nodeName == doctype.nodeName | |
| and clone.name == doctype.name | |
| and clone.publicId == doctype.publicId | |
| and clone.systemId == doctype.systemId | |
| and len(clone.entities) == len(doctype.entities) | |
| and clone.entities.item(len(clone.entities)) is None | |
| and len(clone.notations) == len(doctype.notations) | |
| and clone.notations.item(len(clone.notations)) is None | |
| and len(clone.childNodes) == 0) | |
| for i in range(len(doctype.entities)): | |
| se = doctype.entities.item(i) | |
| ce = clone.entities.item(i) | |
| self.confirm((not se.isSameNode(ce)) | |
| and (not ce.isSameNode(se)) | |
| and ce.nodeName == se.nodeName | |
| and ce.notationName == se.notationName | |
| and ce.publicId == se.publicId | |
| and ce.systemId == se.systemId | |
| and ce.encoding == se.encoding | |
| and ce.actualEncoding == se.actualEncoding | |
| and ce.version == se.version) | |
| for i in range(len(doctype.notations)): | |
| sn = doctype.notations.item(i) | |
| cn = clone.notations.item(i) | |
| self.confirm((not sn.isSameNode(cn)) | |
| and (not cn.isSameNode(sn)) | |
| and cn.nodeName == sn.nodeName | |
| and cn.publicId == sn.publicId | |
| and cn.systemId == sn.systemId) | |
| def testCloneDocumentTypeDeepNotOk(self): | |
| doc = create_doc_with_doctype() | |
| clone = doc.doctype.cloneNode(1) | |
| self.assertIsNone(clone) | |
| def testCloneDocumentTypeShallowOk(self): | |
| doctype = create_nonempty_doctype() | |
| clone = doctype.cloneNode(0) | |
| self.confirm(clone is not None | |
| and clone.nodeName == doctype.nodeName | |
| and clone.name == doctype.name | |
| and clone.publicId == doctype.publicId | |
| and clone.systemId == doctype.systemId | |
| and len(clone.entities) == 0 | |
| and clone.entities.item(0) is None | |
| and len(clone.notations) == 0 | |
| and clone.notations.item(0) is None | |
| and len(clone.childNodes) == 0) | |
| def testCloneDocumentTypeShallowNotOk(self): | |
| doc = create_doc_with_doctype() | |
| clone = doc.doctype.cloneNode(0) | |
| self.assertIsNone(clone) | |
| def check_import_document(self, deep, testName): | |
| doc1 = parseString("<doc/>") | |
| doc2 = parseString("<doc/>") | |
| self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep) | |
| def testImportDocumentShallow(self): | |
| self.check_import_document(0, "testImportDocumentShallow") | |
| def testImportDocumentDeep(self): | |
| self.check_import_document(1, "testImportDocumentDeep") | |
| def testImportDocumentTypeShallow(self): | |
| src = create_doc_with_doctype() | |
| target = create_doc_without_doctype() | |
| self.assertRaises(xml.dom.NotSupportedErr, target.importNode, | |
| src.doctype, 0) | |
| def testImportDocumentTypeDeep(self): | |
| src = create_doc_with_doctype() | |
| target = create_doc_without_doctype() | |
| self.assertRaises(xml.dom.NotSupportedErr, target.importNode, | |
| src.doctype, 1) | |
| # Testing attribute clones uses a helper, and should always be deep, | |
| # even if the argument to cloneNode is false. | |
| def check_clone_attribute(self, deep, testName): | |
| doc = parseString("<doc attr='value'/>") | |
| attr = doc.documentElement.getAttributeNode("attr") | |
| self.assertIsNotNone(attr) | |
| clone = attr.cloneNode(deep) | |
| self.assertFalse(clone.isSameNode(attr)) | |
| self.assertFalse(attr.isSameNode(clone)) | |
| self.assertIsNone(clone.ownerElement, | |
| testName + ": ownerElement should be None") | |
| self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument), | |
| testName + ": ownerDocument does not match") | |
| self.confirm(clone.specified, | |
| testName + ": cloned attribute must have specified == True") | |
| def testCloneAttributeShallow(self): | |
| self.check_clone_attribute(0, "testCloneAttributeShallow") | |
| def testCloneAttributeDeep(self): | |
| self.check_clone_attribute(1, "testCloneAttributeDeep") | |
| def check_clone_pi(self, deep, testName): | |
| doc = parseString("<?target data?><doc/>") | |
| pi = doc.firstChild | |
| self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) | |
| clone = pi.cloneNode(deep) | |
| self.confirm(clone.target == pi.target | |
| and clone.data == pi.data) | |
| def testClonePIShallow(self): | |
| self.check_clone_pi(0, "testClonePIShallow") | |
| def testClonePIDeep(self): | |
| self.check_clone_pi(1, "testClonePIDeep") | |
| def check_clone_node_entity(self, clone_document): | |
| # bpo-35052: Test user data handler in cloneNode() on a document with | |
| # an entity | |
| document = xml.dom.minidom.parseString(""" | |
| <?xml version="1.0" ?> | |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" | |
| "http://www.w3.org/TR/html4/strict.dtd" | |
| [ <!ENTITY smile "☺"> ] | |
| > | |
| <doc>Don't let entities make you frown ⌣</doc> | |
| """.strip()) | |
| class Handler: | |
| def handle(self, operation, key, data, src, dst): | |
| self.operation = operation | |
| self.key = key | |
| self.data = data | |
| self.src = src | |
| self.dst = dst | |
| handler = Handler() | |
| doctype = document.doctype | |
| entity = doctype.entities['smile'] | |
| entity.setUserData("key", "data", handler) | |
| if clone_document: | |
| # clone Document | |
| clone = document.cloneNode(deep=True) | |
| self.assertEqual(clone.documentElement.firstChild.wholeText, | |
| "Don't let entities make you frown ☺") | |
| operation = xml.dom.UserDataHandler.NODE_IMPORTED | |
| dst = clone.doctype.entities['smile'] | |
| else: | |
| # clone DocumentType | |
| with support.swap_attr(doctype, 'ownerDocument', None): | |
| clone = doctype.cloneNode(deep=True) | |
| operation = xml.dom.UserDataHandler.NODE_CLONED | |
| dst = clone.entities['smile'] | |
| self.assertEqual(handler.operation, operation) | |
| self.assertEqual(handler.key, "key") | |
| self.assertEqual(handler.data, "data") | |
| self.assertIs(handler.src, entity) | |
| self.assertIs(handler.dst, dst) | |
| def testCloneNodeEntity(self): | |
| self.check_clone_node_entity(False) | |
| self.check_clone_node_entity(True) | |
| def testNormalize(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("first")) | |
| root.appendChild(doc.createTextNode("second")) | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2, | |
| "testNormalize -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 1 | |
| and root.childNodes.length == 1 | |
| and root.firstChild is root.lastChild | |
| and root.firstChild.data == "firstsecond" | |
| , "testNormalize -- result") | |
| doc.unlink() | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("")) | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 0 | |
| and root.childNodes.length == 0, | |
| "testNormalize -- single empty node removed") | |
| doc.unlink() | |
| def testNormalizeCombineAndNextSibling(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("first")) | |
| root.appendChild(doc.createTextNode("second")) | |
| root.appendChild(doc.createElement("i")) | |
| self.confirm(len(root.childNodes) == 3 | |
| and root.childNodes.length == 3, | |
| "testNormalizeCombineAndNextSibling -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2 | |
| and root.firstChild.data == "firstsecond" | |
| and root.firstChild is not root.lastChild | |
| and root.firstChild.nextSibling is root.lastChild | |
| and root.firstChild.previousSibling is None | |
| and root.lastChild.previousSibling is root.firstChild | |
| and root.lastChild.nextSibling is None | |
| , "testNormalizeCombinedAndNextSibling -- result") | |
| doc.unlink() | |
| def testNormalizeDeleteWithPrevSibling(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("first")) | |
| root.appendChild(doc.createTextNode("")) | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2, | |
| "testNormalizeDeleteWithPrevSibling -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 1 | |
| and root.childNodes.length == 1 | |
| and root.firstChild.data == "first" | |
| and root.firstChild is root.lastChild | |
| and root.firstChild.nextSibling is None | |
| and root.firstChild.previousSibling is None | |
| , "testNormalizeDeleteWithPrevSibling -- result") | |
| doc.unlink() | |
| def testNormalizeDeleteWithNextSibling(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("")) | |
| root.appendChild(doc.createTextNode("second")) | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2, | |
| "testNormalizeDeleteWithNextSibling -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 1 | |
| and root.childNodes.length == 1 | |
| and root.firstChild.data == "second" | |
| and root.firstChild is root.lastChild | |
| and root.firstChild.nextSibling is None | |
| and root.firstChild.previousSibling is None | |
| , "testNormalizeDeleteWithNextSibling -- result") | |
| doc.unlink() | |
| def testNormalizeDeleteWithTwoNonTextSiblings(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createElement("i")) | |
| root.appendChild(doc.createTextNode("")) | |
| root.appendChild(doc.createElement("i")) | |
| self.confirm(len(root.childNodes) == 3 | |
| and root.childNodes.length == 3, | |
| "testNormalizeDeleteWithTwoSiblings -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2 | |
| and root.firstChild is not root.lastChild | |
| and root.firstChild.nextSibling is root.lastChild | |
| and root.firstChild.previousSibling is None | |
| and root.lastChild.previousSibling is root.firstChild | |
| and root.lastChild.nextSibling is None | |
| , "testNormalizeDeleteWithTwoSiblings -- result") | |
| doc.unlink() | |
| def testNormalizeDeleteAndCombine(self): | |
| doc = parseString("<doc/>") | |
| root = doc.documentElement | |
| root.appendChild(doc.createTextNode("")) | |
| root.appendChild(doc.createTextNode("second")) | |
| root.appendChild(doc.createTextNode("")) | |
| root.appendChild(doc.createTextNode("fourth")) | |
| root.appendChild(doc.createTextNode("")) | |
| self.confirm(len(root.childNodes) == 5 | |
| and root.childNodes.length == 5, | |
| "testNormalizeDeleteAndCombine -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 1 | |
| and root.childNodes.length == 1 | |
| and root.firstChild is root.lastChild | |
| and root.firstChild.data == "secondfourth" | |
| and root.firstChild.previousSibling is None | |
| and root.firstChild.nextSibling is None | |
| , "testNormalizeDeleteAndCombine -- result") | |
| doc.unlink() | |
| def testNormalizeRecursion(self): | |
| doc = parseString("<doc>" | |
| "<o>" | |
| "<i/>" | |
| "t" | |
| # | |
| #x | |
| "</o>" | |
| "<o>" | |
| "<o>" | |
| "t2" | |
| #x2 | |
| "</o>" | |
| "t3" | |
| #x3 | |
| "</o>" | |
| # | |
| "</doc>") | |
| root = doc.documentElement | |
| root.childNodes[0].appendChild(doc.createTextNode("")) | |
| root.childNodes[0].appendChild(doc.createTextNode("x")) | |
| root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2")) | |
| root.childNodes[1].appendChild(doc.createTextNode("x3")) | |
| root.appendChild(doc.createTextNode("")) | |
| self.confirm(len(root.childNodes) == 3 | |
| and root.childNodes.length == 3 | |
| and len(root.childNodes[0].childNodes) == 4 | |
| and root.childNodes[0].childNodes.length == 4 | |
| and len(root.childNodes[1].childNodes) == 3 | |
| and root.childNodes[1].childNodes.length == 3 | |
| and len(root.childNodes[1].childNodes[0].childNodes) == 2 | |
| and root.childNodes[1].childNodes[0].childNodes.length == 2 | |
| , "testNormalize2 -- preparation") | |
| doc.normalize() | |
| self.confirm(len(root.childNodes) == 2 | |
| and root.childNodes.length == 2 | |
| and len(root.childNodes[0].childNodes) == 2 | |
| and root.childNodes[0].childNodes.length == 2 | |
| and len(root.childNodes[1].childNodes) == 2 | |
| and root.childNodes[1].childNodes.length == 2 | |
| and len(root.childNodes[1].childNodes[0].childNodes) == 1 | |
| and root.childNodes[1].childNodes[0].childNodes.length == 1 | |
| , "testNormalize2 -- childNodes lengths") | |
| self.confirm(root.childNodes[0].childNodes[1].data == "tx" | |
| and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2" | |
| and root.childNodes[1].childNodes[1].data == "t3x3" | |
| , "testNormalize2 -- joined text fields") | |
| self.confirm(root.childNodes[0].childNodes[1].nextSibling is None | |
| and root.childNodes[0].childNodes[1].previousSibling | |
| is root.childNodes[0].childNodes[0] | |
| and root.childNodes[0].childNodes[0].previousSibling is None | |
| and root.childNodes[0].childNodes[0].nextSibling | |
| is root.childNodes[0].childNodes[1] | |
| and root.childNodes[1].childNodes[1].nextSibling is None | |
| and root.childNodes[1].childNodes[1].previousSibling | |
| is root.childNodes[1].childNodes[0] | |
| and root.childNodes[1].childNodes[0].previousSibling is None | |
| and root.childNodes[1].childNodes[0].nextSibling | |
| is root.childNodes[1].childNodes[1] | |
| , "testNormalize2 -- sibling pointers") | |
| doc.unlink() | |
| def testBug0777884(self): | |
| doc = parseString("<o>text</o>") | |
| text = doc.documentElement.childNodes[0] | |
| self.assertEqual(text.nodeType, Node.TEXT_NODE) | |
| # Should run quietly, doing nothing. | |
| text.normalize() | |
| doc.unlink() | |
| def testBug1433694(self): | |
| doc = parseString("<o><i/>t</o>") | |
| node = doc.documentElement | |
| node.childNodes[1].nodeValue = "" | |
| node.normalize() | |
| self.assertIsNone(node.childNodes[-1].nextSibling, | |
| "Final child's .nextSibling should be None") | |
| def testSiblings(self): | |
| doc = parseString("<doc><?pi?>text?<elm/></doc>") | |
| root = doc.documentElement | |
| (pi, text, elm) = root.childNodes | |
| self.confirm(pi.nextSibling is text and | |
| pi.previousSibling is None and | |
| text.nextSibling is elm and | |
| text.previousSibling is pi and | |
| elm.nextSibling is None and | |
| elm.previousSibling is text, "testSiblings") | |
| doc.unlink() | |
| def testParents(self): | |
| doc = parseString( | |
| "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>") | |
| root = doc.documentElement | |
| elm1 = root.childNodes[0] | |
| (elm2a, elm2b) = elm1.childNodes | |
| elm3 = elm2b.childNodes[0] | |
| self.confirm(root.parentNode is doc and | |
| elm1.parentNode is root and | |
| elm2a.parentNode is elm1 and | |
| elm2b.parentNode is elm1 and | |
| elm3.parentNode is elm2b, "testParents") | |
| doc.unlink() | |
| def testNodeListItem(self): | |
| doc = parseString("<doc><e/><e/></doc>") | |
| children = doc.childNodes | |
| docelem = children[0] | |
| self.confirm(children[0] is children.item(0) | |
| and children.item(1) is None | |
| and docelem.childNodes.item(0) is docelem.childNodes[0] | |
| and docelem.childNodes.item(1) is docelem.childNodes[1] | |
| and docelem.childNodes.item(0).childNodes.item(0) is None, | |
| "test NodeList.item()") | |
| doc.unlink() | |
| def testEncodings(self): | |
| doc = parseString('<foo>€</foo>') | |
| self.assertEqual(doc.toxml(), | |
| '<?xml version="1.0" ?><foo>\u20ac</foo>') | |
| self.assertEqual(doc.toxml('utf-8'), | |
| b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>') | |
| self.assertEqual(doc.toxml('iso-8859-15'), | |
| b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>') | |
| self.assertEqual(doc.toxml('us-ascii'), | |
| b'<?xml version="1.0" encoding="us-ascii"?><foo>€</foo>') | |
| self.assertEqual(doc.toxml('utf-16'), | |
| '<?xml version="1.0" encoding="utf-16"?>' | |
| '<foo>\u20ac</foo>'.encode('utf-16')) | |
| # Verify that character decoding errors raise exceptions instead | |
| # of crashing | |
| with self.assertRaises((UnicodeDecodeError, ExpatError)): | |
| parseString( | |
| b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>' | |
| ) | |
| doc.unlink() | |
| def testStandalone(self): | |
| doc = parseString('<foo>€</foo>') | |
| self.assertEqual(doc.toxml(), | |
| '<?xml version="1.0" ?><foo>\u20ac</foo>') | |
| self.assertEqual(doc.toxml(standalone=None), | |
| '<?xml version="1.0" ?><foo>\u20ac</foo>') | |
| self.assertEqual(doc.toxml(standalone=True), | |
| '<?xml version="1.0" standalone="yes"?><foo>\u20ac</foo>') | |
| self.assertEqual(doc.toxml(standalone=False), | |
| '<?xml version="1.0" standalone="no"?><foo>\u20ac</foo>') | |
| self.assertEqual(doc.toxml('utf-8', True), | |
| b'<?xml version="1.0" encoding="utf-8" standalone="yes"?>' | |
| b'<foo>\xe2\x82\xac</foo>') | |
| doc.unlink() | |
| class UserDataHandler: | |
| called = 0 | |
| def handle(self, operation, key, data, src, dst): | |
| dst.setUserData(key, data + 1, self) | |
| src.setUserData(key, None, None) | |
| self.called = 1 | |
| def testUserData(self): | |
| dom = Document() | |
| n = dom.createElement('e') | |
| self.assertIsNone(n.getUserData("foo")) | |
| n.setUserData("foo", None, None) | |
| self.assertIsNone(n.getUserData("foo")) | |
| n.setUserData("foo", 12, 12) | |
| n.setUserData("bar", 13, 13) | |
| self.assertEqual(n.getUserData("foo"), 12) | |
| self.assertEqual(n.getUserData("bar"), 13) | |
| n.setUserData("foo", None, None) | |
| self.assertIsNone(n.getUserData("foo")) | |
| self.assertEqual(n.getUserData("bar"), 13) | |
| handler = self.UserDataHandler() | |
| n.setUserData("bar", 12, handler) | |
| c = n.cloneNode(1) | |
| self.confirm(handler.called | |
| and n.getUserData("bar") is None | |
| and c.getUserData("bar") == 13) | |
| n.unlink() | |
| c.unlink() | |
| dom.unlink() | |
| def checkRenameNodeSharedConstraints(self, doc, node): | |
| # Make sure illegal NS usage is detected: | |
| self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, | |
| "http://xml.python.org/ns", "xmlns:foo") | |
| doc2 = parseString("<doc/>") | |
| self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, | |
| xml.dom.EMPTY_NAMESPACE, "foo") | |
| def testRenameAttribute(self): | |
| doc = parseString("<doc a='v'/>") | |
| elem = doc.documentElement | |
| attrmap = elem.attributes | |
| attr = elem.attributes['a'] | |
| # Simple renaming | |
| attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") | |
| self.confirm(attr.name == "b" | |
| and attr.nodeName == "b" | |
| and attr.localName is None | |
| and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE | |
| and attr.prefix is None | |
| and attr.value == "v" | |
| and elem.getAttributeNode("a") is None | |
| and elem.getAttributeNode("b").isSameNode(attr) | |
| and attrmap["b"].isSameNode(attr) | |
| and attr.ownerDocument.isSameNode(doc) | |
| and attr.ownerElement.isSameNode(elem)) | |
| # Rename to have a namespace, no prefix | |
| attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") | |
| self.confirm(attr.name == "c" | |
| and attr.nodeName == "c" | |
| and attr.localName == "c" | |
| and attr.namespaceURI == "http://xml.python.org/ns" | |
| and attr.prefix is None | |
| and attr.value == "v" | |
| and elem.getAttributeNode("a") is None | |
| and elem.getAttributeNode("b") is None | |
| and elem.getAttributeNode("c").isSameNode(attr) | |
| and elem.getAttributeNodeNS( | |
| "http://xml.python.org/ns", "c").isSameNode(attr) | |
| and attrmap["c"].isSameNode(attr) | |
| and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) | |
| # Rename to have a namespace, with prefix | |
| attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") | |
| self.confirm(attr.name == "p:d" | |
| and attr.nodeName == "p:d" | |
| and attr.localName == "d" | |
| and attr.namespaceURI == "http://xml.python.org/ns2" | |
| and attr.prefix == "p" | |
| and attr.value == "v" | |
| and elem.getAttributeNode("a") is None | |
| and elem.getAttributeNode("b") is None | |
| and elem.getAttributeNode("c") is None | |
| and elem.getAttributeNodeNS( | |
| "http://xml.python.org/ns", "c") is None | |
| and elem.getAttributeNode("p:d").isSameNode(attr) | |
| and elem.getAttributeNodeNS( | |
| "http://xml.python.org/ns2", "d").isSameNode(attr) | |
| and attrmap["p:d"].isSameNode(attr) | |
| and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) | |
| # Rename back to a simple non-NS node | |
| attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") | |
| self.confirm(attr.name == "e" | |
| and attr.nodeName == "e" | |
| and attr.localName is None | |
| and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE | |
| and attr.prefix is None | |
| and attr.value == "v" | |
| and elem.getAttributeNode("a") is None | |
| and elem.getAttributeNode("b") is None | |
| and elem.getAttributeNode("c") is None | |
| and elem.getAttributeNode("p:d") is None | |
| and elem.getAttributeNodeNS( | |
| "http://xml.python.org/ns", "c") is None | |
| and elem.getAttributeNode("e").isSameNode(attr) | |
| and attrmap["e"].isSameNode(attr)) | |
| self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, | |
| "http://xml.python.org/ns", "xmlns") | |
| self.checkRenameNodeSharedConstraints(doc, attr) | |
| doc.unlink() | |
| def testRenameElement(self): | |
| doc = parseString("<doc/>") | |
| elem = doc.documentElement | |
| # Simple renaming | |
| elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") | |
| self.confirm(elem.tagName == "a" | |
| and elem.nodeName == "a" | |
| and elem.localName is None | |
| and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE | |
| and elem.prefix is None | |
| and elem.ownerDocument.isSameNode(doc)) | |
| # Rename to have a namespace, no prefix | |
| elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") | |
| self.confirm(elem.tagName == "b" | |
| and elem.nodeName == "b" | |
| and elem.localName == "b" | |
| and elem.namespaceURI == "http://xml.python.org/ns" | |
| and elem.prefix is None | |
| and elem.ownerDocument.isSameNode(doc)) | |
| # Rename to have a namespace, with prefix | |
| elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") | |
| self.confirm(elem.tagName == "p:c" | |
| and elem.nodeName == "p:c" | |
| and elem.localName == "c" | |
| and elem.namespaceURI == "http://xml.python.org/ns2" | |
| and elem.prefix == "p" | |
| and elem.ownerDocument.isSameNode(doc)) | |
| # Rename back to a simple non-NS node | |
| elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") | |
| self.confirm(elem.tagName == "d" | |
| and elem.nodeName == "d" | |
| and elem.localName is None | |
| and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE | |
| and elem.prefix is None | |
| and elem.ownerDocument.isSameNode(doc)) | |
| self.checkRenameNodeSharedConstraints(doc, elem) | |
| doc.unlink() | |
| def testRenameOther(self): | |
| # We have to create a comment node explicitly since not all DOM | |
| # builders used with minidom add comments to the DOM. | |
| doc = xml.dom.minidom.getDOMImplementation().createDocument( | |
| xml.dom.EMPTY_NAMESPACE, "e", None) | |
| node = doc.createComment("comment") | |
| self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, | |
| xml.dom.EMPTY_NAMESPACE, "foo") | |
| doc.unlink() | |
| def testWholeText(self): | |
| doc = parseString("<doc>a</doc>") | |
| elem = doc.documentElement | |
| text = elem.childNodes[0] | |
| self.assertEqual(text.nodeType, Node.TEXT_NODE) | |
| self.checkWholeText(text, "a") | |
| elem.appendChild(doc.createTextNode("b")) | |
| self.checkWholeText(text, "ab") | |
| elem.insertBefore(doc.createCDATASection("c"), text) | |
| self.checkWholeText(text, "cab") | |
| # make sure we don't cross other nodes | |
| splitter = doc.createComment("comment") | |
| elem.appendChild(splitter) | |
| text2 = doc.createTextNode("d") | |
| elem.appendChild(text2) | |
| self.checkWholeText(text, "cab") | |
| self.checkWholeText(text2, "d") | |
| x = doc.createElement("x") | |
| elem.replaceChild(x, splitter) | |
| splitter = x | |
| self.checkWholeText(text, "cab") | |
| self.checkWholeText(text2, "d") | |
| x = doc.createProcessingInstruction("y", "z") | |
| elem.replaceChild(x, splitter) | |
| splitter = x | |
| self.checkWholeText(text, "cab") | |
| self.checkWholeText(text2, "d") | |
| elem.removeChild(splitter) | |
| self.checkWholeText(text, "cabd") | |
| self.checkWholeText(text2, "cabd") | |
| def testPatch1094164(self): | |
| doc = parseString("<doc><e/></doc>") | |
| elem = doc.documentElement | |
| e = elem.firstChild | |
| self.assertIs(e.parentNode, elem, "Before replaceChild()") | |
| # Check that replacing a child with itself leaves the tree unchanged | |
| elem.replaceChild(e, e) | |
| self.assertIs(e.parentNode, elem, "After replaceChild()") | |
| def testReplaceWholeText(self): | |
| def setup(): | |
| doc = parseString("<doc>a<e/>d</doc>") | |
| elem = doc.documentElement | |
| text1 = elem.firstChild | |
| text2 = elem.lastChild | |
| splitter = text1.nextSibling | |
| elem.insertBefore(doc.createTextNode("b"), splitter) | |
| elem.insertBefore(doc.createCDATASection("c"), text1) | |
| return doc, elem, text1, splitter, text2 | |
| doc, elem, text1, splitter, text2 = setup() | |
| text = text1.replaceWholeText("new content") | |
| self.checkWholeText(text, "new content") | |
| self.checkWholeText(text2, "d") | |
| self.assertEqual(len(elem.childNodes), 3) | |
| doc, elem, text1, splitter, text2 = setup() | |
| text = text2.replaceWholeText("new content") | |
| self.checkWholeText(text, "new content") | |
| self.checkWholeText(text1, "cab") | |
| self.assertEqual(len(elem.childNodes), 5) | |
| doc, elem, text1, splitter, text2 = setup() | |
| text = text1.replaceWholeText("") | |
| self.checkWholeText(text2, "d") | |
| self.confirm(text is None | |
| and len(elem.childNodes) == 2) | |
| def testSchemaType(self): | |
| doc = parseString( | |
| "<!DOCTYPE doc [\n" | |
| " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" | |
| " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" | |
| " <!ATTLIST doc id ID #IMPLIED \n" | |
| " ref IDREF #IMPLIED \n" | |
| " refs IDREFS #IMPLIED \n" | |
| " enum (a|b) #IMPLIED \n" | |
| " ent ENTITY #IMPLIED \n" | |
| " ents ENTITIES #IMPLIED \n" | |
| " nm NMTOKEN #IMPLIED \n" | |
| " nms NMTOKENS #IMPLIED \n" | |
| " text CDATA #IMPLIED \n" | |
| " >\n" | |
| "]><doc id='name' notid='name' text='splat!' enum='b'" | |
| " ref='name' refs='name name' ent='e1' ents='e1 e2'" | |
| " nm='123' nms='123 abc' />") | |
| elem = doc.documentElement | |
| # We don't want to rely on any specific loader at this point, so | |
| # just make sure we can get to all the names, and that the | |
| # DTD-based namespace is right. The names can vary by loader | |
| # since each supports a different level of DTD information. | |
| t = elem.schemaType | |
| self.confirm(t.name is None | |
| and t.namespace == xml.dom.EMPTY_NAMESPACE) | |
| names = "id notid text enum ref refs ent ents nm nms".split() | |
| for name in names: | |
| a = elem.getAttributeNode(name) | |
| t = a.schemaType | |
| self.confirm(hasattr(t, "name") | |
| and t.namespace == xml.dom.EMPTY_NAMESPACE) | |
| def testSetIdAttribute(self): | |
| doc = parseString("<doc a1='v' a2='w'/>") | |
| e = doc.documentElement | |
| a1 = e.getAttributeNode("a1") | |
| a2 = e.getAttributeNode("a2") | |
| self.confirm(doc.getElementById("v") is None | |
| and not a1.isId | |
| and not a2.isId) | |
| e.setIdAttribute("a1") | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and a1.isId | |
| and not a2.isId) | |
| e.setIdAttribute("a2") | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and e.isSameNode(doc.getElementById("w")) | |
| and a1.isId | |
| and a2.isId) | |
| # replace the a1 node; the new node should *not* be an ID | |
| a3 = doc.createAttribute("a1") | |
| a3.value = "v" | |
| e.setAttributeNode(a3) | |
| self.confirm(doc.getElementById("v") is None | |
| and e.isSameNode(doc.getElementById("w")) | |
| and not a1.isId | |
| and a2.isId | |
| and not a3.isId) | |
| # renaming an attribute should not affect its ID-ness: | |
| doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") | |
| self.confirm(e.isSameNode(doc.getElementById("w")) | |
| and a2.isId) | |
| def testSetIdAttributeNS(self): | |
| NS1 = "http://xml.python.org/ns1" | |
| NS2 = "http://xml.python.org/ns2" | |
| doc = parseString("<doc" | |
| " xmlns:ns1='" + NS1 + "'" | |
| " xmlns:ns2='" + NS2 + "'" | |
| " ns1:a1='v' ns2:a2='w'/>") | |
| e = doc.documentElement | |
| a1 = e.getAttributeNodeNS(NS1, "a1") | |
| a2 = e.getAttributeNodeNS(NS2, "a2") | |
| self.confirm(doc.getElementById("v") is None | |
| and not a1.isId | |
| and not a2.isId) | |
| e.setIdAttributeNS(NS1, "a1") | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and a1.isId | |
| and not a2.isId) | |
| e.setIdAttributeNS(NS2, "a2") | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and e.isSameNode(doc.getElementById("w")) | |
| and a1.isId | |
| and a2.isId) | |
| # replace the a1 node; the new node should *not* be an ID | |
| a3 = doc.createAttributeNS(NS1, "a1") | |
| a3.value = "v" | |
| e.setAttributeNode(a3) | |
| self.assertTrue(e.isSameNode(doc.getElementById("w"))) | |
| self.assertFalse(a1.isId) | |
| self.assertTrue(a2.isId) | |
| self.assertFalse(a3.isId) | |
| self.assertIsNone(doc.getElementById("v")) | |
| # renaming an attribute should not affect its ID-ness: | |
| doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") | |
| self.confirm(e.isSameNode(doc.getElementById("w")) | |
| and a2.isId) | |
| def testSetIdAttributeNode(self): | |
| NS1 = "http://xml.python.org/ns1" | |
| NS2 = "http://xml.python.org/ns2" | |
| doc = parseString("<doc" | |
| " xmlns:ns1='" + NS1 + "'" | |
| " xmlns:ns2='" + NS2 + "'" | |
| " ns1:a1='v' ns2:a2='w'/>") | |
| e = doc.documentElement | |
| a1 = e.getAttributeNodeNS(NS1, "a1") | |
| a2 = e.getAttributeNodeNS(NS2, "a2") | |
| self.confirm(doc.getElementById("v") is None | |
| and not a1.isId | |
| and not a2.isId) | |
| e.setIdAttributeNode(a1) | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and a1.isId | |
| and not a2.isId) | |
| e.setIdAttributeNode(a2) | |
| self.confirm(e.isSameNode(doc.getElementById("v")) | |
| and e.isSameNode(doc.getElementById("w")) | |
| and a1.isId | |
| and a2.isId) | |
| # replace the a1 node; the new node should *not* be an ID | |
| a3 = doc.createAttributeNS(NS1, "a1") | |
| a3.value = "v" | |
| e.setAttributeNode(a3) | |
| self.assertTrue(e.isSameNode(doc.getElementById("w"))) | |
| self.assertFalse(a1.isId) | |
| self.assertTrue(a2.isId) | |
| self.assertFalse(a3.isId) | |
| self.assertIsNone(doc.getElementById("v")) | |
| # renaming an attribute should not affect its ID-ness: | |
| doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") | |
| self.confirm(e.isSameNode(doc.getElementById("w")) | |
| and a2.isId) | |
| def assert_recursive_equal(self, doc, doc2): | |
| stack = [(doc, doc2)] | |
| while stack: | |
| n1, n2 = stack.pop() | |
| self.assertEqual(n1.nodeType, n2.nodeType) | |
| self.assertEqual(len(n1.childNodes), len(n2.childNodes)) | |
| self.assertEqual(n1.nodeName, n2.nodeName) | |
| self.assertFalse(n1.isSameNode(n2)) | |
| self.assertFalse(n2.isSameNode(n1)) | |
| if n1.nodeType == Node.DOCUMENT_TYPE_NODE: | |
| len(n1.entities) | |
| len(n2.entities) | |
| len(n1.notations) | |
| len(n2.notations) | |
| self.assertEqual(len(n1.entities), len(n2.entities)) | |
| self.assertEqual(len(n1.notations), len(n2.notations)) | |
| for i in range(len(n1.notations)): | |
| # XXX this loop body doesn't seem to be executed? | |
| no1 = n1.notations.item(i) | |
| no2 = n1.notations.item(i) | |
| self.assertEqual(no1.name, no2.name) | |
| self.assertEqual(no1.publicId, no2.publicId) | |
| self.assertEqual(no1.systemId, no2.systemId) | |
| stack.append((no1, no2)) | |
| for i in range(len(n1.entities)): | |
| e1 = n1.entities.item(i) | |
| e2 = n2.entities.item(i) | |
| self.assertEqual(e1.notationName, e2.notationName) | |
| self.assertEqual(e1.publicId, e2.publicId) | |
| self.assertEqual(e1.systemId, e2.systemId) | |
| stack.append((e1, e2)) | |
| if n1.nodeType != Node.DOCUMENT_NODE: | |
| self.assertTrue(n1.ownerDocument.isSameNode(doc)) | |
| self.assertTrue(n2.ownerDocument.isSameNode(doc2)) | |
| for i in range(len(n1.childNodes)): | |
| stack.append((n1.childNodes[i], n2.childNodes[i])) | |
| def testPickledDocument(self): | |
| doc = parseString(sample) | |
| for proto in range(2, pickle.HIGHEST_PROTOCOL + 1): | |
| s = pickle.dumps(doc, proto) | |
| doc2 = pickle.loads(s) | |
| self.assert_recursive_equal(doc, doc2) | |
| def testDeepcopiedDocument(self): | |
| doc = parseString(sample) | |
| doc2 = copy.deepcopy(doc) | |
| self.assert_recursive_equal(doc, doc2) | |
| def testSerializeCommentNodeWithDoubleHyphen(self): | |
| doc = create_doc_without_doctype() | |
| doc.appendChild(doc.createComment("foo--bar")) | |
| self.assertRaises(ValueError, doc.toxml) | |
| def testEmptyXMLNSValue(self): | |
| doc = parseString("<element xmlns=''>\n" | |
| "<foo/>\n</element>") | |
| doc2 = parseString(doc.toxml()) | |
| self.assertEqual(doc2.namespaceURI, xml.dom.EMPTY_NAMESPACE) | |
| def testExceptionOnSpacesInXMLNSValue(self): | |
| with self.assertRaises((ValueError, ExpatError)): | |
| parseString( | |
| '<element xmlns:abc="http:abc.com/de f g/hi/j k">' + | |
| '<abc:foo /></element>' | |
| ) | |
| def testDocRemoveChild(self): | |
| doc = parse(tstfile) | |
| title_tag = doc.documentElement.getElementsByTagName("TITLE")[0] | |
| self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag) | |
| num_children_before = len(doc.childNodes) | |
| doc.removeChild(doc.childNodes[0]) | |
| num_children_after = len(doc.childNodes) | |
| self.assertEqual(num_children_after, num_children_before - 1) | |
| def testProcessingInstructionNameError(self): | |
| # wrong variable in .nodeValue property will | |
| # lead to "NameError: name 'data' is not defined" | |
| doc = parse(tstfile) | |
| pi = doc.createProcessingInstruction("y", "z") | |
| pi.nodeValue = "crash" | |
| def test_minidom_attribute_order(self): | |
| xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' | |
| doc = parseString(xml_str) | |
| output = io.StringIO() | |
| doc.writexml(output) | |
| self.assertEqual(output.getvalue(), xml_str) | |
| def test_toxml_with_attributes_ordered(self): | |
| xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' | |
| doc = parseString(xml_str) | |
| self.assertEqual(doc.toxml(), xml_str) | |
| def test_toprettyxml_with_attributes_ordered(self): | |
| xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>' | |
| doc = parseString(xml_str) | |
| self.assertEqual(doc.toprettyxml(), | |
| '<?xml version="1.0" ?>\n' | |
| '<curriculum status="public" company="example"/>\n') | |
| def test_toprettyxml_with_cdata(self): | |
| xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' | |
| doc = parseString(xml_str) | |
| self.assertEqual(doc.toprettyxml(), | |
| '<?xml version="1.0" ?>\n' | |
| '<root>\n' | |
| '\t<node><![CDATA[</data>]]></node>\n' | |
| '</root>\n') | |
| def test_cdata_parsing(self): | |
| xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>' | |
| dom1 = parseString(xml_str) | |
| self.checkWholeText(dom1.getElementsByTagName('node')[0].firstChild, '</data>') | |
| dom2 = parseString(dom1.toprettyxml()) | |
| self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '</data>') | |
| def testNamespaceErr(self): | |
| doc = parseString("<doc/>") | |
| elem = doc.documentElement | |
| XML_NS = xml.dom.XML_NAMESPACE | |
| XMLNS_NS = xml.dom.XMLNS_NAMESPACE | |
| for namespaceURI, qname in [ | |
| (None, "p:e"), # a prefix without a namespace | |
| ("", "p:e"), | |
| ("http://xml.python.org/ns", "p:p:e"), # malformed | |
| ("http://xml.python.org/ns", "p:"), | |
| ("http://xml.python.org/ns", "p:1e"), | |
| ("http://xml.python.org/ns", "xml:e"), # the xml prefix | |
| ]: | |
| with self.subTest(namespaceURI=namespaceURI, qname=qname): | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| doc.createElementNS, namespaceURI, qname) | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| doc.createAttributeNS, namespaceURI, qname) | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| elem.setAttributeNS, namespaceURI, qname, "v") | |
| # the xmlns name and prefix are only allowed in the XMLNS namespace | |
| for namespaceURI, qname in [ | |
| ("http://xml.python.org/ns", "xmlns"), | |
| ("http://xml.python.org/ns", "xmlns:p"), | |
| (None, "xmlns:p"), | |
| (XMLNS_NS, "p:a"), # and it allows nothing else | |
| (XMLNS_NS, "a"), | |
| ]: | |
| with self.subTest(namespaceURI=namespaceURI, qname=qname): | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| doc.createAttributeNS, namespaceURI, qname) | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| elem.setAttributeNS, namespaceURI, qname, "v") | |
| # valid combinations | |
| doc.createElementNS(None, "e") | |
| doc.createElementNS("http://xml.python.org/ns", "p:e") | |
| doc.createElementNS(XML_NS, "xml:e") | |
| doc.createAttributeNS(None, "a") | |
| doc.createAttributeNS(XML_NS, "xml:lang") | |
| doc.createAttributeNS(XMLNS_NS, "xmlns") | |
| doc.createAttributeNS(XMLNS_NS, "xmlns:p") | |
| elem.setAttributeNS("http://xml.python.org/ns", "p:a", "v") | |
| doc.unlink() | |
| def testAttrPrefix(self): | |
| doc = parseString("<doc/>") | |
| attr = doc.createAttributeNS("http://xml.python.org/ns", "p:a") | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| setattr, attr, "prefix", "q:r") | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| setattr, attr, "prefix", "1q") | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| setattr, attr, "prefix", "xml") | |
| self.assertRaises(xml.dom.NamespaceErr, | |
| setattr, attr, "prefix", "xmlns") | |
| attr.prefix = "q" | |
| self.assertEqual(attr.name, "q:a") | |
| attr.prefix = None | |
| self.assertEqual(attr.name, "a") | |
| doc.unlink() | |
| def testInvalidCharacterErr(self): | |
| doc = parseString("<doc/>") | |
| impl = getDOMImplementation() | |
| for name in ("", "bad name", "1st", "-x", ".x", "a<b", "a&b", "a\tb"): | |
| with self.subTest(name=name): | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createElement, name) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createElementNS, None, name) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createAttribute, name) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createAttributeNS, None, name) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createProcessingInstruction, name, "") | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.createEntityReference, name) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| impl.createDocumentType, name, None, None) | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.documentElement.setAttribute, name, "v") | |
| self.assertRaises(xml.dom.InvalidCharacterErr, | |
| doc.documentElement.setAttributeNS, | |
| None, name, "v") | |
| for name in ("a", "_x", ":x", "a.b-c", "ns:tag", "a1", | |
| "\N{GREEK CAPITAL LETTER OMEGA}", "\N{LINEAR B SYLLABLE B008 A}x"): | |
| with self.subTest(name=name): | |
| self.assertEqual(doc.createElement(name).tagName, name) | |
| self.assertEqual(doc.createAttribute(name).name, name) | |
| doc.unlink() | |
| def testWrongDocumentErr(self): | |
| doc = parseString("<doc><child/></doc>") | |
| other = parseString("<other/>") | |
| elem = doc.documentElement | |
| alien = other.createElement("alien") | |
| self.assertRaises(xml.dom.WrongDocumentErr, elem.appendChild, alien) | |
| self.assertRaises(xml.dom.WrongDocumentErr, elem.insertBefore, | |
| alien, elem.firstChild) | |
| self.assertRaises(xml.dom.WrongDocumentErr, elem.replaceChild, | |
| alien, elem.firstChild) | |
| self.assertRaises(xml.dom.WrongDocumentErr, doc.appendChild, alien) | |
| # the rejected node is left alone | |
| self.assertIs(alien.ownerDocument, other) | |
| self.assertIsNone(alien.parentNode) | |
| # importNode() is the supported way to do this | |
| elem.appendChild(doc.importNode(alien, True)) | |
| self.assertEqual(elem.lastChild.tagName, "alien") | |
| doc.unlink() | |
| other.unlink() | |
| def testAncestorLoops(self): | |
| doc = parseString("<doc><child><grandchild/></child></doc>") | |
| elem = doc.documentElement | |
| child = elem.firstChild | |
| grandchild = child.firstChild | |
| for node in elem, child, grandchild: | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| node.appendChild, node) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, child.appendChild, elem) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| grandchild.appendChild, elem) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| grandchild.insertBefore, child, None) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| grandchild.replaceChild, elem, None) | |
| # the tree is unchanged | |
| self.assertIs(child.parentNode, elem) | |
| self.assertIs(grandchild.parentNode, child) | |
| doc.unlink() | |
| def testOnlyOneElementAndDocumentType(self): | |
| impl = getDOMImplementation() | |
| doc = impl.createDocument(None, "root", None) | |
| elem = doc.documentElement | |
| comment = doc.appendChild(doc.createComment("c")) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| doc.appendChild, doc.createElement("x")) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| doc.insertBefore, doc.createElement("x"), comment) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| doc.replaceChild, doc.createElement("x"), comment) | |
| # replacing the document element and reordering it are allowed | |
| other = doc.createElement("other") | |
| doc.replaceChild(other, elem) | |
| self.assertIs(doc.documentElement, other) | |
| doc.appendChild(other) | |
| self.assertEqual([n.nodeName for n in doc.childNodes], | |
| ["#comment", "other"]) | |
| doc2 = impl.createDocument(None, None, None) | |
| doctype = impl.createDocumentType("a", None, None) | |
| doc2.appendChild(doctype) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, doc2.appendChild, | |
| impl.createDocumentType("b", None, None)) | |
| # re-adding the same node and replacing it are allowed | |
| doc2.appendChild(doctype) | |
| doc2.replaceChild(impl.createDocumentType("b", None, None), doctype) | |
| doc.unlink() | |
| doc2.unlink() | |
| def testDocumentFragmentChildren(self): | |
| doc = parseString('<!DOCTYPE doc [<!NOTATION n SYSTEM "n">]><doc/>') | |
| frag = doc.createDocumentFragment() | |
| notation = doc.doctype.notations.item(0) | |
| self.assertRaises(xml.dom.HierarchyRequestErr, | |
| frag.appendChild, notation) | |
| frag.appendChild(doc.createElement("e")) | |
| frag.appendChild(doc.createTextNode("t")) | |
| frag.appendChild(doc.createComment("c")) | |
| self.assertEqual(len(frag.childNodes), 3) | |
| doc.unlink() | |
| def testSetAttributeNodeWrongDocument(self): | |
| doc = parseString("<doc a='v'/>") | |
| other = parseString("<other b='w'/>") | |
| elem = doc.documentElement | |
| self.assertRaises(xml.dom.WrongDocumentErr, elem.setAttributeNode, | |
| other.createAttribute("z")) | |
| self.assertRaises(xml.dom.WrongDocumentErr, elem.setAttributeNodeNS, | |
| other.createAttributeNS(None, "z")) | |
| # an imported attribute belongs to this document and is accepted | |
| elem.setAttributeNode(doc.importNode(other.createAttribute("z"), True)) | |
| self.assertTrue(elem.hasAttribute("z")) | |
| # re-setting an own attribute is not an error | |
| elem.setAttributeNode(elem.getAttributeNode("a")) | |
| # the same checks are applied in NamedNodeMap.setNamedItem() | |
| attrs = elem.attributes | |
| self.assertRaises(xml.dom.WrongDocumentErr, attrs.setNamedItem, | |
| other.createAttribute("y")) | |
| self.assertRaises(xml.dom.WrongDocumentErr, attrs.setNamedItemNS, | |
| other.createAttributeNS(None, "y")) | |
| attrs.setNamedItem(doc.createAttribute("y")) | |
| self.assertTrue(elem.hasAttribute("y")) | |
| # an attribute of another element of the same document is in use | |
| doc2 = parseString("<doc><a x='v'/><b/></doc>") | |
| a, b = doc2.documentElement.childNodes | |
| self.assertRaises(xml.dom.InuseAttributeErr, b.attributes.setNamedItem, | |
| a.getAttributeNode("x")) | |
| doc2.unlink() | |
| doc.unlink() | |
| other.unlink() | |
| def testAttrSpecified(self): | |
| doc = parseString("<!DOCTYPE doc [" | |
| " <!ELEMENT doc EMPTY>" | |
| " <!ATTLIST doc a CDATA 'default' b CDATA #IMPLIED>" | |
| "]><doc b='given'/>") | |
| elem = doc.documentElement | |
| # attributes defaulted from the DTD are reported too | |
| self.assertEqual(sorted(elem.attributes.keys()), ["a", "b"]) | |
| self.assertEqual(elem.getAttribute("a"), "default") | |
| self.assertFalse(elem.getAttributeNode("a").specified) | |
| self.assertEqual(elem.getAttribute("b"), "given") | |
| self.assertTrue(elem.getAttributeNode("b").specified) | |
| doc.unlink() | |
| def testEntityReference(self): | |
| doc = parseString("<doc/>") | |
| ref = doc.createEntityReference("ent") | |
| self.assertEqual(ref.nodeType, Node.ENTITY_REFERENCE_NODE) | |
| self.assertEqual(ref.nodeName, "ent") | |
| self.assertIsNone(ref.nodeValue) | |
| self.assertIs(ref.ownerDocument, doc) | |
| doc.documentElement.appendChild(ref) | |
| self.assertEqual(doc.documentElement.toxml(), "<doc>&ent;</doc>") | |
| # entity reference nodes are read-only | |
| text = doc.createTextNode("x") | |
| self.assertRaises(xml.dom.NoModificationAllowedErr, | |
| ref.appendChild, text) | |
| self.assertRaises(xml.dom.NoModificationAllowedErr, | |
| ref.insertBefore, text, None) | |
| self.assertRaises(xml.dom.NoModificationAllowedErr, | |
| ref.removeChild, text) | |
| self.assertRaises(xml.dom.NoModificationAllowedErr, | |
| ref.replaceChild, text, None) | |
| self.assertEqual(ref.cloneNode(True).nodeName, "ent") | |
| other = parseString("<other/>") | |
| self.assertEqual(other.importNode(ref, True).nodeName, "ent") | |
| doc.unlink() | |
| other.unlink() | |
| if __name__ == "__main__": | |
| unittest.main() |