|
@@
-3,12
+3,17
b''
|
|
3
|
Asynchronous in REPL: Autoawait
|
|
3
|
Asynchronous in REPL: Autoawait
|
|
4
|
===============================
|
|
4
|
===============================
|
|
5
|
|
|
5
|
|
|
6
|
Starting with IPython 6.0, and when user Python 3.6 and above, IPython offer the
|
|
6
|
Starting with IPython 7.0, and when user Python 3.6 and above, IPython offer the
|
|
7
|
ability to run asynchronous code from the REPL. constructs which are
|
|
7
|
ability to run asynchronous code from the REPL. Constructs which are
|
|
8
|
:exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
|
|
8
|
:exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
|
|
9
|
|
|
9
|
|
|
10
|
When a supported libray is used, IPython will automatically `await` Futures
|
|
10
|
The example given here are for terminal IPython, running async code in a
|
|
11
|
and Coroutines in the REPL. This will happen if an :ref:`await <await>` (or `async`) is
|
|
11
|
notebook interface or any other frontend using the Jupyter protocol will need to
|
|
|
|
|
12
|
use a newer version of IPykernel. The details of how async code runs in
|
|
|
|
|
13
|
IPykernel will differ between IPython, IPykernel and their versions.
|
|
|
|
|
14
|
|
|
|
|
|
15
|
When a supported library is used, IPython will automatically `await` Futures
|
|
|
|
|
16
|
and Coroutines in the REPL. This will happen if an :ref:`await <await>` is
|
|
12
|
use at top level scope, or if any structure valid only in `async def
|
|
17
|
use at top level scope, or if any structure valid only in `async def
|
|
13
|
<https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
|
|
18
|
<https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
|
|
14
|
context are present. For example, the following being a syntax error in the
|
|
19
|
context are present. For example, the following being a syntax error in the
|
|
@@
-29,7
+34,7
b' Should behave as expected in the IPython REPL::'
|
|
29
|
|
|
34
|
|
|
30
|
Python 3.6.0
|
|
35
|
Python 3.6.0
|
|
31
|
Type 'copyright', 'credits' or 'license' for more information
|
|
36
|
Type 'copyright', 'credits' or 'license' for more information
|
|
32
|
IPython 6.0.0.dev -- An enhanced Interactive Python. Type '?' for help.
|
|
37
|
IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
|
|
33
|
|
|
38
|
|
|
34
|
In [1]: import aiohttp
|
|
39
|
In [1]: import aiohttp
|
|
35
|
...: result = aiohttp.get('https://api.github.com')
|
|
40
|
...: result = aiohttp.get('https://api.github.com')
|
|
@@
-58,7
+63,9
b' use the :magic:`%autoawait` magic to toggle the behavior at runtime::'
|
|
58
|
|
|
63
|
|
|
59
|
By default IPython will assume integration with Python's provided
|
|
64
|
By default IPython will assume integration with Python's provided
|
|
60
|
:mod:`asyncio`, but integration with other libraries is provided. In particular
|
|
65
|
:mod:`asyncio`, but integration with other libraries is provided. In particular
|
|
61
|
we provide experimental integration with the ``curio`` and ``trio`` library.
|
|
66
|
we provide experimental integration with the ``curio`` and ``trio`` library, the
|
|
|
|
|
67
|
later one being necessary if you require the ability to do nested call of
|
|
|
|
|
68
|
IPython's ``embed()`` functionality.
|
|
62
|
|
|
69
|
|
|
63
|
You can switch current integration by using the
|
|
70
|
You can switch current integration by using the
|
|
64
|
``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
|
|
71
|
``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
|
|
@@
-103,24
+110,25
b' other features of IPython and various registered extensions. In particular if yo'
|
|
103
|
are a direct or indirect user of the AST transformers, these may not apply to
|
|
110
|
are a direct or indirect user of the AST transformers, these may not apply to
|
|
104
|
your code.
|
|
111
|
your code.
|
|
105
|
|
|
112
|
|
|
106
|
The default loop, or runner does not run in the background, so top level
|
|
113
|
When Using command line IPython, the default loop (or runner) does not process
|
|
107
|
asynchronous code must finish for the REPL to allow you to enter more code. As
|
|
114
|
in the background, so top level asynchronous code must finish for the REPL to
|
|
108
|
with usual Python semantic, the awaitables are started only when awaited for the
|
|
115
|
allow you to enter more code. As with usual Python semantic, the awaitables are
|
|
109
|
first time. That is to say, in first example, no network request is done between
|
|
116
|
started only when awaited for the first time. That is to say, in first example,
|
|
110
|
``In[1]`` and ``In[2]``.
|
|
117
|
no network request is done between ``In[1]`` and ``In[2]``.
|
|
111
|
|
|
118
|
|
|
112
|
|
|
119
|
|
|
113
|
Internals
|
|
120
|
Internals
|
|
114
|
=========
|
|
121
|
=========
|
|
115
|
|
|
122
|
|
|
116
|
As running asynchronous code is not supported in interactive REPL as of Python
|
|
123
|
As running asynchronous code is not supported in interactive REPL (as of Python
|
|
117
|
3.6 we have to rely to a number of complex workaround to allow this to happen.
|
|
124
|
3.7) we have to rely to a number of complex workaround to allow this to happen.
|
|
118
|
It is interesting to understand how this works in order to understand potential
|
|
125
|
It is interesting to understand how this works in order to comprehend potential
|
|
119
|
bugs, or provide a custom runner.
|
|
126
|
bugs, or provide a custom runner.
|
|
120
|
|
|
127
|
|
|
121
|
Among the many approaches that are at our disposition, we find only one that
|
|
128
|
Among the many approaches that are at our disposition, we find only one that
|
|
122
|
suited out need. Under the hood we :ct the code object from a async-def function
|
|
129
|
suited out need. Under the hood we use the code object from a async-def function
|
|
123
|
and run it in global namesapace after modifying the ``__code__`` object.::
|
|
130
|
and run it in global namespace after modifying it to not create a new
|
|
|
|
|
131
|
``locals()`` scope::
|
|
124
|
|
|
132
|
|
|
125
|
async def inner_async():
|
|
133
|
async def inner_async():
|
|
126
|
locals().update(**global_namespace)
|
|
134
|
locals().update(**global_namespace)
|
|
@@
-135,7
+143,7
b' and run it in global namesapace after modifying the ``__code__`` object.::'
|
|
135
|
|
|
143
|
|
|
136
|
|
|
144
|
|
|
137
|
The first thing you'll notice is that unlike classical ``exec``, there is only
|
|
145
|
The first thing you'll notice is that unlike classical ``exec``, there is only
|
|
138
|
one name space. Second, user code runs in a function scope, and not a module
|
|
146
|
one namespace. Second, user code runs in a function scope, and not a module
|
|
139
|
scope.
|
|
147
|
scope.
|
|
140
|
|
|
148
|
|
|
141
|
On top of the above there are significant modification to the AST of
|
|
149
|
On top of the above there are significant modification to the AST of
|
|
@@
-181,5
+189,5
b' We can set it up by passing it to ``%autoawait``::'
|
|
181
|
|
|
189
|
|
|
182
|
|
|
190
|
|
|
183
|
Asynchronous programming in python (and in particular in the REPL) is still a
|
|
191
|
Asynchronous programming in python (and in particular in the REPL) is still a
|
|
184
|
relatively young subject. Feel free to contribute improvements to this codebase
|
|
192
|
relatively young subject. We expect some code to not behave as you expect, so
|
|
185
|
and give us feedback.
|
|
193
|
feel free to contribute improvements to this codebase and give us feedback.
|