diff --git a/IPython/core/async_helpers.py b/IPython/core/async_helpers.py index fb4cc19..fca78de 100644 --- a/IPython/core/async_helpers.py +++ b/IPython/core/async_helpers.py @@ -13,19 +13,29 @@ Python semantics. import ast import sys +import asyncio import inspect from textwrap import dedent, indent class _AsyncIORunner: + def __init__(self): + self._loop = None + + @property + def loop(self): + """Always returns a non-closed event loop""" + if self._loop is None or self._loop.is_closed(): + policy = asyncio.get_event_loop_policy() + self._loop = policy.new_event_loop() + policy.set_event_loop(self._loop) + return self._loop def __call__(self, coro): """ Handler for asyncio autoawait """ - import asyncio - - return asyncio.get_event_loop().run_until_complete(coro) + return self.loop.run_until_complete(coro) def __str__(self): return 'asyncio' diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 84bea9c..598b6dc 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -1037,6 +1037,22 @@ def test_run_cell_async(): assert result.result == 5 +def test_run_cell_await(): + ip.run_cell("import asyncio") + result = ip.run_cell("await asyncio.sleep(0.01); 10") + assert ip.user_ns["_"] == 10 + + +def test_run_cell_asyncio_run(): + ip.run_cell("import asyncio") + result = ip.run_cell("await asyncio.sleep(0.01); 1") + assert ip.user_ns["_"] == 1 + result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2") + assert ip.user_ns["_"] == 2 + result = ip.run_cell("await asyncio.sleep(0.01); 3") + assert ip.user_ns["_"] == 3 + + def test_should_run_async(): assert not ip.should_run_async("a = 5") assert ip.should_run_async("await x") diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py index 1ea9e7e..4e35aad 100644 --- a/IPython/terminal/interactiveshell.py +++ b/IPython/terminal/interactiveshell.py @@ -460,13 +460,15 @@ class TerminalInteractiveShell(InteractiveShell): # If we don't do this, people could spawn coroutine with a # while/true inside which will freeze the prompt. + policy = asyncio.get_event_loop_policy() try: - old_loop = asyncio.get_running_loop() + old_loop = policy.get_event_loop() except RuntimeError: - # This happens when the user used `asyncio.run()`. + # This happens when the the event loop is closed, + # e.g. by calling `asyncio.run()`. old_loop = None - asyncio.set_event_loop(self.pt_loop) + policy.set_event_loop(self.pt_loop) try: with patch_stdout(raw=True): text = self.pt_app.prompt( @@ -474,7 +476,8 @@ class TerminalInteractiveShell(InteractiveShell): **self._extra_prompt_options()) finally: # Restore the original event loop. - asyncio.set_event_loop(old_loop) + if old_loop is not None: + policy.set_event_loop(old_loop) return text