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.
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.
See the description of setting up the TwistedQuotes example .
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)
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
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 .