Using Application

  1. Motivation
  2. Services, Persistence and Security, Oh My!
  3. Services
  4. String Ports
  5. Configuration

There are many ways in which your code will be called by various parts of the Twisted framework by the time you're done. The initial one we're going to focus on here is a plug-in for the mktap utility. mktap produces complete, runnable Application instances, so no additional work is necessary to make your code work with twistd. First we will go through the process of creating a plug-in that Twisted can find, then we make it adhere to the mktap interface. Finally we will load that plug-in with a server.

Motivation

Twisted can power anything connected to a network, from your corporate message-broadcasting network to your desktop IRC client. This is great for integrating lots of different tools, but can make it very difficult to understand how to use the Twisted platform most effectively.

You've probably come to Twisted thinking of it as a library of code to help you write an application. It is much more useful to think of your code as the library. Twisted is a framework. The difference between a framework and a library is that a developer's code will run a library's functions; whereas a framework runs the developer's functions.

The difference is subtle, but significant; there are a range of resources which have to be allocated and managed regarding start-up and shut-down of an process, such as spawning of threads and handling events. In the library model, you handle the management of these resources. If you use Twisted as a framework, though, it will manage these resources itself.

If you were using Twisted as a library, you would probably write a Python script that:

There are two problems with this methodology: it requires a lot of boiler-plate code; and it introduces inflexibility into the design. The usual way to solve this kind of problem is to write configuration files, and it is no different in Twisted.

At this point, the standard thing to do would be to write a long, tedious and subtly wrong manual describing the configuration language. Rest assured, like every other project, Twisted has those. But the language is secondary, and will be described later — more important are the configuration objects.

Services, Persistence and Security, Oh My!

It is possible to think of any configuration language as designed to build a configuration object, which is then queried and acted upon by the program runtime.

In Twisted, this is literally true internally — the master configuration object is twisted.application.service.Application. However, there is virtually nothing you can do with this object directly. This object is Componentized— it has different, orthogonal, aspects. You access these aspects by using interfaces. Interfaces, for our purposes, are just callables which return different aspects of the Application.

There are four interfaces supported, three of which are defined in twisted.application.service:

Constructing an application is done by calling it with a single argument — its name. The name influences some things, among them the default file it will persist to (which is why it is mandatory).

from twisted.application import service
from twisted.persisted import sob
application = service.Application("myapplication")
s = service.IService(application)
sc = service.IServiceCollection(application)
proc = service.IProcess(application)
per = sob.IPersistable(application)

Services

The central framework classes that you will deal with, both as a Twisted developer and administrator, are services, which implement twisted.application.service.IService. twisted.application.service.Application creates the root of a tree of services that form a twisted application. There is one Application instance per Twisted process, and it is the top-level manager of resources and handler of events in the Twisted framework. (Unlike other frameworks, like Qt or wxWindows, in Twisted you do not derive from Application; rather you register event handlers for it to call by adding Services to it.) To store configuration data, as well as other information, Twisted serializes Application instances, storing all services that have been registered with them. Since the whole Application instance is serialized, Twisted configuration files are significantly more comprehensive than those for other systems. These files store everything related to a running Application instance; in essence the full state of a running process.

There are two interfaces relevant to services —IService and IServiceCollection. IService represents a state-aware container. That means the service is ready to be notified of application start-ups and shutdowns. Services can be named or unnamed. IServiceCollection holds other services. It is possible to get named services from it by name. All services can be gotten from it via either indexing or iteration.

Services can have a parent. Parents are set using setServiceParent. Services are detached from their parent with disownServiceParent. The parent must always be something that complies with the IServiceCollection interface.

Most services will inherit from Service. This class will set an attribute running to a true value in startService and to a false value in stopService. This attribute will always be false in just-unpersisted Services, without regards to its value at the time the Service was persisted.

MultiService implements both IService and IServiceCollection. It is used to keep the services in a hierarchy.

It is, of course, possible to write one's own services, but Twisted comes out of the box with several services which are useful in writing network applications. These are found in twisted.application.internet, including TCPServer, TCPClient, and TimerService.

To each reactor.listenFoo method corresponds a service named FooServer. The arguments to its constructor are the same as the arguments to the method. It calls the method on application start-up, and stops listening on application shut-down.

To each reactor.connectFoo methods corresponds a service named FooClient. The arguments to its constructor are the same as the arguments to the method. It calls the method on application start-up. It might, or might not, stop the connection on application shut-down. (This limitation will be removed at some point, and guaranteed disconnection will be implemented.)

The last service in twisted.application.internet is TimerService. The constructor takes a period, a callable and optionally arguments and keyword arguments. The service, when it is running, will make sure the callable will be called every time the period elapses.

String Ports

In Twisted, a ServerFactory does not care what kind of virtual reliable circuit it listens too — SSL, UNIX domain sockets, TCP sockets or something else. However, the APIs for constructing the various *Server classes are different. When it is necessary for a less sophisticated user to direct construction of such a class, the twisted.application.strports module comes in handy. It contains a function service which accepts a description and a factory, and returns a service. The description is a string in a mini-language designed to specify ports. Full specifications are in the module docstrings.

Configuration

At some point, the objects for the configuration actually have to be constructed. The easiest and simplest way to do it is to use Python as the configuration mini-language. This format is called TAC and traditionally files with this format have the extension .tac.

TAC files need to be valid Python files, which construct a variable named application. This variable will be the configuration object. The full power of Python is available, of course.

Here's an example:

# Import modules
from twisted.application import service, internet
from twisted.protocols import wire
from twisted.internet import protocol

# Construct the application
application = service.Application("echo")

# Get the IServiceCollection interface
myService = service.IServiceCollection(application)

# Create the protocol factory
myFactory = protocol.ServerFactory()
myFactory.protocol = wire.Echo

# Create the (sole) server
# Normally, the echo protocol lives on port 7, but since that
# is a privileged port, for this example we'll use port 7001
myServer = internet.TCPServer(7001, myFactory)

# Tie the service to the application
myServer.setServiceParent(myService)

Note that setServiceParent will, in fact, automatically cast its argument to IServiceCollection. So, more succinctly, the above code can be written:

from twisted.application import service, internet
from twisted.protocols import wire
from twisted.internet import protocol
application = service.Application("echo")
myFactory = protocol.ServerFactory()
myFactory.protocol = wire.Echo
internet.TCPServer(7001, myFactory).setServiceParent(application)

TAC files are run with twistd -y or twistd --python. The twistd manpage has more information, but a common way to run is twistd -noy echo.tac. This tells twistd to not daemonize and not to try and save application on shutdown.

Index

Version: 2.0.1