Your SlideShare is downloading. ×
0
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
>>>
>>> ...
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...
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 ...
Frameworks
• Platform abstraction
• Protocol implementations
• Integration with other event loops: Qt,
GLib, ...
• Differen...
import twisted
• Uses select, poll, kqueue, epoll from
the select module
• IOCP on Windows
• Integration with other event ...
import tornado
• Uses select, poll, kqueue, epoll from
the select module
• select() on Windows :-(
• Mainly oriented to we...
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 Pytho...
Goals
• Modern implementation of async i/o for
Python
• Use yield from (PEP-380)
• But don’t force it

• Don’t use anythin...
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...
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
connectio...
Callbacks

• loop.call_soon(func, *args)
• loop.call_later(delay, func, *args)
• loop.call_at(when, func, *args)
• loop.ti...
Callbacks for I/O

• loop.add_reader(fd, func, *args)
• loop.add_writer(fd, func, *args)
• loop.remove_reader(fd)
• loop.r...
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_...
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...
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
...
Coroutines & yield from
import asyncio
import socket
loop = asyncio.get_event_loop()
@asyncio.coroutine
def handle_client(...
Coroutines & yield from

• Imagine the yield from is not there
• Imagine the code is executed
sequentially
• Not exactly t...
Futures
• Similar to Futures from PEP-3148
•

concurrent.futures.Future

• API (almost) identical:
•

f.set_result(); r = ...
Futures + Coroutines
• yield from works with Futures!
• f = Future()
•

Someone will set the result or exception

• r = yi...
Undoing callbacks
@asyncio.coroutine
def sync_looking_function(*args):
fut = asyncio.Future()
def cb(result, error):
if er...
Tasks
• Unicorns covered in fairy dust
• It’s a coroutine wrapped in a Future
• WAT
• Inherits from Future
• Works with yi...
Tasks vs coroutines
• A coroutine doesn’t “advance” without
a scheduling mechanism
• Tasks can advance in their own
• The ...
Example
import asyncio
loop = asyncio.get_event_loop()
clients = {} # task -> (reader, writer)
def accept_client(client_re...
Transports & Protocols
Transports & Protocols

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

• Protocol: represents an application
(H...
Clients & servers
• loop.create_connection(...)
• creates a Transport and a Protocol

• loop.create_server(...)
• creates ...
Clients & servers
• loop.open_connection(...)
• wrapper around create_connection, returns
(stream_reader, stream_writer)

...
Transport -> Protocol

• connection_made(transport)
• data_received(data)
• eof_received()
• connection_lost(exc)
• UDP, p...
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...
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 libr...
Questions?

@saghul
bettercallsaghul.com
References
• code.google.com/p/tulip/
• groups.google.com/forum/#!forum/
python-tulip
• PEP-3156
• http://www.youtube.com/...
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

7,492

Published on

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

Published in: Technology, Education
0 Comments
19 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
7,492
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
96
Comments
0
Likes
19
Embeds 0
No embeds

No notes for slide

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

  1. 1. A deep dive into PEP-3156 and the new asyncio module Saúl Ibarra Corretgé @saghul FOSDEM 2014
  2. 2. 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 >>>
  3. 3. import open_source github.com/saghul
  4. 4. 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. 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
  6. 6. Frameworks • Platform abstraction • Protocol implementations • Integration with other event loops: Qt, GLib, ... • Different API styles
  7. 7. 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
  8. 8. import tornado • Uses select, poll, kqueue, epoll from the select module • select() on Windows :-( • Mainly oriented to web development • Synchronous looking API with coroutines
  9. 9. import gevent • Uses libevent in version 0.x and libev in 1.x • select() on Windows :-( • Syncronous API using greenlet
  10. 10. import asyncore • raise RuntimeError(“NOT GOOD ENOUGH”) • “asyncore: included batteries don’t fit” bit.ly/182HcHT
  11. 11. Solution!
  12. 12. I’m not trying to reinvent the wheel. I’m trying to build a good one. Guido van Rossum
  13. 13. asyncio import tulip
  14. 14. import asyncio • Reference implementation for PEP-3156 • Basic components for doing async i/o • Works (officially) on Python >= 3.3 [*]
  15. 15. 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
  16. 16. Goals • Unix and Windows support • IPv4 and IPv6 • TCP, UDP and pipes • Basic SSL (secure by default) • Subprocesses
  17. 17. Non goals • Perfection • Replacing current frameworks • Protocol implementations • Replace httplib, smtplib, ... • Make it work on Python < 3.3
  18. 18. Interoperability? twisted tornado gevent ... asyncio selectors iocp
  19. 19. Tornado interop. tornado tornado epoll/kqueue asyncio
  20. 20. Rose asyncio pyuv github.com/saghul/rose
  21. 21. Architecture
  22. 22. Components Event loop, policy Coroutines, Futures, Tasks Transports, Protocols
  23. 23. Calculate poll time Poll Run callbacks Event loop
  24. 24. Event loop & policy • Chooses the best i/o mechanism for a given platform • APIs for creating server and client connections (TCP, UDP, ...)
  25. 25. Callbacks • loop.call_soon(func, *args) • loop.call_later(delay, func, *args) • loop.call_at(when, func, *args) • loop.time()
  26. 26. Callbacks for I/O • loop.add_reader(fd, func, *args) • loop.add_writer(fd, func, *args) • loop.remove_reader(fd) • loop.remove_writer(fd)
  27. 27. Unix signals • loop.add_signal_handler(sig, func, *args) • loop.remove_signal_handler(sig)
  28. 28. Working with threads • loop.call_soon_threadsafe(func, *args) • loop.run_in_executor(exc, func, *args) • loop.set_default_executor(exc) • PEP-3148 executor
  29. 29. Starting / stopping • loop.run_forever() • loop.stop() • loop.run_until_complete(f)
  30. 30. The loop instance • get_event_loop() • set_event_loop(loop) • new_event_loop()
  31. 31. Policy (default) • Defines the event loop context • One event loop per thread • An event lop is automagically created just for the main thread
  32. 32. Policy • Configures what get/set/new _event_loop do • The single global object • It can be changed (example: rose)
  33. 33. Coroutines, Futures & Tasks
  34. 34. 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
  35. 35. 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))
  36. 36. 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)
  37. 37. 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()
  38. 38. 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
  39. 39. 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)
  40. 40. 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(...))
  41. 41. 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!
  42. 42. 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()
  43. 43. Transports & Protocols
  44. 44. 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
  45. 45. 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
  46. 46. 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
  47. 47. Transport -> Protocol • connection_made(transport) • data_received(data) • eof_received() • connection_lost(exc) • UDP, pipes and subprocesses are slightly different
  48. 48. Protocol -> Transport • write(data) • writelines(seq) • write_eof() • close()
  49. 49. More Transport methods • can_write_eof() • abort() • get_extra_info(key) • ‘socket’ • ‘sockname’, ‘peername’ • ‘sslcontext’ • ...
  50. 50. 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
  51. 51. Status • PEP (provisionally) accepted • Available since Python 3.4b1 and on PyPI • Still evolving!
  52. 52. 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
  53. 53. Questions? @saghul bettercallsaghul.com
  54. 54. 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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×