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)