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 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.
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 .