Skip to content

bpo-13127: Fix attr name setter behavior #14757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
10 changes: 10 additions & 0 deletions Lib/test/test_minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,16 @@ def checkRenameNodeSharedConstraints(self, doc, node):
self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
xml.dom.EMPTY_NAMESPACE, "foo")

def testAttributeNameSetter(self):
doc = parseString("<doc a='v'/>")
elem = doc.documentElement
attrmap = elem.attributes
attr = elem.attributes['a']

attr.name = "b"
self.confirm(attr.name == "b" and attr.nodeName == "b")
self.assertEqual(doc.toxml(), '<?xml version="1.0" ?><doc b="v"/>')

def testRenameAttribute(self):
doc = parseString("<doc a='v'/>")
elem = doc.documentElement
Expand Down
84 changes: 47 additions & 37 deletions Lib/xml/dom/minidom.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,23 @@ def _get_specified(self):
def _get_name(self):
return self._name

def _set_name(self, value):
self._name = value
def _set_name(self, name, namespaceURI=EMPTY_NAMESPACE):
is_id = self._is_id

if self.ownerElement is not None:
_clear_id_cache(self.ownerElement)
self.ownerElement.removeAttributeNode(self)

prefix, localName = _parseName(namespaceURI, name, self.nodeType)

self._name = name
self._prefix = prefix
self._localName = localName
self.namespaceURI = namespaceURI

if self.ownerElement is not None:
self.ownerElement.setAttributeNode(self)
if is_id:
self.ownerElement.setIdAttributeNode(self)

nodeName = name = property(_get_name, _set_name)

Expand Down Expand Up @@ -1806,44 +1819,19 @@ def renameNode(self, n, namespaceURI, name):
if n.nodeType not in (Node.ELEMENT_NODE, Node.ATTRIBUTE_NODE):
raise xml.dom.NotSupportedErr(
"renameNode() only applies to element and attribute nodes")
if namespaceURI != EMPTY_NAMESPACE:
if ':' in name:
prefix, localName = name.split(':', 1)
if ( prefix == "xmlns"
and namespaceURI != xml.dom.XMLNS_NAMESPACE):
raise xml.dom.NamespaceErr(
"illegal use of 'xmlns' prefix")
else:
if ( name == "xmlns"
and namespaceURI != xml.dom.XMLNS_NAMESPACE
and n.nodeType == Node.ATTRIBUTE_NODE):
raise xml.dom.NamespaceErr(
"illegal use of the 'xmlns' attribute")
prefix = None
localName = name
else:
prefix = None
localName = None
if n.nodeType == Node.ATTRIBUTE_NODE:
element = n.ownerElement
if element is not None:
is_id = n._is_id
element.removeAttributeNode(n)
else:
element = None
n.prefix = prefix
n._localName = localName
n.namespaceURI = namespaceURI
n.nodeName = name

prefix, localName = _parseName(namespaceURI, name, n.nodeType)

if n.nodeType == Node.ELEMENT_NODE:
n.prefix = prefix
n._localName = localName
n.namespaceURI = namespaceURI
n.nodeName = name
n.tagName = name
else:
# attribute node
n.name = name
if element is not None:
element.setAttributeNode(n)
if is_id:
element.setIdAttributeNode(n)
n._set_name(name, namespaceURI)

# It's not clear from a semantic perspective whether we should
# call the user data handlers for the NODE_RENAMED event since
# we're re-using the existing node. The draft spec has been
Expand All @@ -1855,6 +1843,28 @@ def renameNode(self, n, namespaceURI, name):
doc="Top-level element of this document.")


def _parseName(namespaceURI, name, nodeType):
if namespaceURI == EMPTY_NAMESPACE:
return None, None

if ':' in name:
prefix, localName = name.split(':', 1)
if ( prefix == "xmlns"
and namespaceURI != xml.dom.XMLNS_NAMESPACE):
raise xml.dom.NamespaceErr(
"illegal use of 'xmlns' prefix")
else:
if ( name == "xmlns"
and namespaceURI != xml.dom.XMLNS_NAMESPACE
and nodeType == Node.ATTRIBUTE_NODE):
raise xml.dom.NamespaceErr(
"illegal use of the 'xmlns' attribute")
prefix = None
localName = name

return prefix, localName


def _clone_node(node, deep, newOwnerDocument):
"""
Clone a node and give it the new owner document.
Expand Down