2. Oleksandr Fedorov
• Software Engineer.
• More than 5 years in Python.
• 1.5 years of QA Automation.
E-mail: a.g.fedorof@gmail.com
Skype ID: a.g.fedorof
3. Agenda
1. The evolution of a loop.
2. We will call you back.
3. The Future is here.
4. Await asynchronously.
BDD with Python
5. The evolution of a loop
from anykey import get_key
def loop():
while True:
key = get_key()
if key is not None:
break
if __name__ == '__main__':
loop()
6. The evolution of a loop
Tasks: 183 total, 2 running, 181 sleeping, 0 stopped, 0 zombie
PID USER PR NI SHR S %CPU %MEM TIME+ COMMAND
1949 alex 20 0 4408 R 99,7 0,1 0:29.94 python
1602 alex 20 0 28484 S 3,7 1,2 6:20.05 Viber
29713 alex 20 0 79952 S 2,0 1,9 1:41.21 skype
640 root 20 0 23980 S 1,3 0,4 2:29.21 Xorg
7. The evolution of a loop
import time
from anykey import get_key
def loop():
while True:
key = get_key()
if key is not None:
break
time.sleep(0.5)
if __name__ == '__main__':
loop()
8. The evolution of a loop – `select` example.
# twisted/internet/selectreactor.py
# class SelectReactor... Kinda of.
def doSelect(self, timeout):
r, w, ignored = _select(self._reads,
self._writes,
[], timeout)
for selectables, method in ((r, "doRead"), (w, "doWrite")):
for selectable in selectables:
self._doReadOrWrite(selectable, method)
9. The evolution of a loop. I/O loops.
# Our example
loop() # Twisted
from twisted.internet import reactor
reactor.run()
# Tornado
import tornado.ioloop
tornado.ioloop.IOLoop.current().start()
# pygame
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
pygame.display.flip()
# Tkinter
App().mainloop()
# Asyncio
import asyncio
asyncio.get_event_loop().run_forever()
# JavaScript
/* Nothing to do, it already runs */
18. The Future is here
Future (aka Deferred, aka Promise)
• Composite of callbacks.
• Callbacks can be added in both "pending" and "done" states.
• The result passed to a future is propagated to its callbacks.
• It's possible to propagate errors (exceptions).
20. The Future is here – Promise in JavaScript
/* A function that returns a promise */
function readFile(filename, enc){
return new Promise(function (fulfill, reject){
fs.readFile(filename, enc, function (err, res){
if (err) reject(err);
else fulfill(res);
});
});
}
/* Adding callbacks */
readFile(filename, 'utf8').then(
function (res){
alert('Done: ' + res);
},
function (reason){
alert('Error: ' + reason);
}
);
readFile(filename, 'utf8').then(...).then(...).then(...);
21. The Future is here – Deferred in Twisted
from twisted.internet import defer
def get_data():
d = defer.Deferred()
d.callback(3)
return d
def print_result(res):
print res
raise AssertionError('Catch me!')
def handle_exception(failure):
failure.trap(AssertionError)
sys.stderr.write(str(failure))
d = get_data()
d.addCallback(print_data)
d.addErrback(handle_exception)
22. The Future is here – Future in asyncio
(not to be confused with concurrent.futures.Future)
import asyncio
def get_data():
future = asyncio.Future()
future.set_result('Future is done!')
return future
def print_result(future):
print(future.result())
loop.stop()
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(get_data())
future.add_done_callback(print_result)
try:
loop.run_forever()
finally:
loop.close()
23. The Future is here
Problems
• Spaghetti code.
• The execution flow is not clear.
• Errors handling is harder.
32. Await asynchronously – Some rules
async def my_coroutine(*args):
res = await get_data()
1. Use async def to create a coroutine.
2. Use await to get real data from a
future, coroutine or a task.
async def do_stuff():
try:
return await moar_stuff()
except SomeException:
return None
3. Handle exceptions as usual.
class ClassWithCoroutines:
def __init__(self, loop):
self._loop = loop
4. Keep your loop around.
async with asyncfile() as file:
async for line in file:
print(line)
5. Use async with and async for. They
are awesome.