##// END OF EJS Templates
back to dev
back to dev

File last commit:

r24999:4ab3299b
r25563:98b1e108
Show More
autoawait.rst
319 lines | 11.2 KiB | text/x-rst | RstLexer
Matthias Bussonnier
fix docs
r24466 .. _autoawait:
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Asynchronous in REPL: Autoawait
===============================
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 .. note::
Matthias Bussonnier
typos
r24546 This feature is experimental and behavior can change between python and
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 IPython version without prior deprecation.
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 Starting with IPython 7.0, and when user Python 3.6 and above, IPython offer the
ability to run asynchronous code from the REPL. Constructs which are
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 :exc:`SyntaxError` s in the Python REPL can be used seamlessly in IPython.
Matthias Bussonnier
one more rephrasing/plural
r24572 The examples given here are for terminal IPython, running async code in a
Matthias Bussonnier
fix Paul's comments
r24571 notebook interface or any other frontend using the Jupyter protocol needs
Matthias Bussonnier
one more rephrasing/plural
r24572 IPykernel version 5.0 or above. The details of how async code runs in IPykernel
will differ between IPython, IPykernel and their versions.
Matthias Bussonnier
doc, remove appveyor 3.4
r24475
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 When a supported library is used, IPython will automatically allow Futures and
Coroutines in the REPL to be ``await`` ed. This will happen if an :ref:`await
<await>` (or any other async constructs like async-with, async-for) is use at
top level scope, or if any structure valid only in `async def
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 <https://docs.python.org/3/reference/compound_stmts.html#async-def>`_ function
context are present. For example, the following being a syntax error in the
Python REPL::
Python 3.6.0
[GCC 4.2.1]
Type "help", "copyright", "credits" or "license" for more information.
>>> import aiohttp
Sebastian Witowski
docs: update the aiohttp usage in autoawait.rst...
r24999 >>> session = aiohttp.ClientSession()
>>> result = session.get('https://api.github.com')
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 >>> response = await result
File "<stdin>", line 1
response = await result
^
SyntaxError: invalid syntax
Should behave as expected in the IPython REPL::
Python 3.6.0
Type 'copyright', 'credits' or 'license' for more information
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 IPython 7.0.0 -- An enhanced Interactive Python. Type '?' for help.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
In [1]: import aiohttp
Sebastian Witowski
docs: update the aiohttp usage in autoawait.rst...
r24999 ...: session = aiohttp.ClientSession()
...: result = session.get('https://api.github.com')
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
In [2]: response = await result
<pause for a few 100s ms>
In [3]: await response.json()
Out[3]:
{'authorizations_url': 'https://api.github.com/authorizations',
'code_search_url': 'https://api.github.com/search/code?q={query}...',
...
}
You can use the ``c.InteractiveShell.autoawait`` configuration option and set it
Matthias Bussonnier
fix Paul's comments
r24571 to :any:`False` to deactivate automatic wrapping of asynchronous code. You can
Matthias Bussonnier
Update autoawait.rst
r24581 also use the :magic:`%autoawait` magic to toggle the behavior at runtime::
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
In [1]: %autoawait False
In [2]: %autoawait
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 IPython autoawait is `Off`, and set to use `asyncio`
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
By default IPython will assume integration with Python's provided
:mod:`asyncio`, but integration with other libraries is provided. In particular
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 we provide experimental integration with the ``curio`` and ``trio`` library.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
You can switch current integration by using the
``c.InteractiveShell.loop_runner`` option or the ``autoawait <name
integration>`` magic.
For example::
In [1]: %autoawait trio
In [2]: import trio
In [3]: async def child(i):
...: print(" child %s goes to sleep"%i)
...: await trio.sleep(2)
...: print(" child %s wakes up"%i)
In [4]: print('parent start')
...: async with trio.open_nursery() as n:
...: for i in range(5):
...: n.spawn(child, i)
...: print('parent end')
parent start
child 2 goes to sleep
child 0 goes to sleep
child 3 goes to sleep
child 1 goes to sleep
child 4 goes to sleep
<about 2 seconds pause>
child 2 wakes up
child 1 wakes up
child 0 wakes up
child 3 wakes up
child 4 wakes up
parent end
In the above example, ``async with`` at top level scope is a syntax error in
Python.
Using this mode can have unexpected consequences if used in interaction with
Matthias Bussonnier
fix Paul's comments
r24571 other features of IPython and various registered extensions. In particular if
you are a direct or indirect user of the AST transformers, these may not apply
to your code.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 When using command line IPython, the default loop (or runner) does not process
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 in the background, so top level asynchronous code must finish for the REPL to
allow you to enter more code. As with usual Python semantic, the awaitables are
started only when awaited for the first time. That is to say, in first example,
no network request is done between ``In[1]`` and ``In[2]``.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480 Effects on IPython.embed()
Matthias Bussonnier
Rst formatting (typo and decrease level to not be in sidebar)
r24577 --------------------------
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480
Matthias Bussonnier
Add pseudo sync mode
r24481 IPython core being asynchronous, the use of ``IPython.embed()`` will now require
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 a loop to run. By default IPython will use a fake coroutine runner which should
allow ``IPython.embed()`` to be nested. Though this will prevent usage of the
Matthias Bussonnier
Update autoawait.rst
r24581 :magic:`%autoawait` feature when using IPython embed.
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 You can set explicitly a coroutine runner for ``embed()`` if you desire to run
asynchronous code, the exact behavior is though undefined.
Matthias Bussonnier
att triio runner at top level now that 3.4 drop + docs
r24480
Matthias Bussonnier
add notes some magic don't work yet
r24500 Effects on Magics
Matthias Bussonnier
Rst formatting (typo and decrease level to not be in sidebar)
r24577 -----------------
Matthias Bussonnier
add notes some magic don't work yet
r24500
A couple of magics (``%%timeit``, ``%timeit``, ``%%time``, ``%%prun``) have not
yet been updated to work with asynchronous code and will raise syntax errors
when trying to use top-level ``await``. We welcome any contribution to help fix
those, and extra cases we haven't caught yet. We hope for better support in Cor
Python for top-level Async code.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 Internals
Matthias Bussonnier
Rst formatting (typo and decrease level to not be in sidebar)
r24577 ---------
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 As running asynchronous code is not supported in interactive REPL (as of Python
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 3.7) we have to rely to a number of complex workaround and heuristic to allow
this to happen. It is interesting to understand how this works in order to
comprehend potential bugs, or provide a custom runner.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
Among the many approaches that are at our disposition, we find only one that
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 suited out need. Under the hood we use the code object from a async-def function
and run it in global namespace after modifying it to not create a new
``locals()`` scope::
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
async def inner_async():
locals().update(**global_namespace)
#
# here is user code
#
return last_user_statement
codeobj = modify(inner_async.__code__)
coroutine = eval(codeobj, user_ns)
display(loop_runner(coroutine))
The first thing you'll notice is that unlike classical ``exec``, there is only
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 one namespace. Second, user code runs in a function scope, and not a module
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463 scope.
On top of the above there are significant modification to the AST of
``function``, and ``loop_runner`` can be arbitrary complex. So there is a
significant overhead to this kind of code.
By default the generated coroutine function will be consumed by Asyncio's
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490 ``loop_runner = asyncio.get_evenloop().run_until_complete()`` method if
``async`` mode is deemed necessary, otherwise the coroutine will just be
exhausted in a simple runner. It is though possible to change the default
runner.
Matthias Bussonnier
Prototype async REPL using IPython, take III...
r24463
A loop runner is a *synchronous* function responsible from running a coroutine
object.
The runner is responsible from ensuring that ``coroutine`` run to completion,
and should return the result of executing the coroutine. Let's write a
runner for ``trio`` that print a message when used as an exercise, ``trio`` is
special as it usually prefer to run a function object and make a coroutine by
itself, we can get around this limitation by wrapping it in an async-def without
parameters and passing this value to ``trio``::
In [1]: import trio
...: from types import CoroutineType
...:
...: def trio_runner(coro:CoroutineType):
...: print('running asynchronous code')
...: async def corowrap(coro):
...: return await coro
...: return trio.run(corowrap, coro)
We can set it up by passing it to ``%autoawait``::
In [2]: %autoawait trio_runner
In [3]: async def async_hello(name):
...: await trio.sleep(1)
...: print(f'Hello {name} world !')
...: await trio.sleep(1)
In [4]: await async_hello('async')
running asynchronous code
Hello async world !
Asynchronous programming in python (and in particular in the REPL) is still a
Matthias Bussonnier
doc, remove appveyor 3.4
r24475 relatively young subject. We expect some code to not behave as you expect, so
feel free to contribute improvements to this codebase and give us feedback.
Matthias Bussonnier
docs cleanup, reformat code, remove dead code.
r24490
We invite you to thoroughly test this feature and report any unexpected behavior
as well as propose any improvement.
Matthias Bussonnier
update docs about autoawait
r24565
Using Autoawait in a notebook (IPykernel)
Matthias Bussonnier
Rst formatting (typo and decrease level to not be in sidebar)
r24577 -----------------------------------------
Matthias Bussonnier
update docs about autoawait
r24565
Update ipykernel to version 5.0 or greater::
pip install ipykernel ipython --upgrade
# or
conda install ipykernel ipython --upgrade
Matthias Bussonnier
Fix magic directive and role....
r24579 This should automatically enable :magic:`autoawait` integration. Unlike
terminal IPython, all code runs on ``asyncio`` eventloop, so creating a loop by
hand will not work, including with magics like :magic:`%run` or other
frameworks that create the eventloop themselves. In cases like these you can
try to use projects like `nest_asyncio
<https://github.com/erdewit/nest_asyncio>`_ and follow `this discussion
Matthias Bussonnier
update docs about autoawait
r24565 <https://github.com/jupyter/notebook/issues/3397#issuecomment-419386811>`_
Difference between terminal IPython and IPykernel
Matthias Bussonnier
Rst formatting (typo and decrease level to not be in sidebar)
r24577 -------------------------------------------------
Matthias Bussonnier
update docs about autoawait
r24565
Matthias Bussonnier
fix Paul's comments
r24571 The exact asynchronous code running behavior varies between Terminal IPython and
IPykernel. The root cause of this behavior is due to IPykernel having a
Matthias Bussonnier
Fix magic directive and role....
r24579 *persistent* `asyncio` loop running, while Terminal IPython starts and stops a
Matthias Bussonnier
fix Paul's comments
r24571 loop for each code block. This can lead to surprising behavior in some case if
you are used to manipulate asyncio loop yourself, see for example
Matthias Bussonnier
update docs about autoawait
r24565 :ghissue:`11303` for a longer discussion but here are some of the astonishing
cases.
Matthias Bussonnier
fix Paul's comments
r24571 This behavior is an implementation detail, and should not be relied upon. It can
change without warnings in future versions of IPython.
Matthias Bussonnier
update docs about autoawait
r24565
In terminal IPython a loop is started for each code blocks only if there is top
level async code::
$ ipython
In [1]: import asyncio
...: asyncio.get_event_loop()
Out[1]: <_UnixSelectorEventLoop running=False closed=False debug=False>
In [2]:
In [2]: import asyncio
...: await asyncio.sleep(0)
...: asyncio.get_event_loop()
Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
See that ``running`` is ``True`` only in the case were we ``await sleep()``
In a Notebook, with ipykernel the asyncio eventloop is always running::
$ jupyter notebook
In [1]: import asyncio
...: loop1 = asyncio.get_event_loop()
...: loop1
Out[1]: <_UnixSelectorEventLoop running=True closed=False debug=False>
In [2]: loop2 = asyncio.get_event_loop()
...: loop2
Out[2]: <_UnixSelectorEventLoop running=True closed=False debug=False>
In [3]: loop1 is loop2
Out[3]: True
Matthias Bussonnier
fix Paul's comments
r24571 In Terminal IPython background tasks are only processed while the foreground
task is running, if and only if the foreground task is async::
Matthias Bussonnier
update docs about autoawait
r24565
$ ipython
In [1]: import asyncio
...:
...: async def repeat(msg, n):
...: for i in range(n):
...: print(f"{msg} {i}")
...: await asyncio.sleep(1)
...: return f"{msg} done"
...:
...: asyncio.ensure_future(repeat("background", 10))
Out[1]: <Task pending coro=<repeat() running at <ipython-input-1-02d0ef250fe7>:3>>
In [2]: await asyncio.sleep(3)
background 0
background 1
background 2
background 3
In [3]: import time
...: time.sleep(5)
In [4]: await asyncio.sleep(3)
background 4
background 5
Paul Ivanov
minor typo and style tweak
r24574 background 6g
Matthias Bussonnier
update docs about autoawait
r24565
In a Notebook, QtConsole, or any other frontend using IPykernel, background
Matthias Bussonnier
fix Paul's comments
r24571 tasks should behave as expected.