Twisted is Easy

Credit: Thanks Glyph Lefkowitz for pointing out some of the reasons people think Twisted is hard.

Note: Please treat this talk, and all alternative versions, as CC-BY:

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Alternative versions:

Twisted is easy. It makes it easy to write high-performance networking code. Yes, really easy. The reason we write papers like “Quantum electronics and Twisted” is because Twisted makes it so easy, we enjoy doing complicated things with it.

There are some things which just should not be complicated. Writing a custom protocol implementation should not be complicated. Writing a simple web interface should not be be complicated. Most importantly, writing code that exposes a set of objects through several protocols should not be complicated. How easy is it to do these things with twisted?

First, here is how to do nothing with Twisted:

from twisted.internet import reactor
reactor.run()

Yes, it’s not the zero program. However, doing nothing with most other frameworks takes considerably more set-up code. But you don’t want to do nothing! You want to do something! Something like implementing a network protocol, maybe?

from twisted.internet import reactor, protocol

That’s the import line. That’s not interesting.

class Echo(protocol.Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

That’s the actual code. Echo is a real network protocol, which is specified to parrot back its input.

factory = protocol.Factory()
factory.protocol = Echo
reactor.listenTCP(1033, factory)

Now we set up the factory, get it to listen on a network port, and…

reactor.run()

We’re done! That’s all there is to it. Maybe, instead, you want to write a web application. While Twisted supports WSGI natively, so you can use any Python web framework, maybe you just want to write your code already, dammit! No problems:

from twisted.internet import reactor
from twisted.web import resource, server

Imports. Yawn.

class Resource(resource.Resource):
    isLeaf = True
    users = 0
    def render_GET(self, request):
        self.users += 1
        return "Welcome user number %d" % self.users

Ooooh, that looks interesting, doesn’t it? isLeaf means that all URLs under this resource will be rendered with the resource itself, without trying to find children. The render_GET method is called when GET HTTP requests are received, and its output is displayed. The output is actually HTML, but for brevity, and because browsers survive it, we did not add any tags.

reactor.listenTCP(1080, server.Site(Resource()))

Set-up. We’ve seen this earlier, this isn’t interesting anymore.

reactor.run()

We finish with the nothing.

But so what? You already had a way to write web services and network protocols. But Twisted allows you to have them communicate in fun ways. A good example would be a game where you want to be able to show the highscore table on the web, as the players keep playing through an application-specific protocol. Writing the whole game is beyond my scope, but here are the core ideas in Twisted that would apply:

from twisted.internet import reactor, protocol
from twisted.web import resource, server

Imports…but here comes the exciting part!

class Counter(object):
    count = 0
    def add(self, number):
        self.count += number

A POPO (Plain Old Python Object) if you will. This object just increments a counter. Not interesting until you do something with it…

class ByteCount(protocol.Protocol):
    def dataReceived(self, data):
        self.factory.counter.add(len(data))

…like use it in a protocol. We count the total number of bytes we get. Nothing here should look strange, by now.

class Resource(resource.Resource):
    isLeaf = True
    users = 0
    counter = None
    def render_GET(self, request):
        self.users += 1
        return "Received %d bytes" % self.counter.count

By now, we have seen most everything. But where does the “counter” object come from?

factory = protocol.Factory()
factory.protocol = ByteCount
factory.counter = Counter()
resource = Resource()
resource.counter = factory.counter

Object set-up. Here we create all the objects we need, wiring them up correctly.

reactor.listenTCP(1033, factory)
reactor.listenTCP(1080, server.Site(resource))

Networking set-up — we’ve seen this earlier.

reactor.run()

The loop.

That’s it! That’s how easy it is to write a multiprotocol application in Twisted. Did you notice, by the way, how easy it was to count total users? Try doing it with Django sometimes, just for fun!

Leave a comment