A deep dive into PEP-3156 and the new asyncio module
-
Upload
saul-ibarra-corretge -
Category
Technology
-
view
6.620 -
download
1
description
Transcript of 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
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>>>
import open_source
github.com/saghul
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()
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
Frameworks
• Platform abstraction• Protocol implementations• Integration with other event loops: Qt,
GLib, ...• Different API styles
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
import tornado
• Uses select, poll, kqueue, epoll from the select module
• select() on Windows :-(• Mainly oriented to web development• Synchronous looking API with
coroutines
import gevent
• Uses libevent in version 0.x and libev in 1.x
• select() on Windows :-(
• Syncronous API using greenlet
import asyncore
• raise RuntimeError(“NOT GOOD ENOUGH”)• “asyncore: included batteries don’t fit”
bit.ly/182HcHT
Solution!
I’m not trying to reinvent the wheel. I’m trying to build a good one.
Guido van Rossum
import tulipasyncio
import asyncio
• Reference implementation for PEP-3156
• Basic components for doing async i/o• Works (officially) on Python >= 3.3 [*]
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
Goals
• Unix and Windows support
• IPv4 and IPv6
• TCP, UDP and pipes
• Basic SSL (secure by default)
• Subprocesses
Non goals
• Perfection• Replacing current frameworks• Protocol implementations• Replace httplib, smtplib, ...• Make it work on Python < 3.3
Interoperability?
selectors iocp
asyncio
twisted tornado gevent ...
Tornado interop.
asyncio
tornado
epoll/kqueue
tornado
Rose
pyuv
asyncio
github.com/saghul/rose
Architecture
Components
Event loop, policy
Coroutines, Futures, Tasks
Transports, Protocols
Event loop
Calculatepoll time Poll
Runcallbacks
Event loop & policy
• Chooses the best i/o mechanism for a given platform
• APIs for creating server and client connections (TCP, UDP, ...)
Callbacks
• loop.call_soon(func, *args)
• loop.call_later(delay, func, *args)
• loop.call_at(when, func, *args)
• loop.time()
Callbacks for I/O
• loop.add_reader(fd, func, *args)
• loop.add_writer(fd, func, *args)
• loop.remove_reader(fd)
• loop.remove_writer(fd)
Unix signals
• loop.add_signal_handler(sig, func, *args)• loop.remove_signal_handler(sig)
Working with threads
• loop.call_soon_threadsafe(func, *args)• loop.run_in_executor(exc, func, *args)• loop.set_default_executor(exc)
• PEP-3148 executor
Starting / stopping
• loop.run_forever()• loop.stop()• loop.run_until_complete(f)
The loop instance
• get_event_loop()• set_event_loop(loop)• new_event_loop()
Policy (default)
• Defines the event loop context• One event loop per thread• An event lop is automagically created
just for the main thread
Policy
• Configures what get/set/new _event_loop do
• The single global object• It can be changed (example: rose)
Coroutines, Futures & Tasks
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
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))
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)
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()
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
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)
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(...))
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!
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()
Transports & Protocols
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
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
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
Transport -> Protocol
• connection_made(transport)• data_received(data)• eof_received()• connection_lost(exc)
• UDP, pipes and subprocesses are slightly different
Protocol -> Transport
• write(data)• writelines(seq)• write_eof()• close()
More Transport methods
• can_write_eof()• abort()• get_extra_info(key)• ‘socket’• ‘sockname’, ‘peername’• ‘sslcontext’• ...
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
Status
• PEP (provisionally) accepted
• Available since Python 3.4b1 and on PyPI
• Still evolving!
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
Questions?
bettercallsaghul.com@saghul
References
• code.google.com/p/tulip/• groups.google.com/forum/#!forum/
python-tulip• PEP-3156• http://www.youtube.com/watch?
v=1coLC-MUCJc• https://www.dropbox.com/s/
essjj4qmmtrhys4/SFMeetup2013.pdf