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 ;-)