@@ -146,11 +146,11 @@ def test_heappushpop(self):
146146 self .assertEqual (type (h [0 ]), int )
147147 self .assertEqual (type (x ), float )
148148
149- h = [10 ];
149+ h = [10 ]
150150 x = self .module .heappushpop (h , 9 )
151151 self .assertEqual ((h , x ), ([10 ], 9 ))
152152
153- h = [10 ];
153+ h = [10 ]
154154 x = self .module .heappushpop (h , 11 )
155155 self .assertEqual ((h , x ), ([11 ], 10 ))
156156
@@ -433,6 +433,37 @@ def test_heappop_mutating_heap(self):
433433 with self .assertRaises ((IndexError , RuntimeError )):
434434 self .module .heappop (heap )
435435
436+ def test_comparison_operator_modifiying_heap (self ):
437+ # See bpo-39421: Strong references need to be taken
438+ # when comparing objects as they can alter the heap
439+ class EvilClass (int ):
440+ def __lt__ (self , o ):
441+ heap .clear ()
442+ return NotImplemented
443+
444+ heap = []
445+ self .module .heappush (heap , EvilClass (0 ))
446+ self .assertRaises (IndexError , self .module .heappushpop , heap , 1 )
447+
448+ def test_comparison_operator_modifiying_heap_two_heaps (self ):
449+
450+ class h (int ):
451+ def __lt__ (self , o ):
452+ list2 .clear ()
453+ return NotImplemented
454+
455+ class g (int ):
456+ def __lt__ (self , o ):
457+ list1 .clear ()
458+ return NotImplemented
459+
460+ list1 , list2 = [], []
461+
462+ self .module .heappush (list1 , h (0 ))
463+ self .module .heappush (list2 , g (0 ))
464+
465+ self .assertRaises ((IndexError , RuntimeError ), self .module .heappush , list1 , g (1 ))
466+ self .assertRaises ((IndexError , RuntimeError ), self .module .heappush , list2 , h (1 ))
436467
437468class TestErrorHandlingPython (TestErrorHandling , TestCase ):
438469 module = py_heapq
0 commit comments