a deep dive into pep-3156 and the new asyncio module

55
A deep dive into PEP-3156 and the new asyncio module Saúl Ibarra Corretgé @saghul FOSDEM 2014

Upload: saul-ibarra-corretge

Post on 06-May-2015

6.621 views

Category:

Technology


1 download

DESCRIPTION

Slides from the talk I gave at FOSDEM 2014 about what PEP-3156 specifies and how the asyncio module works.

TRANSCRIPT

Page 1: A deep dive into PEP-3156 and the new asyncio module

A deep dive into PEP-3156and the new asyncio module

Saúl Ibarra Corretgé@saghul

FOSDEM 2014

Page 2: A deep dive into PEP-3156 and the new asyncio module

repr(self)>>> from Amsterdam import saghul>>>>>> saghul.work()VoIP, SIP, XMPP, chat, Real Time Communications>>>>>> saghul.other()networking, event loops, sockets, MOAR PYTHON>>>>>> saghul.languages()Python, C>>>

Page 3: A deep dive into PEP-3156 and the new asyncio module

import open_source

github.com/saghul

Page 4: A deep dive into PEP-3156 and the new asyncio module

import socketimport socket

server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)server.bind(('127.0.0.1', 1234))server.listen(128)print("Server listening on: {}".format(server.getsockname()))

client, addr = server.accept()print("Client connected: {}".format(addr))

while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data)

server.close()

Page 5: A deep dive into PEP-3156 and the new asyncio module

I/O is hard

• Sync i/o is bad, async i/o is Good (TM)• Different paradigms in Unix vs

Windows• “are you ready?” vs “call me later”

• Event loops are The Way To Go• See the c10k problem

Page 6: A deep dive into PEP-3156 and the new asyncio module
Page 7: A deep dive into PEP-3156 and the new asyncio module

Frameworks

• Platform abstraction• Protocol implementations• Integration with other event loops: Qt,

GLib, ...• Different API styles

Page 8: A deep dive into PEP-3156 and the new asyncio module

import twisted

• Uses select, poll, kqueue, epoll from the select module

• IOCP on Windows• Integration with other event loops: Qt• Factory/Protocol/Transport

abstractions• Deferred

Page 9: A deep dive into PEP-3156 and the new asyncio module

import tornado

• Uses select, poll, kqueue, epoll from the select module

• select() on Windows :-(• Mainly oriented to web development• Synchronous looking API with

coroutines

Page 10: A deep dive into PEP-3156 and the new asyncio module

import gevent

• Uses libevent in version 0.x and libev in 1.x

• select() on Windows :-(

• Syncronous API using greenlet

Page 11: A deep dive into PEP-3156 and the new asyncio module

import asyncore

• raise RuntimeError(“NOT GOOD ENOUGH”)• “asyncore: included batteries don’t fit”

bit.ly/182HcHT

Page 12: A deep dive into PEP-3156 and the new asyncio module

Solution!

Page 13: A deep dive into PEP-3156 and the new asyncio module

I’m not trying to reinvent the wheel. I’m trying to build a good one.

Guido van Rossum

Page 14: A deep dive into PEP-3156 and the new asyncio module

import tulipasyncio

Page 15: A deep dive into PEP-3156 and the new asyncio module

import asyncio

• Reference implementation for PEP-3156

• Basic components for doing async i/o• Works (officially) on Python >= 3.3 [*]

Page 16: A deep dive into PEP-3156 and the new asyncio module

Goals

• Modern implementation of async i/o for Python

• Use yield from (PEP-380)• But don’t force it

• Don’t use anything that requiresPython > 3.3

• Interoperability with other frameworks

Page 17: A deep dive into PEP-3156 and the new asyncio module

Goals

• Unix and Windows support

• IPv4 and IPv6

• TCP, UDP and pipes

• Basic SSL (secure by default)

• Subprocesses

Page 18: A deep dive into PEP-3156 and the new asyncio module

Non goals

• Perfection• Replacing current frameworks• Protocol implementations• Replace httplib, smtplib, ...• Make it work on Python < 3.3

Page 19: A deep dive into PEP-3156 and the new asyncio module

Interoperability?

selectors iocp

asyncio

twisted tornado gevent ...

Page 20: A deep dive into PEP-3156 and the new asyncio module

Tornado interop.

asyncio

tornado

epoll/kqueue

tornado

Page 21: A deep dive into PEP-3156 and the new asyncio module

Rose

pyuv

asyncio

github.com/saghul/rose

Page 22: A deep dive into PEP-3156 and the new asyncio module

Architecture

Page 23: A deep dive into PEP-3156 and the new asyncio module

Components

Event loop, policy

Coroutines, Futures, Tasks

Transports, Protocols

Page 24: A deep dive into PEP-3156 and the new asyncio module

Event loop

Calculatepoll time Poll

Runcallbacks

Page 25: A deep dive into PEP-3156 and the new asyncio module

Event loop & policy

• Chooses the best i/o mechanism for a given platform

• APIs for creating server and client connections (TCP, UDP, ...)

Page 26: A deep dive into PEP-3156 and the new asyncio module

Callbacks

• loop.call_soon(func, *args)

• loop.call_later(delay, func, *args)

• loop.call_at(when, func, *args)

• loop.time()

Page 27: A deep dive into PEP-3156 and the new asyncio module

Callbacks for I/O

• loop.add_reader(fd, func, *args)

• loop.add_writer(fd, func, *args)

• loop.remove_reader(fd)

• loop.remove_writer(fd)

Page 28: A deep dive into PEP-3156 and the new asyncio module

Unix signals

• loop.add_signal_handler(sig, func, *args)• loop.remove_signal_handler(sig)

Page 29: A deep dive into PEP-3156 and the new asyncio module

Working with threads

• loop.call_soon_threadsafe(func, *args)• loop.run_in_executor(exc, func, *args)• loop.set_default_executor(exc)

• PEP-3148 executor

Page 30: A deep dive into PEP-3156 and the new asyncio module

Starting / stopping

• loop.run_forever()• loop.stop()• loop.run_until_complete(f)

Page 31: A deep dive into PEP-3156 and the new asyncio module

The loop instance

• get_event_loop()• set_event_loop(loop)• new_event_loop()

Page 32: A deep dive into PEP-3156 and the new asyncio module

Policy (default)

• Defines the event loop context• One event loop per thread• An event lop is automagically created

just for the main thread

Page 33: A deep dive into PEP-3156 and the new asyncio module

Policy

• Configures what get/set/new _event_loop do

• The single global object• It can be changed (example: rose)

Page 34: A deep dive into PEP-3156 and the new asyncio module

Coroutines, Futures & Tasks

Page 35: A deep dive into PEP-3156 and the new asyncio module

Coroutines, Futures & Tasks

• Coroutines• a generator function, can receive values

• decorated with @coroutine

• Future• promise of a result or an error

• Task• Future which runs a coroutine

Page 36: A deep dive into PEP-3156 and the new asyncio module

Coroutines & yield fromimport asyncioimport socket

loop = asyncio.get_event_loop()

@asyncio.coroutinedef handle_client(client, addr): print("Client connected: {}".format(addr)) while True: data = yield from loop.sock_recv(client, 4096) if not data: print("Client has disconnected") break client.send(data)

@asyncio.coroutinedef accept_connections(server_socket): while True: client, addr = yield from loop.sock_accept(server_socket) asyncio.async(handle_client(client, addr))

server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)server.bind(('127.0.0.1', 1234))server.listen(128)server.setblocking(False)print("Server listening on: {}".format(server.getsockname()))

loop.run_until_complete(accept_connections(server))

Page 37: A deep dive into PEP-3156 and the new asyncio module

Coroutines & yield from

• Imagine the yield from is not there• Imagine the code is executed

sequentially• Not exactly the formal definition of yield

from (from PEP-380)

Page 38: A deep dive into PEP-3156 and the new asyncio module

Futures

• Similar to Futures from PEP-3148• concurrent.futures.Future

• API (almost) identical:• f.set_result(); r = f.result()

• f.set_exception(e); e = f.exception()

• f.done()

• f.add_done_callback(x); f.remove_done_callback(x)

• f.cancel(); f.cancelled()

Page 39: A deep dive into PEP-3156 and the new asyncio module

Futures + Coroutines

• yield from works with Futures!• f = Future()

• Someone will set the result or exception

• r = yield from f• Waits until done and returns f.result()

• Usually returned by functions

Page 40: A deep dive into PEP-3156 and the new asyncio module

Undoing callbacks

@asyncio.coroutinedef sync_looking_function(*args): fut = asyncio.Future() def cb(result, error): if error is not None: fut.set_result(result) else: fut.set_exception(Exception(error)) async_function(cb, *args) return (yield from fut)

Page 41: A deep dive into PEP-3156 and the new asyncio module

Tasks

• Unicorns covered in fairy dust• It’s a coroutine wrapped in a Future• WAT• Inherits from Future• Works with yield from• r = yield from Task(coro(...))

Page 42: A deep dive into PEP-3156 and the new asyncio module

Tasks vs coroutines

• A coroutine doesn’t “advance” without a scheduling mechanism

• Tasks can advance in their own• The event loop is the scheduler!• Magic!

Page 43: A deep dive into PEP-3156 and the new asyncio module

Exampleimport asyncio

loop = asyncio.get_event_loop()clients = {} # task -> (reader, writer)

def accept_client(client_reader, client_writer): task = asyncio.Task(handle_client(client_reader, client_writer)) clients[task] = (client_reader, client_writer)

def client_done(task): del clients[task]

task.add_done_callback(client_done)

@asyncio.coroutinedef handle_client(client_reader, client_writer): while True: data = (yield from client_reader.readline()) client_writer.write(data)

f = asyncio.start_server(accept_client, '127.0.0.1', 12345)server = loop.run_until_complete(f)loop.run_forever()

Page 44: A deep dive into PEP-3156 and the new asyncio module

Transports & Protocols

Page 45: A deep dive into PEP-3156 and the new asyncio module

Transports & Protocols

• Transport: represents a connection (socket, pipe, ...)

• Protocol: represents an application (HTTP server, IRC client, ...)

• They always go together

• API is based on function calls and callbacks

Page 46: A deep dive into PEP-3156 and the new asyncio module

Clients & servers

• loop.create_connection(...)• creates a Transport and a Protocol

• loop.create_server(...)• creates a Transport and a Protocol for each

accepted connection

• returns a Server object

Page 47: A deep dive into PEP-3156 and the new asyncio module

Clients & servers

• loop.open_connection(...)• wrapper around create_connection, returns

(stream_reader, stream_writer)

• loop.start_server(...)• wrapper around create_server, calls a

callback with (stream_reader, stream_writer) for each accepted conection

Page 48: A deep dive into PEP-3156 and the new asyncio module

Transport -> Protocol

• connection_made(transport)• data_received(data)• eof_received()• connection_lost(exc)

• UDP, pipes and subprocesses are slightly different

Page 49: A deep dive into PEP-3156 and the new asyncio module

Protocol -> Transport

• write(data)• writelines(seq)• write_eof()• close()

Page 50: A deep dive into PEP-3156 and the new asyncio module

More Transport methods

• can_write_eof()• abort()• get_extra_info(key)• ‘socket’• ‘sockname’, ‘peername’• ‘sslcontext’• ...

Page 51: A deep dive into PEP-3156 and the new asyncio module

Enter Trollius!

• Backport of asyncio for Python >= 2.6

• By Victor Stinner

• Slightly different syntax

• yield instead of yield from

• raise Return(x) instead of return x on generators

• pip install trollius

Page 52: A deep dive into PEP-3156 and the new asyncio module

Status

• PEP (provisionally) accepted

• Available since Python 3.4b1 and on PyPI

• Still evolving!

Page 53: A deep dive into PEP-3156 and the new asyncio module

That was all?

• Yes and No

• Go read PEP-3156

• Implement a simple protocol (IRC client)

• Checkout the third party libraries

• Use asyncio in your next project

Page 54: A deep dive into PEP-3156 and the new asyncio module

Questions?

bettercallsaghul.com@saghul