Let's look at progressively more modern versions of the same Python code, a very simple async Twisted webserver made with klein and treq. For a related legacy article, check using-twisteds-inlinecallbacks-in-5-steps
What are coroutine functions in Python?#
Coroutine functions are functions that return a coroutine object. You can define these using the async def
syntax. Here is an example of an async/await coroutine function.
async def doit():
a = await x()
return a
await
accepts any Awaitable object.
What is a deferred in Twisted?#
Deferreds are Twisted's way of structuring and handling asynchronous code. They share concepts with Promises in JavaScript and Futures in standard Python. Here is an example of a web server written with deferreds:
import treq # type: ignore
from klein import Klein
from twisted.internet.defer import Deferred
from twisted.web.iweb import IRenderable, IRequest
app = Klein()
def on_error(exc: Exception) -> str:
return str(exc)
@app.route("/")
def default(request: IRequest) -> Deferred:
d = treq.get("https://httpbin.org/get?deferred=yes")
d.addCallback(treq.content)
d.addErrback(on_error)
return d
app.run("localhost", 3000)
Using inlineCallbacks.#
This is the Python2 version of cleaner async code.
import treq # type: ignore
from klein import Klein
from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
from twisted.web.iweb import IRenderable, IRequest
app = Klein()
@app.route("/")
@inlineCallbacks
def default(request: IRequest) -> Deferred:
try:
response = yield treq.get("https://httpbin.org/get?inlineCallbacks=yes")
content = yield treq.content(response)
returnValue(content)
except Exception as exc:
returnValue(str(exc))
app.run("localhost", 3000)
Using async/await#
Let's dive into using Python async/await coroutines with Twisted. Ever since Python added async/await syntax, using inlineCallbacks is not necessary, unless you are still using Python 2.
import treq # type: ignore
from klein import Klein
from twisted.internet.defer import Deferred
from twisted.web.iweb import IRenderable, IRequest
app = Klein()
@app.route("/")
async def default(request: IRequest) -> Deferred:
try:
response = await treq.get("https://httpbin.org/get?async=yes")
content = await treq.content(response)
return content
except Exception as exc:
return str(exc)
app.run("localhost", 3000)