Twisted code ↔ other code
Python, some Twisted probably helps
An object that reacts to events
Time
:callLater
…Process
:spawnProcess
Threads
:call(In|From)Thread
, …(TCP|UDP|SSL|Multicast)
(UNIX|UNIXDatagram|Socket)
FDSet
:(add|remove)(Reader|Writer)
, …
An object you get now,
gets you result or failure later
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
- Flask app, served by
t.w.wsgi
- Real-time chat, with
txsockjs
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 thread
IO 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
- Blocking API:
DBAPI2
,WSGI
… - Opaque code:
scrypt
,Pillow
… - Kernel/syscall level: file IO, …
Can’t block reactor thread
→ block a different one!
- … in the same process:
deferToThread
- … in a child process:
spawnProcess
… - … in a remote process: Ampoule, RPC…
- Easy automagic deferreds!
- Shared mutable state :-(
- Run in reactor thread
- Return
EventualResult
- Synchronous analog of
Deferred
wait(timeout=None)
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()
- Twisted queries exchange rate every 30s
- Flask app serves the latest exchange rate
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)
# ...
@app.route('/')
def index():
rate = EURUSD.latest_value()
if rate is None:
rate = "unavailable"
return "EUR/USD rate: {0}.".format(rate)
app.run()
gevent-style automagic suspending
r = waitForDeferred(d)
d = waitForGreenlet(g)
TODO
- Many supported protocols
- Cooperates with blocking code
- Cooperates with other event loops
If you want to use Twisted, you probably can.
That doesn’t necessarily mean it’s a good idea.
Although it obviously is ;-)