A deep dive into PEP-3156 and the new asyncio module
The document provides an in-depth look at PEP 3156 and the asyncio module in Python, outlining its architecture, goals, and components for asynchronous I/O programming. It discusses existing frameworks and their capabilities, highlights asyncio's interoperability with them, and explains key concepts like coroutines, futures, and tasks. The document concludes by encouraging readers to explore PEP 3156 further and consider using asyncio in future projects.
import socket
import 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()
5.
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
import twisted
• Usesselect, poll, kqueue, epoll from
the select module
• IOCP on Windows
• Integration with other event loops: Qt
• Factory/Protocol/Transport
abstractions
• Deferred
9.
import tornado
• Usesselect, poll, kqueue, epoll from
the select module
• select() on Windows :-(
• Mainly oriented to web development
• Synchronous looking API with
coroutines
10.
import gevent
• Useslibevent in version 0.x and libev in
1.x
• select() on Windows :-(
• Syncronous API using greenlet
11.
import asyncore
• raiseRuntimeError(“NOT GOOD ENOUGH”)
• “asyncore: included batteries don’t fit”
bit.ly/182HcHT
import asyncio
• Referenceimplementation for
PEP-3156
• Basic components for doing async i/o
• Works (officially) on Python >= 3.3 [*]
16.
Goals
• Modern implementationof async i/o for
Python
• Use yield from (PEP-380)
• But don’t force it
• Don’t use anything that requires
Python > 3.3
• Interoperability with other frameworks
17.
Goals
• Unix andWindows support
• IPv4 and IPv6
• TCP, UDP and pipes
• Basic SSL (secure by default)
• Subprocesses
18.
Non goals
• Perfection
•Replacing current frameworks
• Protocol implementations
• Replace httplib, smtplib, ...
• Make it work on Python < 3.3
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
36.
Coroutines & yieldfrom
import asyncio
import socket
loop = asyncio.get_event_loop()
@asyncio.coroutine
def 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.coroutine
def 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))
37.
Coroutines & yieldfrom
• Imagine the yield from is not there
• Imagine the code is executed
sequentially
• Not exactly the formal definition of yield
from (from PEP-380)
38.
Futures
• Similar toFutures 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()
39.
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
Tasks
• Unicorns coveredin fairy dust
• It’s a coroutine wrapped in a Future
• WAT
• Inherits from Future
• Works with yield from
• r = yield from Task(coro(...))
42.
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!
43.
Example
import 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.coroutine
def 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()
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
46.
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
47.
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
48.
Transport -> Protocol
•connection_made(transport)
• data_received(data)
• eof_received()
• connection_lost(exc)
• UDP, pipes and subprocesses are
slightly different
More Transport methods
•can_write_eof()
• abort()
• get_extra_info(key)
• ‘socket’
• ‘sockname’, ‘peername’
• ‘sslcontext’
• ...
51.
Enter Trollius!
• Backportof 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
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