Hi, I'm lvh.
I hack on and with Twisted.
An object that reacts to events
Usually you call higher-level APIs!
IReactorBase: run, stop…
IReactorTime: callLater…
IReactorProcess: spawnProcess
IReactorThreads: call(In|From)Thread, …
IReactor(TCP|UDP|SSL|Multicast)
IReactor(UNIX|UNIXDatagram|Socket)
IReactorFDSet: (add|remove)(Reader|Writer), …
An object you get now,
gets you result or failure later
Blocking read:
try: result = blocking_read() on_result(result) except SomeError as e: on_failure(e)
Deferred read:
d = async_read()
d.addCallbacks(on_result, on_failure)
Blocking read:
try: result = blocking_read() on_result(result) except SomeError as e: on_failure(e)
inlineCallbacks: Deferreds + sugar
try: result = yield async_read() on_result(result) except SomeError as e: on_failure(e)
Maybe it's trivial to get started!
Service Oriented Architecture
Web Server Gateway Interface
twistd web --wsgi=location.of.wsgi.app
praekelt/vumi
t.w.wsgi
txsockjs
select, epoll, kqueue, IOCP, libev(ent)…
Blocking IO
def _getDataAtURL(url): return requests.get(url).json() # BLOCKS!
Blocking computation
def _compute(n): x = 2 for _ in xrange(n): # BLOCKS! (for large n) x *= x send_somewhere(x)
Therefore, you have two options:
IO bound? Be asynchronous!
CPU bound? Cooperate with the event loop!
treq: requests-like, but asynchronous
def _getDataAtURL(url): d = treq.get(url) d.addCallback(treq.json_content) return d
twisted.internet.task.coiterate and friends
def _compute(n): x = 2 for _ in xrange(n): x *= x yield # Yields to the reactor :) send_somewhere(x) coiterate(_compute(n))
Avoiding blocking isn't always possible
DBAPI2, …
scrypt, …
Can't block the reactor thread → block a different one!
deferToThread
adbapi, txscrypt…
spawnProcess and friends
itamarst/crochet
from crochet import setup
setup()
@run_in_reactor
EventualResult
EventualResult?
Deferred
wait(timeout=None)
cancel(), stash()
from twisted.web.client import getPage from crochet import setup, run_in_reactor setup() @run_in_reactor def download_page(url): return getPage(url) result = download_page("http://www.google.com") print result.wait()
crochet
class ExchangeRate(object): # ... @run_in_reactor def start(self): # in reactor thread because of decorator self._lc = LoopingCall(self._download) self._lc.start(30, now=True) def _download(self): # in reactor thread because of LoopingCall d = getPage(url) # ...
Twisted code looks like regular Twisted code!
(But remember the @run_in_reactor)
@app.route('/') def index(): # runs in whatever thread app.run() runs it in rate = EURUSD.latest_value() if rate is None: rate = "unavailable" return "EUR/USD rate: {0}.".format(rate) app.run()
Flask code looks like regular Flask code!
jyio/geventreactor
import geventreactor
geventreactor.install()
requests.get example: probably okay
r = waitForDeferred(d)
d = waitForGreenlet(g)
TODO
libev(ent), CoreFoundation…
treq is like requests
klein is like Flask
cyclone is Tornado on top of Twisted's reactor