Async Programming in Python
Async Programming in Python
Async Programming in Python
Coroutines
in Python
Asynchronous programming
with Coroutines
in Python
Ewoud Van Craeynest
2
Table of Contents
introduction
summary
(extra slides)
3
Introduction
4
Introduction
5 1
not to be confused with parallelism
Introduction
6
Introduction
7
Introduction
Blocking
8
Introduction
Blocking
9
Introduction
Blocking
10
Introduction
Threading
11
Introduction
Threading
12
Introduction
Threading
13
Introduction
Threading
14 2
doesn’t mean they can’t be useful if used correctly, like goto’s
Introduction
Threading
15
Introduction
Threading
Threads no more?
I No!
I just less of them
I one thread for all connections
I i.s.o. one thread per connection
I one for all video stuff
I one for all screen io
I one for all . . .
16
Introduction
Non blocking calls
Circling back
17
Introduction
Non blocking calls
Wait a minute . . .
18
Introduction
Non blocking calls
19
Introduction
Non blocking calls
20
Introduction
Not so new
21
Introduction
History
22
Introduction
History
Predecessors vs 3.5
23
Introduction
24
asyncio
python 3.4
asyncio
25 3
get it from PyPI for Python 3.3
asyncio
provisional
provisional in 3.4
Note
The asyncio package has been included in the standard library on
a provisional basis. Backwards incompatible changes (up to and
including removal of the module) may occur if deemed necessary
by the core developers.
26
asyncio
coroutines
An example:
first coroutine
@asyncio.coroutine
def print_hello():
while True:
print("{} - Hello world!".format(int(time())))
yield from asyncio.sleep(3)
27
asyncio
coroutines
coroutines
first coroutine
@asyncio.coroutine
def print_hello():
while True:
print("{} - Hello world!".format(int(time())))
yield from asyncio.sleep(3)
28
asyncio
coroutines
new style
29
asyncio
callbacks
coroutine api
coroutine api
@asyncio.coroutine
def tcp_echo_client(message, loop):
reader, writer = yield from asyncio.open_connection(’127.0.0.1’, 888
loop=loop)
print(’Send: %r’ % message)
writer.write(message.encode())
31
asyncio in Python 3.5
new keywords
coroutines
32
asyncio in Python 3.5
provisional
Note
The asyncio package has been included in the standard library on
a provisional basis. Backwards incompatible changes (up to and
including removal of the module) may occur if deemed necessary
by the core developers.
33 4
note is gone in Python3.6 docs
asyncio in Python 3.5
coroutines
same examples
new syntax
async def print_hello():
while True:
print("{} - Hello world!".format(int(time())))
await asyncio.sleep(3)
34
asyncio
coroutines
coroutines reiterated
first coroutine
async def print_hello():
while True:
print("{} - Hello world!".format(int(time())))
await asyncio.sleep(3)
35
asyncio
event loop
event loop
36
asyncio
old style async
loop.add_reader(sys.stdin, process_input)
37
asyncio
callbacks
38
asyncio
callbacks
loop = asyncio.get_event_loop()
loop.run_forever()
39
loop.close()
asyncio
callbacks
40
asyncio in Python 3.5
coroutines
same examples
new syntax
async def tcp_echo_client(message, loop):
reader, writer = await asyncio.open_connection(’127.0.0.1’, 8888,
loop=loop)
coroutine api
reader, writer = await asyncio.open_connection(’127.0.0.1’, 8888,
loop=loop)
42
asyncio
coroutine api in Python 3.5
coroutine api
coroutine api
writer.write(message.encode())
coroutine api
coroutine api
async def tcp_echo_client(message, loop):
reader, writer = await asyncio.open_connection(’127.0.0.1’, 8888,
loop=loop)
print(’Send: %r’ % message)
writer.write(message.encode())
coroutine api
45
asyncio in Python 3.5
coroutines api
batteries include:
46
asyncio in Python 3.5
asyncio.org
asyncio.org
47 5
workaround using threads
asyncio in Python 3.5
asyncserial
asyncserial
asyncserial example
async def foo():
port = asyncserial.AsyncSerial(’/dev/ttyUSB1’)
await port.write(somedata)
response = await port.read(5)
await some_handle_response(response)
48
asyncio in Python 3.5
aiozmq.rpc
aiozmq.rpc
await asyncio.sleep(42)
await client.call.stop_some_remote_action()
49
asyncio in Python 3.5
aiohttp
aiohttp
I HTTP using coroutines
I even with and for can use coroutines
aiohttp
async def fetch(session, url):
with aiohttp.Timeout(10):
async with session.get(url) as response:
return await response.text()
if __name__ == ’__main__’:
loop = asyncio.get_event_loop()
with aiohttp.ClientSession(loop=loop) as session:
html = loop.run_until_complete(
fetch(session, ’http://python.org’))
50
print(html)
asyncio in Python 3.5
coroutines
AsyncSSH
aiohttp
async def run_client():
async with asyncssh.connect(’localhost’) as conn:
stdin, stdout, stderr = await conn.open_session(’echo "Hello!"’)
await stdout.channel.wait_closed()
status = stdout.channel.get_exit_status()
if status:
print(’Program exited with status %d’ % status, file=sys.std
else:
print(’Program exited successfully’)
51
asyncio.get_event_loop().run_until_complete(run_client())
asyncio in Python 3.5
blocking
blocking stuff
I blocking functions should not be called directly
I it will block the loop and all other tasks
I if no high level async API available
I run in executor, like ThreadPoolExecutor
52
asyncio in Python 3.5
coroutines
aiofiles
I file IO is blocking
I not easily made asynchronous
I aiofiles delegates to thread pool
I unblocking your event loop
I using the future mechanism
I discussion with Guido on GitHub about asynchronous files
53
asyncio in Python 3.5
testing
asynctest
I you’ll want to test coroutines
I but that requires a loop running
I loop aware test
I asynctest module
asynctest
import asynctest
import aiozmq.rpc
class MyRpcTest(asynctest.TestCase):
async def setUp(self ):
self.client = await aiozmq.rpc.connect_rpc(
connect=’tcp://127.0.0.1:5555’)
54
asyncio in Python 3.5
testing
await cc.start_some_remote_action(some_calc(ret))
for _ in range(5):
await time.sleep(0.5)
newr = await cc.get_some_value()
self.assertGreater(newr, r)
r = newr
await cc.stop_some_remote_action()
55
asyncio in Python 3.5
testing
asynctest
I loop aware test
I ideally run unrelated tests concurrently on the same loop
I realistic?
I perhaps not
asynctest
import asynctest
56
asyncio in Python 3.5
testing
asynctest
import asynctest
57
asyncio in Python 3.5
testing
asynctest
import asynctest.selector
58
asyncio in Python 3.5
testing
pytest-asyncio
I for those on pytest iso unittest
I haven’t tried it . . .
I claim custom event loop support
I monkey patching coroutines allowed
pytest-asyncio
@pytest.mark.asyncio
async def test_some_asyncio_code():
res = await library.do_something()
assert b’expected result’ == res
59
asyncio in Python 3.5
stopping loop
60
asyncio in Python 3.5
stopping loop
cancelling a task
I sometimes not required to stop whole loop
I a single task might suffice
61
asyncio in Python 3.5
stopping loop
threadsafety
I the whole thing isn’t threadsafe
I why would it ?
I so take precautions from other threads
stopping threadsafe
loop.call_soon_threadsafe(loop.stop)
loop.call_soon_threadsafe(sometask.cancel)
62
asyncio in Python 3.5
exceptions
exceptions
I raised exceptions from a coroutine
I get set on the internal future object
I and reraised when awaited on
exceptions
async def foo():
raise Exception()
63
asyncio in Python 3.5
exceptions
exceptions
I but if never awaited
I aka exception never consumed
I it’s logged with traceback 6
exceptions
async def foo():
raise Exception()
64 6
Get more logging by enabling asyncio debug mode
asyncio in Python 3.5
logging
logging
I asyncio logs information on the logging module in logger
’asyncio’
I useful to redirect this away from frameworks that steal stdin
and stdout
I like robotframework
65
asyncio in Python 3.5
alternatives
alternatives to asyncio
I as is to be expected . . .
I not everyone completely agrees on Python’s implementation
I and offer partial or complete improvement over asyncio
66
asyncio in Python 3.5
alternatives to asyncio
other loops
I we can use loops other than the standard one
I like uvloop 7
I a fast, drop-in replacement of asyncio event loop
I implements asyncio.AbstractEventLoop
I promises Go-like performance
I expect others . . .
uvloop
import asyncio
import uvloop
loop = uvloop.new_event_loop()
asyncio.set_event_loop(loop)
67 7
https://github.com/MagicStack/uvloop
asyncio in Python 3.5
alternatives
I by David Beazly
I based on task queueing
I not callback based event loop
I not just the loop
I complete async I/O library
I sockets, files, sleep, signals, synchronization, processes, ssl, ipc
I interactive monitoring
I claims 75 to 150% faster than asyncio
I claims 5 to 40% faster than uvloop
I and about the same speed as gevent
68
asyncio in Python 3.5
alternatives
alternatives to asyncio
69
summary
summary
70
summary
asynchronous programming
71
summary
asynchronous programming
72
summary
asynchronous programming
73
summary
Python 3.6
I a christmas present
I minor asyncio improvements
I run_coroutine_threadsafe
I submit coroutines to event loops in other threads
I timeout() context manager
I simplifying timeouts handling code
I all changes backported to 3.5.x
74
summary
Python 3.6
Python 3.6
I deserves a presentation of its own
I but do checkout formatted string literals
75
summary
76
summary
77
asyncio in Python 3.5
behind the screens
extra slides
78
asyncio in Python 3.5
behind the screens
79
asyncio in Python 3.5
behind the screens
80
asyncio in Python 3.5
behind the screens
going async
self._loop.add_reader(self.fileno(),
self._read_ready, n)
82
asyncio in Python 3.5
behind the screens
going async
def read(self, n):
assert self.read_future is None or self.read_future.cancelled()
future = asyncio.Future(loop=self._loop)
. . . # add_reader . . .
return future
83
asyncio in Python 3.5
behind the screens
future objects
def _read_ready(self, n):
self._loop.remove_reader(self.fileno())
if not self.read_future.cancelled():
try:
res = os.read(self.fileno(), n)
except Exception as exc:
self.read_future.set_exception(exc)
else:
self.read_future.set_result(res)
self.read_future = None
84
asyncio in Python 3.5
behind the screens
future objects
return future
85
asyncio in Python 3.5
behind the screens
86