diff --git a/jupyter_client/tests/test_utils.py b/jupyter_client/tests/test_utils.py new file mode 100644 index 000000000..dd6849192 --- /dev/null +++ b/jupyter_client/tests/test_utils.py @@ -0,0 +1,30 @@ +import asyncio +from unittest import mock + +import pytest + +from jupyter_client.utils import run_sync + + +@pytest.fixture +def loop(): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + + +def test_run_sync_clean_up_task(loop): + async def coro_never_called(): + pytest.fail("The call to this coroutine is not expected") + + # Ensure that run_sync cancels the pending task + with mock.patch.object(loop, "run_until_complete") as patched_loop: + patched_loop.side_effect = KeyboardInterrupt + with mock.patch("asyncio.ensure_future") as patched_ensure_future: + mock_future = mock.Mock() + patched_ensure_future.return_value = mock_future + with pytest.raises(KeyboardInterrupt): + run_sync(coro_never_called)() + mock_future.cancel.assert_called_once() + # Suppress 'coroutine ... was never awaited' warning + patched_ensure_future.call_args[0][0].close() diff --git a/jupyter_client/utils.py b/jupyter_client/utils.py index d410e5d58..f2f3c4dc4 100644 --- a/jupyter_client/utils.py +++ b/jupyter_client/utils.py @@ -18,7 +18,12 @@ def wrapped(*args, **kwargs): import nest_asyncio # type: ignore nest_asyncio.apply(loop) - return loop.run_until_complete(coro(*args, **kwargs)) + future = asyncio.ensure_future(coro(*args, **kwargs)) + try: + return loop.run_until_complete(future) + except BaseException as e: + future.cancel() + raise e wrapped.__doc__ = coro.__doc__ return wrapped