##// END OF EJS Templates
Add asyncio input hook for event loop integration.
Jonathan Slenders -
Show More
@@ -0,0 +1,43 b''
1 """
2 Inputhook for running the original asyncio event loop while we're waiting for
3 input.
4
5 By default, in IPython, we run the prompt with a different asyncio event loop,
6 because otherwise we risk that people are freezing the prompt by scheduling bad
7 coroutines. E.g., a coroutine that does a while/true and never yield back
8 control to the loop. We can't cancel that.
9
10 However, sometimes we want the asyncio loop to keep running while waiting for
11 a prompt.
12
13 The following example will print the numbers from 1 to 10 above the prompt,
14 while we are waiting for input. (This works also because we use
15 prompt_toolkit`s `patch_stdout`)::
16
17 In [1]: import asyncio
18
19 In [2]: %gui asyncio
20
21 In [3]: async def f():
22 ...: for i in range(10):
23 ...: await asyncio.sleep(1)
24 ...: print(i)
25
26
27 In [4]: asyncio.ensure_future(f())
28
29 """
30 import asyncio
31
32 # Keep reference to the original asyncio loop, because getting the event loop
33 # within the input hook would return the other loop.
34 loop = asyncio.get_event_loop()
35
36
37 def inputhook(context):
38 def stop():
39 loop.stop()
40
41 loop.add_reader(context.fileno(), stop)
42 context.fileno()
43 loop.run_forever()
@@ -1,49 +1,50 b''
1 import importlib
1 import importlib
2 import os
2 import os
3
3
4 aliases = {
4 aliases = {
5 'qt4': 'qt',
5 'qt4': 'qt',
6 'gtk2': 'gtk',
6 'gtk2': 'gtk',
7 }
7 }
8
8
9 backends = [
9 backends = [
10 'qt', 'qt4', 'qt5',
10 'qt', 'qt4', 'qt5',
11 'gtk', 'gtk2', 'gtk3',
11 'gtk', 'gtk2', 'gtk3',
12 'tk',
12 'tk',
13 'wx',
13 'wx',
14 'pyglet', 'glut',
14 'pyglet', 'glut',
15 'osx'
15 'osx',
16 'asyncio'
16 ]
17 ]
17
18
18 registered = {}
19 registered = {}
19
20
20 def register(name, inputhook):
21 def register(name, inputhook):
21 """Register the function *inputhook* as an event loop integration."""
22 """Register the function *inputhook* as an event loop integration."""
22 registered[name] = inputhook
23 registered[name] = inputhook
23
24
24 class UnknownBackend(KeyError):
25 class UnknownBackend(KeyError):
25 def __init__(self, name):
26 def __init__(self, name):
26 self.name = name
27 self.name = name
27
28
28 def __str__(self):
29 def __str__(self):
29 return ("No event loop integration for {!r}. "
30 return ("No event loop integration for {!r}. "
30 "Supported event loops are: {}").format(self.name,
31 "Supported event loops are: {}").format(self.name,
31 ', '.join(backends + sorted(registered)))
32 ', '.join(backends + sorted(registered)))
32
33
33 def get_inputhook_name_and_func(gui):
34 def get_inputhook_name_and_func(gui):
34 if gui in registered:
35 if gui in registered:
35 return gui, registered[gui]
36 return gui, registered[gui]
36
37
37 if gui not in backends:
38 if gui not in backends:
38 raise UnknownBackend(gui)
39 raise UnknownBackend(gui)
39
40
40 if gui in aliases:
41 if gui in aliases:
41 return get_inputhook_name_and_func(aliases[gui])
42 return get_inputhook_name_and_func(aliases[gui])
42
43
43 gui_mod = gui
44 gui_mod = gui
44 if gui == 'qt5':
45 if gui == 'qt5':
45 os.environ['QT_API'] = 'pyqt5'
46 os.environ['QT_API'] = 'pyqt5'
46 gui_mod = 'qt'
47 gui_mod = 'qt'
47
48
48 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
49 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui_mod)
49 return gui, mod.inputhook
50 return gui, mod.inputhook
General Comments 0
You need to be logged in to leave comments. Login now