A deep dive into PEP-3156 and the new asyncio module
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 3,696 views

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

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

Statistics

Views

Total Views
3,696
Views on SlideShare
3,661
Embed Views
35

Actions

Likes
10
Downloads
44
Comments
0

2 Embeds 35

https://twitter.com 22
http://www.slideee.com 13

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • A deep dive into PEP-3156 and 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 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()
  • 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
  • asyncio import tulip
  • 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 requires Python > 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? twisted tornado gevent ... asyncio selectors iocp
  • Tornado interop. tornado tornado epoll/kqueue asyncio
  • Rose asyncio pyuv github.com/saghul/rose
  • Architecture
  • Components Event loop, policy Coroutines, Futures, Tasks Transports, Protocols
  • Calculate poll time Poll Run callbacks Event loop
  • 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 from 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))
  • 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.coroutine def 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!
  • 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
  • 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? @saghul bettercallsaghul.com
  • 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