Flylib.com

Books Software

 
 
 

1.5. Finding Answers to Your Questions


1.5. Finding Answers to Your Questions

Even 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 excellent community resources you can look to for help. First, there are the mailing lists . The twisted-python list is for general discussion of Twisted. The twisted-web list is dedicated to discussion of web applications. It's good etiquette to use the proper list; if you ask web- related questions on the twisted-python list, you'll probably be asked to move the discussion to twisted-web. You can sign up for the twisted-python and twisted-web mailing lists at http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python and http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web.

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 funny , discussion, and give you the unique opportunity to ask questions directly to members of the Twisted development team. Keep in mind, though, that such real-time support is a privilege, not a right. Be polite, and understand that developers might not always have time to answer your questions right at that moment. If you don't get an immediate answer on IRC, try sending a message to the appropriate mailing list. This approach will give the question to a wider audience, and let people answer when they have more time.

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 Clients and Servers

To develop with Twisted, you'll need to learn how to use several new classes and objects. These classes are at the core of Twisted, and you'll use them over and over in your applications. They also represent the steepest part of the Twisted learning curve. Understand how to use them, and the rest of Twisted will be easy; otherwise , you'll struggle (or write lots of unnecessary code).

This chapter shows how to write simple clients and servers with Twisted. Along the way, it introduces Twisted's basic classes, explains how they work, and demonstrates how to use them.


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 writes a function to respond to this event whenever it does happen. Such a function is known as an event handler .

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 react when a given event occurs?

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 tasks : scheduling, threading, establishing network connections, and listening for connections from other machines. To allow the reactor to do all these things, you must start its event loop, handing off control of your program.

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.py
from 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( ) .

You can pass additional arguments and keyword arguments to reactor.callLater , and they will be used to invoke your function. For example, reactor.callLater(1, func, True, x="hello") would result in the function call func(True, x="hello") , after a one-second delay.


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.

You can still manually stop the reactor. Try pressing Ctrl-C halfway through this example and see what happens.


Notice the order in which things happened . First, the reactor was given instructions to run certain functions at specific times. Then the reactor was started, which handed control of the program to the reactor's event loop and allowed it to manage the scheduled function calls. The reactor continued to run until it was told to stop—this time through a call to reactor.stop( ) . Once the reactor had stopped, the program could proceed with the final line of code, which printed a "Reactor stopped" message.

In real applications, reactor.callLater is typically used for timeouts and scheduled events. You might want to set up a function that runs at set intervals to do something like shut down inactive connections or save snapshots of in-memory data to disk.


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 open a TCP connection.

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 manages the flow of data back and forth along that connection. Example 2-3 shows how to establish a connection between your computer and a server on the Internet (in this case, www.google.com).

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- related events, and to create a new Protocol object for each successful connection. Once the connection is established, the Protocol object takes over, and is put in charge of sending and receiving data across the connection and deciding when (if ever) to close the connection.

The name "Factory" in ClientFactory comes from the fact that it creates Protocol instances on demand, in response to each successful connection.


{% if main.adsdop %}{% include 'adsenceinline.tpl' %}{% endif %}

Example 2-3 defines a custom Protocol called QuickDisconnectProtocol , which inherits from protocol.Protocol . It overrides a single method, connectionMade . This method is run as soon as the connection is up and running, just after the reactor has successfully established the connection and the ClientFactory has created this instance of QuickDisconnectProtocol . TRue to its name, the QuickDisconnectProtocol object quickly closes its connection (after printing the name of the host it's connected to).

Protocol objects have an attribute called TRansport , which is used for interacting with the current connection.


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 methods of ClientFactory , clientConnectionLost and clientConnectionFailed . These methods are event handlers. clientConnectionFailed will be called if the reactor is unable to establish the connection. clientConnectionLost will be called when an established connection is closed or broken.

To tell the reactor to establish a TCP connection, Example 2-3 calls reactor.connectTCP :

reactor.connectTCP('www.google.com', 80,  BasicClientFactory( ))

This line tells the reactor to attempt a TCP connection to the server www.google.com, port 80, managed by an instance of BasicClientFactory .