1.5. Finding Answers to Your QuestionsEven with this book, and the Twisted documentation, you'll eventually run into a question whose answer you can't figure out on your own. When that happens, it's time to get help from the Twisted community. Figure 1-1. Using pydoc to view API documentation
1.5.1. How Do I Do That?
There are a few
Second, you can talk with Twisted users and developers in the
#twisted
and
#twisted.web
IRC channels on the freenode network (see http://freenode.net for a list of servers). These channels feature lively, and often
A final resource available to the Twisted community is Planet Twisted . Located at http://planet.twistedmatrix.com, this web site aggregates weblog posts made by members of the Twisted development team. It's an excellent way to keep track of what's going on with Twisted development, as well as to get to know the personalities of the Twisted team. Planet Twisted also provides an RSS feed at http://planet.twistedmatrix.com/rss.xml. |
Chapter 2. Building Simple
|
2.1. Starting the Twisted Event Loop
Twisted is an
event-driven
framework. This means that instead of having the program's functions called in a sequence specified by the program's logic, they are called in response to external actions, or
events
. For example, a GUI program might have code for responding to the "button pressed" event. The designer of the program can't be sure exactly when such an event will occur; but she
Every event-driven framework includes a special function called an event loop . Once started, an event loop runs indefinitely. While it's running, it waits for events. When an event occurs, the event loop triggers the appropriate event handler function.
Using an event loop requires a different mindset on the part of the programmer than traditional sequential programming. Once you start the event loop, you no longer have the ability to directly instruct your program what to do; it can perform actions only in response to events. Therefore, you need to think in terms of events and event handlers when you design your program. What are the events you want your program to respond to? How do you want it to
In Twisted, there's a special object that implements the event loop. This object is called the
reactor
. You can think of the reactor as the central nervous system of a Twisted application. In addition to being responsible for the event loop, the reactor handles many other important
2.1.1. How Do I Do That?Starting the reactor is easy. Import the reactor object from the twisted.internet module. Then call reactor.run( ) to start the reactor's event loop. Example 2-1 shows all the code you need. Example 2-1. runreactor.pyfrom twisted.internet import reactor print "Running the reactor..." reactor.run( ) print "Reactor stopped." The reactor will continue to run until it's told to stop. Press Ctrl-C to exit the event loop and end the application: $ python runreactor.py Running the reactor... <ctrl-c> ^CReactor stopped. That's a pretty boring example. Although the reactor was running, it hadn't been given anything to do. Example 2-2 provides a more interesting bit of code, which introduces the reactor's callLater method. reactor.callLater is used to set up scheduled events. This method is useful when you want to schedule a function to run in the future. (Such a function is still an event handler, with the event in this case being the passage of time.) In Example 2-2, functions are called after a predetermined number of seconds have passed. Example 2-2. calllater.py
from twisted.internet import reactor
import time
def printTime( ):
print "Current time is", time.strftime("%H:%M:%S")
def stopReactor( ):
print "Stopping reactor"
reactor.stop( )
reactor.callLater(1, printTime)
reactor.callLater(2, printTime)
reactor.callLater(3, printTime)
reactor.callLater(4, printTime)
reactor.callLater(5, stopReactor)
print "Running the reactor..."
reactor.run( )
print "Reactor stopped."
Run this program, and you'll see output like the following:
$ python calllater.py
Running the reactor...
Current time is 10:33:44
Current time is 10:33:45
Current time is 10:33:46
Current time is 10:33:47
Stopping reactor
Reactor stopped.
2.1.2. How Does That Work?Example 2-1 simply imports the reactor and calls reactor.run( ) to start it. The reactor will keep running (although it hadn't been giving anything to do) until you press Ctrl-C. At that point, the reactor stops, and the program proceeds to the final line of code, which prints out a message informing you that the reactor has been stopped. The second example uses the reactor.callLater function to schedule some function calls. reactor.callLater has two required arguments: the number of seconds to wait, and the function to run. After setting up the scheduled function calls, control is handed off to the reactor's event loop using reactor.run( ) .
The first four scheduled function calls were to printTime( ) , which simply printed out the current time. However, the fifth scheduled function, stopReactor( ) , did something interesting. It called reactor.stop( ) , which caused the reactor to exit the event loop. That's why you didn't have to press Ctrl-C to stop the reactor this time. It stopped itself.
Notice the order in which things
2.1.3. Establishing a TCP Connection
All networked programs have to start with one basic step: making a connection. Eventually, of course, you'll want to send email, transfer files, and stream live video, but before any of that can happen, a connection between two computers must be established. This section shows how to use the Twisted
reactor
to
2.1.4. How Do I Do That?
Call the
reactor.connectTCP( )
method to start a TCP connection, passing a
ClientFactory
object as the third parameter. The
ClientFactory
object waits for the connection to be established, and then creates a
Protocol
object that
Example 2-3. tcpconnection.py
from twisted.internet import reactor, protocol
class QuickDisconnectProtocol(protocol.Protocol):
def connectionMade(self):
print "Connected to %s." % self.transport.getPeer( ).host
self.transport.loseConnection( )
class BasicClientFactory(protocol.ClientFactory):
protocol = QuickDisconnectProtocol
def clientConnectionLost(self, connector, reason):
print "Lost connection: %s" % reason.getErrorMessage( )
reactor.stop( )
def clientConnectionFailed(self, connector, reason):
print "Connection failed: %s" % reason.getErrorMessage( )
reactor.stop( )
reactor.connectTCP('www.google.com', 80, BasicClientFactory( ))
reactor.run( )
When you run this example, you should get the following output:
$ python connection.py
Connected to www.google.com.
Lost connection: Connection was closed cleanly.
unless, of course, your computer is offline. In that case, you'll see an error message, such as this:
$ python connection.py
Connection failed: DNS lookup failed: address 'www.google.com' not found.
2.1.5. How Does That Work?There are two main classes used for working with client connections, ClientFactory and Protocol . These classes are designed to handle all the potential events that arise when you start working with connections: when a connection is established, when a connection attempt fails, when an existing connection is broken, or when data is received from the other side.
ClientFactory
and
Protocol
have specific and distinct roles. The
ClientFactory
's job is to manage connection-
{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}
Example 2-3 defines a custom
Protocol
called
QuickDisconnectProtocol
, which inherits from
protocol.Protocol
. It
BasicClientFactory
is a class inheriting from
protocol.ClientFactory
. It first sets the class-level attribute
protocol
to
QuickDisconnectProtocol
. An instance of this class will be created to manage each successful connection.
BasicClientFactory
overrides two
To tell the reactor to establish a TCP connection, Example 2-3 calls reactor.connectTCP :
reactor.connectTCP('www.google.com', 80, BasicClientFactory( ))
This line
|