Advertisement
Advertisement

More Related Content

Advertisement

More from Saúl Ibarra Corretgé(20)

Advertisement

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

  1. A deep dive into PEP-3156 and the new asyncio module Saúl Ibarra Corretgé @saghul FOSDEM 2014
  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. import open_source github.com/saghul
  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. 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. Frameworks • Platform abstraction • Protocol implementations • Integration with other event loops: Qt, GLib, ... • Different API styles
  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. 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. import gevent • Uses libevent in version 0.x and libev in 1.x • select() on Windows :-( • Syncronous API using greenlet
  10. import asyncore • raise RuntimeError(“NOT GOOD ENOUGH”) • “asyncore: included batteries don’t fit” bit.ly/182HcHT
  11. Solution!
  12. I’m not trying to reinvent the wheel. I’m trying to build a good one. Guido van Rossum
  13. asyncio import tulip
  14. import asyncio • Reference implementation for PEP-3156 • Basic components for doing async i/o • Works (officially) on Python >= 3.3 [*]
  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. Goals • Unix and Windows support • IPv4 and IPv6 • TCP, UDP and pipes • Basic SSL (secure by default) • Subprocesses
  17. Non goals • Perfection • Replacing current frameworks • Protocol implementations • Replace httplib, smtplib, ... • Make it work on Python < 3.3
  18. Interoperability? twisted tornado gevent ... asyncio selectors iocp
  19. Tornado interop. tornado tornado epoll/kqueue asyncio
  20. Rose asyncio pyuv github.com/saghul/rose
  21. Architecture
  22. Components Event loop, policy Coroutines, Futures, Tasks Transports, Protocols
  23. Calculate poll time Poll Run callbacks Event loop
  24. Event loop & policy • Chooses the best i/o mechanism for a given platform • APIs for creating server and client connections (TCP, UDP, ...)
  25. Callbacks • loop.call_soon(func, *args) • loop.call_later(delay, func, *args) • loop.call_at(when, func, *args) • loop.time()
  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. Unix signals • loop.add_signal_handler(sig, func, *args) • loop.remove_signal_handler(sig)
  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. Starting / stopping • loop.run_forever() • loop.stop() • loop.run_until_complete(f)
  30. The loop instance • get_event_loop() • set_event_loop(loop) • new_event_loop()
  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. Policy • Configures what get/set/new _event_loop do • The single global object • It can be changed (example: rose)
  33. Coroutines, Futures & Tasks
  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. 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. 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. 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. 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. 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. 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. 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. 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. Transports & Protocols
  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. 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. 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. Transport -> Protocol • connection_made(transport) • data_received(data) • eof_received() • connection_lost(exc) • UDP, pipes and subprocesses are slightly different
  48. Protocol -> Transport • write(data) • writelines(seq) • write_eof() • close()
  49. More Transport methods • can_write_eof() • abort() • get_extra_info(key) • ‘socket’ • ‘sockname’, ‘peername’ • ‘sslcontext’ • ...
  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. Status • PEP (provisionally) accepted • Available since Python 3.4b1 and on PyPI • Still evolving!
  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. Questions? @saghul bettercallsaghul.com
  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
Advertisement