Skip to content

Commit 6b97b07

Browse files
test: fix test_xml_etree
1 parent b4f4d57 commit 6b97b07

1 file changed

Lines changed: 20 additions & 14 deletions

File tree

Lib/test/test_xml_etree.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2816,15 +2816,15 @@ def test_remove_with_clear_assume_missing(self):
28162816

28172817
def test_remove_with_clear_assume_existing(self):
28182818
# gh-126033: Check that a concurrent clear() for an assumed-to-be
2819-
# existing element does not make the interpreter crash.
2819+
# existing element raises RuntimeError but does not crash.
28202820
self.do_test_remove_with_clear(raises=False)
28212821

28222822
def do_test_remove_with_clear(self, *, raises):
2823-
2824-
# Until the discrepency between "del root[:]" and "root.clear()" is
2825-
# resolved, we need to keep two tests. Previously, using "del root[:]"
2826-
# did not crash with the reproducer of gh-126033 while "root.clear()"
2827-
# did.
2823+
# 'del root[:]' mutates the children list in-place, while
2824+
# 'root.clear()' replaces self._children with a new list. When
2825+
# raises=False (element "found"), the in-place mutation is detected
2826+
# by list.remove and raises RuntimeError, whereas root.clear() is
2827+
# invisible to list.remove (the old list is unchanged).
28282828

28292829
class E(ET.Element):
28302830
"""Local class to be able to mock E.__eq__ for introspection."""
@@ -2839,16 +2839,23 @@ def __eq__(self, o):
28392839
root.clear()
28402840
return not raises
28412841

2842-
if raises:
2843-
get_checker_context = lambda: self.assertRaises(ValueError)
2844-
else:
2845-
get_checker_context = nullcontext
2846-
28472842
self.assertIs(E.__eq__, object.__eq__)
28482843

2844+
get_checker_context = nullcontext
28492845
for Z, side_effect in [(X, 'del root[:]'), (Y, 'root.clear()')]:
28502846
self.enterContext(self.subTest(side_effect=side_effect))
28512847

2848+
# X uses 'del root[:]' which mutates the list in-place; this is
2849+
# detected by list.remove when raises=False (element "found").
2850+
# Y uses 'root.clear()' which swaps out self._children; the old
2851+
# list that list.remove is iterating is untouched, so no error.
2852+
if raises:
2853+
get_checker_context = lambda: self.assertRaises(ValueError)
2854+
elif Z is X:
2855+
get_checker_context = lambda: self.assertRaises(RuntimeError)
2856+
else:
2857+
get_checker_context = nullcontext
2858+
28522859
# test removing R() from [U()]
28532860
for R, U, description in [
28542861
(E, Z, "remove missing E() from [Z()]"),
@@ -2883,7 +2890,6 @@ def __eq__(self, o):
28832890
g.assert_not_called()
28842891

28852892
# Test removing root[1] (of type R) from [U(), R()].
2886-
is_special = is_python_implementation() and raises and Z is Y
28872893
if is_python_implementation() and raises and Z is Y:
28882894
# In pure Python, using root.clear() sets the children
28892895
# list to [] without calling list.clear().
@@ -2913,7 +2919,7 @@ def test_remove_with_mutate_root_assume_missing(self):
29132919

29142920
def test_remove_with_mutate_root_assume_existing(self):
29152921
# gh-126033: Check that a concurrent mutation for an assumed-to-be
2916-
# existing element does not make the interpreter crash.
2922+
# existing element raises RuntimeError.
29172923
self.do_test_remove_with_mutate_root(raises=False)
29182924

29192925
def do_test_remove_with_mutate_root(self, *, raises):
@@ -2927,7 +2933,7 @@ def __eq__(self, o):
29272933
if raises:
29282934
get_checker_context = lambda: self.assertRaises(ValueError)
29292935
else:
2930-
get_checker_context = nullcontext
2936+
get_checker_context = lambda: self.assertRaises(RuntimeError)
29312937

29322938
# test removing R() from [U(), V()]
29332939
cases = self.cases_for_remove_missing_with_mutations(E, Z)

0 commit comments

Comments
 (0)