From fcd1bb6e9be1e134644e1f8210acf87b15798b9e Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:19:19 +0100 Subject: [PATCH 1/4] Add `test_generator_stop.py` from 3.14.3 --- Lib/test/test_generator_stop.py | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Lib/test/test_generator_stop.py diff --git a/Lib/test/test_generator_stop.py b/Lib/test/test_generator_stop.py new file mode 100644 index 000000000..9cacdfff4 --- /dev/null +++ b/Lib/test/test_generator_stop.py @@ -0,0 +1,35 @@ +from __future__ import generator_stop + +import unittest + + +class TestPEP479(unittest.TestCase): + def test_stopiteration_wrapping(self): + def f(): + raise StopIteration + def g(): + yield f() + with self.assertRaisesRegex(RuntimeError, + "generator raised StopIteration"): + next(g()) + + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: is not + def test_stopiteration_wrapping_context(self): + def f(): + raise StopIteration + def g(): + yield f() + + try: + next(g()) + except RuntimeError as exc: + self.assertIs(type(exc.__cause__), StopIteration) + self.assertIs(type(exc.__context__), StopIteration) + self.assertTrue(exc.__suppress_context__) + else: + self.fail('__cause__, __context__, or __suppress_context__ ' + 'were not properly set') + + +if __name__ == '__main__': + unittest.main() From 9a20be7b9e528bb12d515f17cbcd24febfc97cf7 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:21:30 +0100 Subject: [PATCH 2/4] Update `test_generators.py` from 3.14.3 --- Lib/test/test_generators.py | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index dc5b23b0f..2bdd5fc8d 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -136,6 +136,18 @@ class FinalizationTest(unittest.TestCase): self.assertEqual(len(resurrected), 1) self.assertIsInstance(resurrected[0].gi_code, types.CodeType) + def test_exhausted_generator_frame_cycle(self): + def g(): + yield + + generator = g() + frame = generator.gi_frame + self.assertIsNone(frame.f_back) + next(generator) + self.assertIsNone(frame.f_back) + next(generator, None) + self.assertIsNone(frame.f_back) + class GeneratorTest(unittest.TestCase): @@ -292,6 +304,33 @@ class GeneratorTest(unittest.TestCase): self.assertEqual([1, 2], list(i for i in C())) + def test_close_clears_frame(self): + # gh-142766: Test that closing a generator clears its frame + class DetectDelete: + def __init__(self): + DetectDelete.deleted = False + + def __del__(self): + DetectDelete.deleted = True + + def generator(arg): + yield + + # Test a freshly created generator (not suspended) + g = generator(DetectDelete()) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Test a suspended generator + g = generator(DetectDelete()) + next(g) + g.close() + self.assertTrue(DetectDelete.deleted) + + # Clear via gi_frame.clear() + g = generator(DetectDelete()) + g.gi_frame.clear() + self.assertTrue(DetectDelete.deleted) class ModifyUnderlyingIterableTest(unittest.TestCase): iterables = [ From 85304392027bbfba712c88dacceb34e95ca82d9d Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:24:29 +0100 Subject: [PATCH 3/4] Mark failing tests --- Lib/test/test_generators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 2bdd5fc8d..8da74ff53 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -136,6 +136,7 @@ class FinalizationTest(unittest.TestCase): self.assertEqual(len(resurrected), 1) self.assertIsInstance(resurrected[0].gi_code, types.CodeType) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: is not None def test_exhausted_generator_frame_cycle(self): def g(): yield @@ -304,6 +305,7 @@ class GeneratorTest(unittest.TestCase): self.assertEqual([1, 2], list(i for i in C())) + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False is not true def test_close_clears_frame(self): # gh-142766: Test that closing a generator clears its frame class DetectDelete: From 93865e6523ae82d85e440e82a09859b09bed04c9 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 12 Mar 2026 13:33:04 +0100 Subject: [PATCH 4/4] Update `test_yield_from.py` from 3.14.3 --- Lib/test/test_yield_from.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_yield_from.py b/Lib/test/test_yield_from.py index e0e3db083..7028a6062 100644 --- a/Lib/test/test_yield_from.py +++ b/Lib/test/test_yield_from.py @@ -538,7 +538,7 @@ class TestPEP380Operation(unittest.TestCase): "finishing g", ]) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON def test_broken_getattr_handling(self): """ Test subiterator with a broken getattr implementation @@ -882,7 +882,7 @@ class TestPEP380Operation(unittest.TestCase): yield from () self.assertRaises(StopIteration, next, g()) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON def test_delegating_generators_claim_to_be_running(self): # Check with basic iteration def one(): @@ -909,7 +909,7 @@ class TestPEP380Operation(unittest.TestCase): pass self.assertEqual(res, [0, 1, 2, 3]) - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Lists differ: [0, 1, 2] != [0, 1, 2, 3] + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Lists differ: [0, 1, 2] != [0, 1, 2, 3] def test_delegating_generators_claim_to_be_running_with_throw(self): # Check with throw class MyErr(Exception): @@ -1071,7 +1071,7 @@ class TestInterestingEdgeCases(unittest.TestCase): def assert_generator_ignored_generator_exit(self): return self.assertRaisesRegex(RuntimeError, r"^generator ignored GeneratorExit$") - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON def test_close_and_throw_work(self): yielded_first = object() @@ -1209,7 +1209,7 @@ class TestInterestingEdgeCases(unittest.TestCase): self.assertIsNone(caught.exception.__context__.__context__) self.assert_stop_iteration(g) - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised def test_close_and_throw_raise_stop_iteration(self): yielded_first = object() @@ -1449,7 +1449,7 @@ class TestInterestingEdgeCases(unittest.TestCase): self.assertIsNone(caught.exception.__context__.__context__) self.assert_stop_iteration(g) - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not StopIteration() + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: None is not StopIteration() def test_close_and_throw_yield(self): yielded_first = object() @@ -1531,8 +1531,9 @@ class TestInterestingEdgeCases(unittest.TestCase): try: yield yielded_first yield yielded_second - finally: - return returned + except: + pass + return returned def outer(): return (yield from inner()) @@ -1587,6 +1588,19 @@ class TestInterestingEdgeCases(unittest.TestCase): self.assertIsNone(caught.exception.__context__) self.assert_stop_iteration(g) + def test_throws_in_iter(self): + # See GH-126366: NULL pointer dereference if __iter__ + # threw an exception. + class Silly: + def __iter__(self): + raise RuntimeError("nobody expects the spanish inquisition") + + def my_generator(): + yield from Silly() + + with self.assertRaisesRegex(RuntimeError, "nobody expects the spanish inquisition"): + next(iter(my_generator())) + if __name__ == '__main__': unittest.main()