From f4eb9ab014545b521fb261b80adfa6d138e7e092 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Wed, 3 Dec 2025 01:16:37 -0600 Subject: [PATCH] gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146) * Remove quadratic behavior in node ID cache clearing Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com> * Add news fragment --------- (cherry picked from commit 08d8e18ad81cd45bc4a27d6da478b51ea49486e4) Co-authored-by: Seth Michael Larson Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com> --- Lib/test/test_minidom.py | 18 ++++++++++ Lib/xml/dom/minidom.py | 9 ----- Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst | 1 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst Index: Python-3.9.25/Lib/test/test_minidom.py =================================================================== --- Python-3.9.25.orig/Lib/test/test_minidom.py 2025-12-19 23:24:47.176384491 +0100 +++ Python-3.9.25/Lib/test/test_minidom.py 2025-12-19 23:27:27.634483015 +0100 @@ -2,6 +2,7 @@ import copy import pickle +import time import io from test import support import unittest @@ -162,6 +163,23 @@ self.confirm(dom.documentElement.childNodes[-1].data == "Hello") dom.unlink() + def testAppendChildNoQuadraticComplexity(self): + impl = getDOMImplementation() + + newdoc = impl.createDocument(None, "some_tag", None) + top_element = newdoc.documentElement + children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)] + element = top_element + + start = time.time() + for child in children: + element.appendChild(child) + element = child + end = time.time() + + # This example used to take at least 30 seconds. + self.assertLess(end - start, 1) + def testAppendChildFragment(self): dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.appendChild(frag) Index: Python-3.9.25/Lib/xml/dom/minidom.py =================================================================== --- Python-3.9.25.orig/Lib/xml/dom/minidom.py 2025-10-31 19:40:52.000000000 +0100 +++ Python-3.9.25/Lib/xml/dom/minidom.py 2025-12-19 23:27:27.635128214 +0100 @@ -292,13 +292,6 @@ childNodes.append(node) node.parentNode = self -def _in_document(node): - # return True iff node is part of a document tree - while node is not None: - if node.nodeType == Node.DOCUMENT_NODE: - return True - node = node.parentNode - return False def _write_data(writer, data): "Writes datachars to writer." @@ -1537,7 +1530,7 @@ if node.nodeType == Node.DOCUMENT_NODE: node._id_cache.clear() node._id_search_stack = None - elif _in_document(node): + elif node.ownerDocument: node.ownerDocument._id_cache.clear() node.ownerDocument._id_search_stack= None Index: Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ Python-3.9.25/Misc/NEWS.d/next/Security/2025-12-01-09-36-45.gh-issue-142145.tcAUhg.rst 2025-12-19 23:27:27.635403950 +0100 @@ -0,0 +1 @@ +Remove quadratic behavior in ``xml.minidom`` node ID cache clearing.