# 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 asynchonous programming using Deferreds and 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

    from random import choice

    from zope.interface import implements

    from TwistedQuotes import quoteproto

    class StaticQuoter:
        """
        Return a static quote.
        """

        implements(quoteproto.IQuoter)

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

        def getQuote(self):
            return self.quote

    class FortuneQuoter:
        """
        Load quotes from a fortune-format file.
        """
        implements(quoteproto.IQuoter)

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

        def getQuote(self):
            quoteFile = file(choice(self.filenames))
            quotes = quoteFile.read().split('\n%\n')
            quoteFile.close()
            return choice(quotes)

Twisted Quotes Central Abstraction - listings/TwistedQuotes/quoters.py

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.

    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

Twisted Quotes Protocol Implementation - listings/TwistedQuotes/quoteproto.py

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.

Index

Version: 8.2.0