Twisted code ↔ other code
“The only reason anyone cares about Twisted is X”
Python, some Twisted probably helps
An object that reacts to events
Internally: event loop, except for test reactors e.g. Clock
Examples of reactor interfaces to give you an idea what a reactor does Usually you call higher-level APIs!
An object you get now,
gets you result or failure later
Why?
try: result = blocking_read() except SomeError as e: on_failure(e) else: on_result(result)
d = async_read() d.addCallbacks(on_result, on_failure)
try: result = yield async_read() except SomeError as e: on_failure(e) else: on_result(result)
Service Oriented Architecture
Web Server Gateway Interface
twistd web --wsgi=wsgi.app
Show of hands: how many of you:
Almost everything I’m about to say applies to pretty much any event-driven single-threaded thing
Production reactors are just event loops Twisted is single-threaded by default One thing at a time, all in the same thread Concurrency through asynchronous IO Blocking the reactor thread means nothing else happens
def _getDataAtURL(url): return requests.get(url) # BLOCKS!
def _compute(n): x = 2 for _ in xrange(n): # BLOCKS! x *= x send_somewhere(x)
Alternatives:
Don’t block Block another threadIO bound? Asynchronous IO!
CPU bound? Cooperate!
treq: requests-like, but asynchronous
def _getDataAtURL(url): return treq.get(url)
t.internet.task.coiterate & 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
Sometimes all of the above!
Can’t block reactor thread
→ block a different one!
deferToThread is used by a lot of wrappers: adbapi, txscrypt
from twisted.web.client import getPage from crochet import setup, run_in_reactor setup() @run_in_reactor def download_page(url): return getPage(url) url = "http://tm.tl/5000" result = download_page(url) print result.wait()
class ExchangeRate(object): # ... @run_in_reactor def start(self): self._lc = LoopingCall(self._download) self._lc.start(30, now=True) def _download(self): d = getPage(url) # ...
most twisted apis can only be called from the reactor thread _start method in reactor thread because of @runinreactor decorator _download method in reactor thread because of LoopingCall
important part here: Twisted code looks like regular Twisted code! (But remember the @run_in_reactor)
@app.route('/') def index(): 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!
index() will be called in whatever thread the wsgi server runs it
gevent-style automagic suspending
r = waitForDeferred(d)
d = waitForGreenlet(g)
TODO
If you want to use Twisted, you probably can.
That doesn’t necessarily mean it’s a good idea.
Although it obviously is ;-)