Skip to content

Commit b96fa22

Browse files
test: fix test_xml_etree
1 parent b4f4d57 commit b96fa22

File tree

1 file changed

+18
-13
lines changed

1 file changed

+18
-13
lines changed

Lib/test/test_xml_etree.py

Lines changed: 18 additions & 13 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,21 @@ 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+
28522857
# test removing R() from [U()]
28532858
for R, U, description in [
28542859
(E, Z, "remove missing E() from [Z()]"),
@@ -2913,7 +2918,7 @@ def test_remove_with_mutate_root_assume_missing(self):
29132918

29142919
def test_remove_with_mutate_root_assume_existing(self):
29152920
# gh-126033: Check that a concurrent mutation for an assumed-to-be
2916-
# existing element does not make the interpreter crash.
2921+
# existing element raises RuntimeError.
29172922
self.do_test_remove_with_mutate_root(raises=False)
29182923

29192924
def do_test_remove_with_mutate_root(self, *, raises):
@@ -2927,7 +2932,7 @@ def __eq__(self, o):
29272932
if raises:
29282933
get_checker_context = lambda: self.assertRaises(ValueError)
29292934
else:
2930-
get_checker_context = nullcontext
2935+
get_checker_context = lambda: self.assertRaises(RuntimeError)
29312936

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

0 commit comments

Comments
 (0)