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