A quick primer on async
/await
. Async and await are relatively new features in Python which allow concurent programming. They won't make your code magically faster, but may make your code easier to read, maintain and reason about.
You will likely hear the terms event-loop, coroutines and many other ones, they will make sens in time.
The key thing to remember is that
await <async functions>
It's like the "One ring", there shoudl be only one. IPython (and Jupyter) usually already run one.
If you need to run any code that need to create and manage an event-loop, consult the docs. Typically you can't run a tornado app inside jupyter.
If you don't know/don't care, all is already setup for you.
Let's deactivate enventloop integration and try what is (usually invalid Python)
%autoawait False
from asyncio import sleep
# does not sleep, need to be awaited
print('before sleep')
sleep(5)
print('after sleep')
await sleep(5)
def f():
await sleep(5)
f()
async def f():
print('before...')
await sleep(5)
print('after')
### does not call f
f()
await f()
... back to step beginning.
Autoawait will attempt to detect async code and run it for you. There are of course limitations (bug report welcome)
%autoawait True
You will note that any line that start with %
is invalid Python and are IPython specific syntax. Those are call magics (line-magics with a single %
sign, cell magics with a double %%
sign)
print('before')
await sleep(5)
print('after')
Top level await is now valid syntax.
tpl = 'https://anapioficeandfire.com/api/characters/{}'
%%time
results = []
for i in range(1,50):
import requests
print('.', end='')
r = requests.get(tpl.format(i)).json()['aliases']
print('x', end='')
results.append(r)
for r in results:
print(r)
Nothing is perfect; if you get RuntimeErrors with asyncio, you may need to restart your kernel. More during my colleagues aiohttp tutorial this Afternoon
import aiohttp
async with aiohttp.ClientSession() as session:
response = await session.get(tpl.format(583))
json = await response.json()
print(json['aliases'])
async def get_char(i, session):
print('.', end='')
response = await session.get(tpl.format(i))
json = await response.json()
print('x', end='')
return json['aliases']
async with aiohttp.ClientSession() as s:
print(await get_char(1303, s))
tasks = []
import asyncio
async with aiohttp.ClientSession() as session:
# start
for i in range(1,50):
task = asyncio.ensure_future(get_char(i, session))
tasks.append(task)
results = await asyncio.gather(*tasks)
for r in results:
print(r)
Find the documentation for autoawait, and try to make it work with another asynchronous library. For exampe try to ply with trio
, using trio.sleep
and trio.open_nursery
to get several concurent task running, pritning different message regularly and at random intervals. What happen if you use time.sleep()
instead of trio.sleep()
? What hapen if you use asyncio.sleep()
?
import trio
async def every(n, message):
for i in range(30):
await trio.sleep(n)
print(message)
%autoawait trio
async with trio.open_nursery() as nursery:
nursery.start_soon(every, 1, 'Plic')
nursery.start_soon(every, 2, 'Ploc')