Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9170c74

Browse files
frenzymadnessjacobtylerwallssethmlarsonencukouhugovk
authored andcommitted
* pythongh-142145: Remove quadratic behavior in node ID cache clearing (pythonGH-142146) * pythongh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (pythonGH-142794) (cherry picked from commit 1cc7551) (cherry picked from commit 08d8e18) (cherry picked from commit 8d2d7bb) Co-authored-by: Jacob Walls <[email protected]> Co-authored-by: Seth Michael Larson <[email protected]> Co-authored-by: Petr Viktorin <[email protected]> Co-authored-by: Hugo van Kemenade <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]> Co-authored-by: Hugo van Kemenade <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]> Co-authoded-by: Lumír Balhar <[email protected]>
1 parent fad58bc commit 9170c74

3 files changed

Lines changed: 40 additions & 9 deletions

File tree

Lib/test/test_minidom.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pyexpat
99
import xml.dom.minidom
1010

11-
from xml.dom.minidom import parse, Node, Document, parseString
11+
from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString
1212
from xml.dom.minidom import getDOMImplementation
1313
from xml.parsers.expat import ExpatError
1414

@@ -172,6 +172,36 @@ def testAppendChild(self):
172172
self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
173173
dom.unlink()
174174

175+
@support.requires_resource('cpu')
176+
def testAppendChildNoQuadraticComplexity(self):
177+
impl = getDOMImplementation()
178+
179+
newdoc = impl.createDocument(None, "some_tag", None)
180+
top_element = newdoc.documentElement
181+
children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
182+
element = top_element
183+
184+
start = time.monotonic()
185+
for child in children:
186+
element.appendChild(child)
187+
element = child
188+
end = time.monotonic()
189+
190+
# This example used to take at least 30 seconds.
191+
# Conservative assertion due to the wide variety of systems and
192+
# build configs timing based tests wind up run under.
193+
# A --with-address-sanitizer --with-pydebug build on a rpi5 still
194+
# completes this loop in <0.5 seconds.
195+
self.assertLess(end - start, 4)
196+
197+
def testSetAttributeNodeWithoutOwnerDocument(self):
198+
# regression test for gh-142754
199+
elem = Element("test")
200+
attr = Attr("id")
201+
attr.value = "test-id"
202+
elem.setAttributeNode(attr)
203+
self.assertEqual(elem.getAttribute("id"), "test-id")
204+
175205
def testAppendChildFragment(self):
176206
dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
177207
dom.documentElement.appendChild(frag)

Lib/xml/dom/minidom.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,6 @@ def _append_child(self, node):
291291
childNodes.append(node)
292292
node.parentNode = self
293293

294-
def _in_document(node):
295-
# return True iff node is part of a document tree
296-
while node is not None:
297-
if node.nodeType == Node.DOCUMENT_NODE:
298-
return True
299-
node = node.parentNode
300-
return False
301294

302295
def _write_data(writer, data):
303296
"Writes datachars to writer."
@@ -354,6 +347,7 @@ class Attr(Node):
354347
def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
355348
prefix=None):
356349
self.ownerElement = None
350+
self.ownerDocument = None
357351
self._name = qName
358352
self.namespaceURI = namespaceURI
359353
self._prefix = prefix
@@ -677,6 +671,7 @@ class Element(Node):
677671

678672
def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
679673
localName=None):
674+
self.ownerDocument = None
680675
self.parentNode = None
681676
self.tagName = self.nodeName = tagName
682677
self.prefix = prefix
@@ -1512,7 +1507,7 @@ def _clear_id_cache(node):
15121507
if node.nodeType == Node.DOCUMENT_NODE:
15131508
node._id_cache.clear()
15141509
node._id_search_stack = None
1515-
elif _in_document(node):
1510+
elif node.ownerDocument:
15161511
node.ownerDocument._id_cache.clear()
15171512
node.ownerDocument._id_search_stack= None
15181513

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

0 commit comments

Comments
 (0)