diff --git a/newsfragments/149.bugfix.rst b/newsfragments/149.bugfix.rst new file mode 100644 index 00000000..9dccfc89 --- /dev/null +++ b/newsfragments/149.bugfix.rst @@ -0,0 +1,3 @@ +trio-asyncio no longer raises a spurious "Event loop stopped before Future +completed!" exception if a function passed to :func:`asyncio.run` calls +:func:`sys.exit`. diff --git a/tests/test_trio_asyncio.py b/tests/test_trio_asyncio.py index 6bcb6845..9e5c8096 100644 --- a/tests/test_trio_asyncio.py +++ b/tests/test_trio_asyncio.py @@ -193,3 +193,13 @@ def noop(): await trio_asyncio.aio_as_trio(loop.run_in_executor)(executor, noop) assert not scope.cancelled_caught + + +def test_system_exit(): + async def main(): + raise SystemExit(42) + + with pytest.raises(SystemExit) as scope: + asyncio.run(main()) + + assert scope.value.code == 42 diff --git a/trio_asyncio/_sync.py b/trio_asyncio/_sync.py index 0dfe7753..bdec0057 100644 --- a/trio_asyncio/_sync.py +++ b/trio_asyncio/_sync.py @@ -139,6 +139,14 @@ def is_done(_): nonlocal result result = outcome.capture(future.result) + if isinstance(result, outcome.Error) and isinstance( + result.error, (SystemExit, KeyboardInterrupt) + ): + # These exceptions propagate out of the event loop; + # don't stop the event loop again, or else it will + # interfere with cleanup actions like + # run_until_complete(shutdown_asyncgens) + return self.stop() future.add_done_callback(is_done)