From 30eb4290d8e193114d3780d1bf46a0416ba89bc8 Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Sat, 17 Oct 2026 21:43:26 +0300 Subject: [PATCH] gh-159637: Fix renaming an attribute to a prefixed name in minidom Document.renameNode() set the new prefix of an attribute before its new namespace, so the prefix was validated against the old namespace. Renaming an attribute with no namespace to a prefixed name in a namespace raised NamespaceErr after the attribute had been removed from its element. Set the namespace first, and check the prefix before removing the attribute, so that a rejected rename leaves the element unchanged. --- Lib/test/test_minidom.py | 22 ++++++++++++++++++++++ Lib/xml/dom/minidom.py | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 446bbe096bd19d..b5028ee126ccee 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1880,6 +1880,28 @@ def testRenameAttribute(self): self.checkRenameNodeSharedConstraints(doc, attr) doc.unlink() + def testRenameAttributeToPrefixedName(self): + doc = parseString("") + elem = doc.documentElement + attr = elem.getAttributeNode("a") + attr = doc.renameNode(attr, "http://xml.python.org/ns", "p:c") + self.assertEqual(attr.name, "p:c") + self.assertEqual(attr.prefix, "p") + self.assertEqual(attr.localName, "c") + self.assertEqual(attr.namespaceURI, "http://xml.python.org/ns") + self.assertIsNone(elem.getAttributeNode("a")) + self.assertIs(elem.getAttributeNodeNS("http://xml.python.org/ns", "c"), + attr) + + # A rejected rename leaves the attribute on its element. + attr = elem.getAttributeNode("b") + self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, + "http://xml.python.org/ns", "xml:lang") + self.assertEqual(attr.name, "b") + self.assertIsNone(attr.namespaceURI) + self.assertIs(elem.getAttributeNode("b"), attr) + doc.unlink() + def testRenameElement(self): doc = parseString("") elem = doc.documentElement diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 7cb652a323dcc2..5ab58c76f032ad 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -2129,15 +2129,17 @@ def renameNode(self, n, namespaceURI, name): prefix = None localName = None if n.nodeType == Node.ATTRIBUTE_NODE: + if prefix is not None: + _check_prefix(prefix, namespaceURI, True) element = n.ownerElement if element is not None: is_id = n._is_id element.removeAttributeNode(n) else: element = None + n.namespaceURI = namespaceURI n.prefix = prefix n._localName = localName - n.namespaceURI = namespaceURI n.nodeName = name if n.nodeType == Node.ELEMENT_NODE: n.tagName = name