diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 446bbe096bd19dc..c542eb10b2784d9 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -6,6 +6,7 @@ from test import support import unittest +import xml.dom.expatbuilder import xml.dom.minidom from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString @@ -1277,6 +1278,52 @@ def testCloneElementDeep(self): self.assertTrue(clone.toxml(), '') dom.unlink() + def create_dom1_element(self): + # DOM Level 1 nodes have no namespace, but their names can contain + # a colon. + doc = Document() + root = doc.appendChild(doc.createElement("root")) + root.setAttribute("xmlns", "http://xml.python.org/default") + root.setAttribute("xmlns:xsi", "http://xml.python.org/xsi") + root.setAttribute("xsi:type", "t") + root.setAttributeNS("http://xml.python.org/ns", "p:a", "v") + root.appendChild(doc.createElement("svg:rect")) + root.appendChild( + doc.createElementNS("http://xml.python.org/ns", "p:b")) + return doc, root + + def check_clone_names(self, clone, node): + for name in ("nodeName", "prefix", "localName", "namespaceURI"): + self.assertEqual(getattr(clone, name), getattr(node, name), name) + + def check_clone_element(self, clone, elem, doc): + self.assertIs(clone.ownerDocument, doc) + self.assertIsNone(clone.parentNode) + self.check_clone_names(clone, elem) + self.assertEqual(len(clone.attributes), len(elem.attributes)) + for attr in elem.attributes.values(): + with self.subTest(attr=attr.name): + a = clone.getAttributeNode(attr.name) + self.check_clone_names(a, attr) + self.assertEqual(a.value, attr.value) + self.assertIs(a.ownerElement, clone) + self.assertEqual(len(clone.childNodes), len(elem.childNodes)) + for c, child in zip(clone.childNodes, elem.childNodes): + self.check_clone_names(c, child) + + def testCloneElementDOM1Names(self): + doc, root = self.create_dom1_element() + self.check_clone_element(root.cloneNode(True), root, doc) + doc.unlink() + + def testCloneElementNoNamespaces(self): + doc = xml.dom.expatbuilder.parseString( + "", + namespaces=False) + root = doc.documentElement + self.check_clone_element(root.cloneNode(True), root, doc) + doc.unlink() + def testCloneDocumentShallow(self): doc = parseString("\n" "" @@ -1395,6 +1442,13 @@ def testImportDocumentTypeDeep(self): self.assertRaises(xml.dom.NotSupportedErr, target.importNode, src.doctype, 1) + def testImportElementDOM1Names(self): + doc1, root = self.create_dom1_element() + doc2 = Document() + self.check_clone_element(doc2.importNode(root, True), root, doc2) + doc1.unlink() + doc2.unlink() + # 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): @@ -1417,6 +1471,24 @@ def testCloneAttributeShallow(self): def testCloneAttributeDeep(self): self.check_clone_attribute(1, "testCloneAttributeDeep") + def testCloneAttributeDOM1Names(self): + doc1 = Document() + attr = doc1.createAttribute("a:b") + attr.value = "v" + doc2 = Document() + for method, clone, owner in [ + ("cloneNode", attr.cloneNode(True), doc1), + ("importNode", doc2.importNode(attr, True), doc2), + ]: + with self.subTest(method=method): + self.check_clone_names(clone, attr) + self.assertEqual(clone.value, "v") + self.assertTrue(clone.specified) + self.assertIsNone(clone.ownerElement) + self.assertIs(clone.ownerDocument, owner) + doc1.unlink() + doc2.unlink() + def check_clone_pi(self, deep, testName): doc = parseString("") pi = doc.firstChild diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 7cb652a323dcc22..f4d544e08bd3e94 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -2169,11 +2169,20 @@ def _clone_node(node, deep, newOwnerDocument): else: operation = xml.dom.UserDataHandler.NODE_IMPORTED if node.nodeType == Node.ELEMENT_NODE: - clone = newOwnerDocument.createElementNS(node.namespaceURI, - node.nodeName) + if node.namespaceURI is None: + clone = newOwnerDocument.createElement(node.nodeName) + else: + clone = newOwnerDocument.createElementNS(node.namespaceURI, + node.nodeName) for attr in node.attributes.values(): - clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value) - a = clone.getAttributeNodeNS(attr.namespaceURI, attr.localName) + if attr.namespaceURI is None: + clone.setAttribute(attr.nodeName, attr.value) + a = clone.getAttributeNode(attr.nodeName) + else: + clone.setAttributeNS(attr.namespaceURI, attr.nodeName, + attr.value) + a = clone.getAttributeNodeNS(attr.namespaceURI, + attr.localName) a.specified = attr.specified if deep: @@ -2200,8 +2209,11 @@ def _clone_node(node, deep, newOwnerDocument): elif node.nodeType == Node.ENTITY_REFERENCE_NODE: clone = newOwnerDocument.createEntityReference(node.nodeName) elif node.nodeType == Node.ATTRIBUTE_NODE: - clone = newOwnerDocument.createAttributeNS(node.namespaceURI, - node.nodeName) + if node.namespaceURI is None: + clone = newOwnerDocument.createAttribute(node.nodeName) + else: + clone = newOwnerDocument.createAttributeNS(node.namespaceURI, + node.nodeName) clone.specified = True clone.value = node.value elif node.nodeType == Node.DOCUMENT_TYPE_NODE: