Skip to content

Commit 8d816b9

Browse files
committed
Use the document factory methods when cloning nodes
Clone nodes without a namespace with createElement(), setAttribute() and createAttribute(), and namespaced nodes with the NS variants, so that a Document subclass can create its own node classes and the clone keeps the prefix of the original. The tests now compare the names of the clone with those of the original.
1 parent 54a3ade commit 8d816b9

2 files changed

Lines changed: 44 additions & 52 deletions

File tree

‎Lib/test/test_minidom.py‎

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,55 +1286,42 @@ def create_dom1_element(self):
12861286
root.setAttribute("xmlns", "http://xml.python.org/default")
12871287
root.setAttribute("xmlns:xsi", "http://xml.python.org/xsi")
12881288
root.setAttribute("xsi:type", "t")
1289+
root.setAttributeNS("http://xml.python.org/ns", "p:a", "v")
12891290
root.appendChild(doc.createElement("svg:rect"))
1291+
root.appendChild(
1292+
doc.createElementNS("http://xml.python.org/ns", "p:b"))
12901293
return doc, root
12911294

1292-
def check_dom1_names(self, node, nodeName, prefix, localName):
1293-
self.assertEqual(node.nodeName, nodeName)
1294-
self.assertEqual(node.prefix, prefix)
1295-
self.assertEqual(node.localName, localName)
1296-
self.assertIsNone(node.namespaceURI)
1297-
1298-
def check_dom1_element(self, elem, doc):
1299-
self.assertIs(elem.ownerDocument, doc)
1300-
self.check_dom1_names(elem, "root", None, "root")
1301-
self.assertEqual(len(elem.attributes), 3)
1302-
for name, prefix, localName, value in [
1303-
("xmlns", None, "xmlns", "http://xml.python.org/default"),
1304-
("xmlns:xsi", "xmlns", "xsi", "http://xml.python.org/xsi"),
1305-
("xsi:type", "xsi", "type", "t"),
1306-
]:
1307-
with self.subTest(name=name):
1308-
attr = elem.getAttributeNode(name)
1309-
self.check_dom1_names(attr, name, prefix, localName)
1310-
self.assertEqual(attr.value, value)
1311-
self.assertIs(attr.ownerElement, elem)
1312-
self.assertIs(attr.ownerDocument, doc)
1313-
self.assertEqual(len(elem.childNodes), 1)
1314-
self.check_dom1_names(elem.firstChild, "svg:rect", "svg", "rect")
1315-
self.assertIs(elem.firstChild.ownerDocument, doc)
1295+
def check_clone_names(self, clone, node):
1296+
for name in ("nodeName", "prefix", "localName", "namespaceURI"):
1297+
self.assertEqual(getattr(clone, name), getattr(node, name), name)
1298+
1299+
def check_clone_element(self, clone, elem, doc):
1300+
self.assertIs(clone.ownerDocument, doc)
1301+
self.assertIsNone(clone.parentNode)
1302+
self.check_clone_names(clone, elem)
1303+
self.assertEqual(len(clone.attributes), len(elem.attributes))
1304+
for attr in elem.attributes.values():
1305+
with self.subTest(attr=attr.name):
1306+
a = clone.getAttributeNode(attr.name)
1307+
self.check_clone_names(a, attr)
1308+
self.assertEqual(a.value, attr.value)
1309+
self.assertIs(a.ownerElement, clone)
1310+
self.assertEqual(len(clone.childNodes), len(elem.childNodes))
1311+
for c, child in zip(clone.childNodes, elem.childNodes):
1312+
self.check_clone_names(c, child)
13161313

13171314
def testCloneElementDOM1Names(self):
13181315
doc, root = self.create_dom1_element()
1319-
clone = root.cloneNode(True)
1320-
self.assertIsNot(clone, root)
1321-
self.assertIsNone(clone.parentNode)
1322-
self.check_dom1_element(clone, doc)
1316+
self.check_clone_element(root.cloneNode(True), root, doc)
13231317
doc.unlink()
13241318

13251319
def testCloneElementNoNamespaces(self):
13261320
doc = xml.dom.expatbuilder.parseString(
13271321
"<a:b xmlns:a='http://xml.python.org/a' a:x='1'><a:c/></a:b>",
13281322
namespaces=False)
1329-
clone = doc.documentElement.cloneNode(True)
1330-
self.check_dom1_names(clone, "a:b", "a", "b")
1331-
self.assertEqual(clone.getAttribute("xmlns:a"),
1332-
"http://xml.python.org/a")
1333-
self.check_dom1_names(clone.getAttributeNode("xmlns:a"),
1334-
"xmlns:a", "xmlns", "a")
1335-
self.assertEqual(clone.getAttribute("a:x"), "1")
1336-
self.check_dom1_names(clone.getAttributeNode("a:x"), "a:x", "a", "x")
1337-
self.check_dom1_names(clone.firstChild, "a:c", "a", "c")
1323+
root = doc.documentElement
1324+
self.check_clone_element(root.cloneNode(True), root, doc)
13381325
doc.unlink()
13391326

13401327
def testCloneDocumentShallow(self):
@@ -1458,9 +1445,7 @@ def testImportDocumentTypeDeep(self):
14581445
def testImportElementDOM1Names(self):
14591446
doc1, root = self.create_dom1_element()
14601447
doc2 = Document()
1461-
imported = doc2.importNode(root, True)
1462-
self.assertIsNone(imported.parentNode)
1463-
self.check_dom1_element(imported, doc2)
1448+
self.check_clone_element(doc2.importNode(root, True), root, doc2)
14641449
doc1.unlink()
14651450
doc2.unlink()
14661451

@@ -1496,7 +1481,7 @@ def testCloneAttributeDOM1Names(self):
14961481
("importNode", doc2.importNode(attr, True), doc2),
14971482
]:
14981483
with self.subTest(method=method):
1499-
self.check_dom1_names(clone, "a:b", "a", "b")
1484+
self.check_clone_names(clone, attr)
15001485
self.assertEqual(clone.value, "v")
15011486
self.assertTrue(clone.specified)
15021487
self.assertIsNone(clone.ownerElement)

‎Lib/xml/dom/minidom.py‎

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,15 +2169,20 @@ def _clone_node(node, deep, newOwnerDocument):
21692169
else:
21702170
operation = xml.dom.UserDataHandler.NODE_IMPORTED
21712171
if node.nodeType == Node.ELEMENT_NODE:
2172-
prefix, localName = _nssplit(node.nodeName)
2173-
clone = Element(node.nodeName, node.namespaceURI, prefix)
2174-
clone.ownerDocument = newOwnerDocument
2172+
if node.namespaceURI is None:
2173+
clone = newOwnerDocument.createElement(node.nodeName)
2174+
else:
2175+
clone = newOwnerDocument.createElementNS(node.namespaceURI,
2176+
node.nodeName)
21752177
for attr in node.attributes.values():
2176-
prefix, localName = _nssplit(attr.nodeName)
2177-
a = Attr(attr.nodeName, attr.namespaceURI, localName, prefix)
2178-
a.value = attr.value
2179-
a.ownerDocument = newOwnerDocument
2180-
clone.setAttributeNode(a)
2178+
if attr.namespaceURI is None:
2179+
clone.setAttribute(attr.nodeName, attr.value)
2180+
a = clone.getAttributeNode(attr.nodeName)
2181+
else:
2182+
clone.setAttributeNS(attr.namespaceURI, attr.nodeName,
2183+
attr.value)
2184+
a = clone.getAttributeNodeNS(attr.namespaceURI,
2185+
attr.localName)
21812186
a.specified = attr.specified
21822187

21832188
if deep:
@@ -2204,9 +2209,11 @@ def _clone_node(node, deep, newOwnerDocument):
22042209
elif node.nodeType == Node.ENTITY_REFERENCE_NODE:
22052210
clone = newOwnerDocument.createEntityReference(node.nodeName)
22062211
elif node.nodeType == Node.ATTRIBUTE_NODE:
2207-
prefix, localName = _nssplit(node.nodeName)
2208-
clone = Attr(node.nodeName, node.namespaceURI, localName, prefix)
2209-
clone.ownerDocument = newOwnerDocument
2212+
if node.namespaceURI is None:
2213+
clone = newOwnerDocument.createAttribute(node.nodeName)
2214+
else:
2215+
clone = newOwnerDocument.createAttributeNS(node.namespaceURI,
2216+
node.nodeName)
22102217
clone.specified = True
22112218
clone.value = node.value
22122219
elif node.nodeType == Node.DOCUMENT_TYPE_NODE:

0 commit comments

Comments
 (0)