From e9d59e53ee30d7451ab748fe8c33c01156590506 2019-11-30 17:07:24
From: Matthias Bussonnier <bussonniermatthias@gmail.com>
Date: 2019-11-30 17:07:24
Subject: [PATCH] Merge pull request #11973 from jonathanslenders/run-prompt-in-different-asyncio-loop

Run the prompt in a separate asyncio loop.
---

diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py
index a185915..26188ae 100644
--- a/IPython/terminal/interactiveshell.py
+++ b/IPython/terminal/interactiveshell.py
@@ -1,5 +1,6 @@
 """IPython terminal interface using prompt_toolkit"""
 
+import asyncio
 import os
 import sys
 import warnings
@@ -309,6 +310,7 @@ class TerminalInteractiveShell(InteractiveShell):
 
         editing_mode = getattr(EditingMode, self.editing_mode.upper())
 
+        self.pt_loop = asyncio.new_event_loop()
         self.pt_app = PromptSession(
                             editing_mode=editing_mode,
                             key_bindings=key_bindings,
@@ -448,10 +450,21 @@ class TerminalInteractiveShell(InteractiveShell):
             default = ''
 
         with patch_stdout(raw=True):
-            text = self.pt_app.prompt(
-                default=default,
-#                pre_run=self.pre_prompt,# reset_current_buffer=True,
-                **self._extra_prompt_options())
+            # In order to make sure that asyncio code written in the
+            # interactive shell doesn't interfere with the prompt, we run the
+            # prompt in a different event loop.
+            # If we don't do this, people could spawn coroutine with a
+            # while/true inside which will freeze the prompt.
+
+            old_loop = asyncio.get_event_loop()
+            asyncio.set_event_loop(self.pt_loop)
+            try:
+                text = self.pt_app.prompt(
+                    default=default,
+                    **self._extra_prompt_options())
+            finally:
+                # Restore the original event loop.
+                asyncio.set_event_loop(old_loop)
         return text
 
     def enable_win_unicode_console(self):
@@ -568,11 +581,11 @@ class TerminalInteractiveShell(InteractiveShell):
         # this inputhook.
         if PTK3:
             if self._inputhook:
-                from prompt_toolkit.eventloop import set_eventloop_with_inputhook
-                set_eventloop_with_inputhook(self._inputhook)
+                from prompt_toolkit.eventloop import new_eventloop_with_inputhook
+                self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
             else:
                 import asyncio
-                asyncio.set_event_loop(asyncio.new_event_loop())
+                self.pt_loop = asyncio.new_event_loop()
 
     # Run !system commands directly, not through pipes, so terminal programs
     # work correctly.
diff --git a/setup.py b/setup.py
index 022520d..593e3a6 100755
--- a/setup.py
+++ b/setup.py
@@ -190,7 +190,7 @@ install_requires = [
     'decorator',
     'pickleshare',
     'traitlets>=4.2',
-    'prompt_toolkit>=2.0.0,<3.1.0',
+    'prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1',
     'pygments',
     'backcall',
 ]