Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions Lib/test/test_minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1277,6 +1278,52 @@ def testCloneElementDeep(self):
self.assertTrue(clone.toxml(), '<doc attr="value"><foo/></doc>')
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(
"<a:b xmlns:a='http://xml.python.org/a' a:x='1'><a:c/></a:b>",
namespaces=False)
root = doc.documentElement
self.check_clone_element(root.cloneNode(True), root, doc)
doc.unlink()

def testCloneDocumentShallow(self):
doc = parseString("<?xml version='1.0'?>\n"
"<!-- comment -->"
Expand Down Expand Up @@ -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):
Expand All @@ -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("<?target data?><doc/>")
pi = doc.firstChild
Expand Down
24 changes: 18 additions & 6 deletions Lib/xml/dom/minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
Loading