@@ -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