Designing Twisted Applications

Goals

This document describes how a good Twisted application is structured. It should be useful for beginning Twisted developers who want to structure their code in a clean, maintainable way that reflects current best practices.

Readers will want to be familiar with writing servers and clients using Twisted.

Example of a modular design: TwistedQuotes

TwistedQuotes is a very simple plugin which is a great demonstration of Twisted’s power. It will export a small kernel of functionality – Quote of the Day – which can be accessed through every interface that Twisted supports: web pages, e-mail, instant messaging, a specific Quote of the Day protocol, and more.

Set up the project directory

See the description of setting up the TwistedQuotes example .

A Look at the Heart of the Application

quoters.py

from random import choice

from zope.interface import implementer

from TwistedQuotes import quoteproto



@implementer(quoteproto.IQuoter)
class StaticQuoter:
    """
    Return a static quote.
    """
    def __init__(self, quote):
        self.quote = quote


    def getQuote(self):
        return self.quote



@implementer(quoteproto.IQuoter)
class FortuneQuoter:
    """
    Load quotes from a fortune-format file.
    """
    def __init__(self, filenames):
        self.filenames = filenames


    def getQuote(self):
        with open(choice(self.filenames)) as quoteFile:
            quotes = quoteFile.read().split('\n%\n')
        return choice(quotes)

This code listing shows us what the Twisted Quotes system is all about. The code doesn’t have any way of talking to the outside world, but it provides a library which is a clear and uncluttered abstraction: “give me the quote of the day” .

Note that this module does not import any Twisted functionality at all! The reason for doing things this way is integration. If your “business objects” are not stuck to your user interface, you can make a module that can integrate those objects with different protocols, GUIs, and file formats. Having such classes provides a way to decouple your components from each other, by allowing each to be used independently.

In this manner, Twisted itself has minimal impact on the logic of your program. Although the Twisted “dot products” are highly interoperable, they also follow this approach. You can use them independently because they are not stuck to each other. They communicate in well-defined ways, and only when that communication provides some additional feature. Thus, you can use twisted.web with twisted.enterprise , but neither requires the other, because they are integrated around the concept of Deferreds .

Your Twisted applications should follow this style as much as possible. Have (at least) one module which implements your specific functionality, independent of any user-interface code.

Next, we’re going to need to associate this abstract logic with some way of displaying it to the user. We’ll do this by writing a Twisted server protocol, which will respond to the clients that connect to it by sending a quote to the client and then closing the connection. Note: don’t get too focused on the details of this – different ways to interface with the user are 90% of what Twisted does, and there are lots of documents describing the different ways to do it.

quoteproto.py

from zope.interface import Interface

from twisted.internet.protocol import Factory, Protocol



class IQuoter(Interface):
    """
    An object that returns quotes.
    """
    def getQuote():
        """
        Return a quote.
        """



class QOTD(Protocol):
    def connectionMade(self):
        self.transport.write(self.factory.quoter.getQuote()+'\r\n')
        self.transport.loseConnection()



class QOTDFactory(Factory):
    """
    A factory for the Quote of the Day protocol.

    @type quoter: L{IQuoter} provider
    @ivar quoter: An object which provides L{IQuoter} which will be used by
        the L{QOTD} protocol to get quotes to emit.
    """
    protocol = QOTD

    def __init__(self, quoter):
        self.quoter = quoter

This is a very straightforward Protocol implementation, and the pattern described above is repeated here. The Protocol contains essentially no logic of its own, just enough to tie together an object which can generate quotes (a Quoter ) and an object which can relay bytes to a TCP connection (a Transport ). When a client connects to this server, a QOTD instance is created, and its connectionMade method is called.

The QOTDFactory ‘s role is to specify to the Twisted framework how to create a Protocol instance that will handle the connection. Twisted will not instantiate a QOTDFactory ; you will do that yourself later, in a twistd plug-in.

Note: you can read more specifics of Protocol and Factory in the Writing Servers HOWTO.

Once we have an abstraction – a Quoter – and we have a mechanism to connect it to the network – the QOTD protocol – the next thing to do is to put the last link in the chain of functionality between abstraction and user. This last link will allow a user to choose a Quoter and configure the protocol. Writing this configuration is covered in the Application HOWTO .