[3.12] gh-112182: Replace StopIteration with RuntimeError for future (GH-113220) (GH-123033)

When an `StopIteration` raises into `asyncio.Future`, this will cause
a thread to hang. This commit address this by not raising an exception
and silently transforming the `StopIteration` with a `RuntimeError`,
which the caller can reconstruct from `fut.exception().__cause__`
(cherry picked from commit 4826d52338)

Co-authored-by: Jamie Phan <jamie@ordinarylab.dev>
This commit is contained in:
Miss Islington (bot) 2024-08-15 17:34:53 +02:00 committed by GitHub
parent 9f153a2acf
commit 41090b7ba0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 12 deletions

View file

@ -270,10 +270,6 @@ class BaseFutureTests:
f = self._new_future(loop=self.loop)
self.assertRaises(asyncio.InvalidStateError, f.exception)
# StopIteration cannot be raised into a Future - CPython issue26221
self.assertRaisesRegex(TypeError, "StopIteration .* cannot be raised",
f.set_exception, StopIteration)
f.set_exception(exc)
self.assertFalse(f.cancelled())
self.assertTrue(f.done())
@ -283,6 +279,25 @@ class BaseFutureTests:
self.assertRaises(asyncio.InvalidStateError, f.set_exception, None)
self.assertFalse(f.cancel())
def test_stop_iteration_exception(self, stop_iteration_class=StopIteration):
exc = stop_iteration_class()
f = self._new_future(loop=self.loop)
f.set_exception(exc)
self.assertFalse(f.cancelled())
self.assertTrue(f.done())
self.assertRaises(RuntimeError, f.result)
exc = f.exception()
cause = exc.__cause__
self.assertIsInstance(exc, RuntimeError)
self.assertRegex(str(exc), 'StopIteration .* cannot be raised')
self.assertIsInstance(cause, stop_iteration_class)
def test_stop_iteration_subclass_exception(self):
class MyStopIteration(StopIteration):
pass
self.test_stop_iteration_exception(MyStopIteration)
def test_exception_class(self):
f = self._new_future(loop=self.loop)
f.set_exception(RuntimeError)