mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
[3.12] gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146) (#142211)
* gh-142145: Remove quadratic behavior in node ID cache clearing (GH-142146) * gh-142754: Ensure that Element & Attr instances have the ownerDocument attribute (GH-142794) (cherry picked from commit1cc7551b3f) (cherry picked from commit08d8e18ad8) (cherry picked from commit8d2d7bb2e7) Co-authored-by: Jacob Walls <38668450+jacobtylerwalls@users.noreply.github.com> Co-authored-by: Seth Michael Larson <seth@python.org> Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
parent
0e4cd89781
commit
9c9dda6625
3 changed files with 41 additions and 9 deletions
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
import copy
|
||||
import pickle
|
||||
import time
|
||||
import io
|
||||
from test import support
|
||||
import unittest
|
||||
|
||||
import xml.dom.minidom
|
||||
|
||||
from xml.dom.minidom import parse, Attr, Node, Document, parseString
|
||||
from xml.dom.minidom import parse, Attr, Node, Document, Element, parseString
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
|
|
@ -176,6 +177,36 @@ def testAppendChild(self):
|
|||
self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
|
||||
dom.unlink()
|
||||
|
||||
@support.requires_resource('cpu')
|
||||
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.monotonic()
|
||||
for child in children:
|
||||
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)
|
||||
|
||||
def testSetAttributeNodeWithoutOwnerDocument(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")
|
||||
|
||||
def testAppendChildFragment(self):
|
||||
dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
|
||||
dom.documentElement.appendChild(frag)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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."
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1539,7 +1534,7 @@ def _clear_id_cache(node):
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue