Skip to content
Merged
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
33 changes: 32 additions & 1 deletion Lib/test/test_minidom.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,14 +2,15 @@

importcopy
importpickle
importtime
importio
fromtestimportsupport
importunittest

importpyexpat
importxml.dom.minidom

fromxml.dom.minidomimportparse, Attr, Node, Document, parseString
fromxml.dom.minidomimportparse, Attr, Node, Document, Element, parseString
fromxml.dom.minidomimportgetDOMImplementation
fromxml.parsers.expatimportExpatError

Expand DownExpand Up@@ -177,6 +178,36 @@ def testAppendChild(self):
self.confirm(dom.documentElement.childNodes[-1].data=="Hello")
dom.unlink()

@support.requires_resource('cpu')
deftestAppendChildNoQuadraticComplexity(self):
impl=getDOMImplementation()

newdoc=impl.createDocument(None, "some_tag", None)
top_element=newdoc.documentElement
children= [newdoc.createElement(f"child-{i}") foriinrange(1, 2**15+1)]
element=top_element

start=time.monotonic()
forchildinchildren:
element.appendChild(child)
element=child
end=time.monotonic()

# This example used to take at least 30 seconds.
# Conservative assertion due to the wide variety of systems and
# build configs timing based tests wind up run under.
# A --with-address-sanitizer --with-pydebug build on a rpi5 still
# completes this loop in <0.5 seconds.
self.assertLess(end-start, 4)

deftestSetAttributeNodeWithoutOwnerDocument(self):
# regression test for gh-142754
elem=Element("test")
attr=Attr("id")
attr.value="test-id"
elem.setAttributeNode(attr)
self.assertEqual(elem.getAttribute("id"), "test-id")

deftestAppendChildFragment(self):
dom, orig, c1, c2, c3, frag=self._create_fragment_test_nodes()
dom.documentElement.appendChild(frag)
Expand Down
11 changes: 3 additions & 8 deletions Lib/xml/dom/minidom.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -292,13 +292,6 @@ def _append_child(self, node):
childNodes.append(node)
node.parentNode=self

def_in_document(node):
# return True iff node is part of a document tree
whilenodeisnotNone:
ifnode.nodeType==Node.DOCUMENT_NODE:
returnTrue
node=node.parentNode
returnFalse

def_write_data(writer, data):
"Writes datachars to writer."
Expand DownExpand Up@@ -355,6 +348,7 @@ class Attr(Node):
def__init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
prefix=None):
self.ownerElement=None
self.ownerDocument=None
self._name=qName
self.namespaceURI=namespaceURI
self._prefix=prefix
Expand DownExpand Up@@ -680,6 +674,7 @@ class Element(Node):

def__init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
localName=None):
self.ownerDocument=None
self.parentNode=None
self.tagName=self.nodeName=tagName
self.prefix=prefix
Expand DownExpand Up@@ -1539,7 +1534,7 @@ def _clear_id_cache(node):
ifnode.nodeType==Node.DOCUMENT_NODE:
node._id_cache.clear()
node._id_search_stack=None
elif_in_document(node):
elifnode.ownerDocument:
node.ownerDocument._id_cache.clear()
node.ownerDocument._id_search_stack=None

Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
Remove quadratic behavior in ``xml.minidom`` node ID cache clearing. In order
to do this without breaking existing users, we also add the *ownerDocument*
attribute to :mod:`xml.dom.minidom` elements and attributes created by directly
instantiating the ``Element`` or ``Attr`` class. Note that this way of creating
nodes is not supported; creator functions like
:py:meth:`xml.dom.Document.documentElement` should be used instead.
Loading