Cover | Table of Contents | Colophon
http://twistedmatrix.com/projects/core/. To enable additional functionality in Twisted, you'll have to install a couple of optional packages as well.http://www.twistedmatrix.com. Then install PyOpenSSL
(a Python wrapper of the popular OpenSSL library), which Twisted uses to make encrypted Secure Socket Layer (SSL) connections. Finally, install PyCrypto
, a package containing Python implementations of encryption algorithms used by the Secure SHell (SSH). Locations for these downloads are provided in each of the following platform-specific sections.http://twistedmatrix.com/projects/core/. Download the Twisted Windows "Sumo" installer for Python 2.4 (or your Python version). The Sumo binary includes the Twisted core, as well as a number of extra modules to support specific groups of protocols like mail and web. You'll need the full Sumo version of Twisted installed to run most of the examples in this book. Then go to http://twistedmatrix.com/products/download and find the section labeled "Twisted Dependencies for Windows." There you'll find links to installers for the latest versions of PyOpenSSL and PyCrypto.http://twistedmatrix.com/projects/core/. To enable additional functionality in Twisted, you'll have to install a couple of optional packages as well.http://www.twistedmatrix.com. Then install PyOpenSSL
(a Python wrapper of the popular OpenSSL library), which Twisted uses to make encrypted Secure Socket Layer (SSL) connections. Finally, install PyCrypto
, a package containing Python implementations of encryption algorithms used by the Secure SHell (SSH). Locations for these downloads are provided in each of the following platform-specific sections.http://twistedmatrix.com/projects/core/. Download the Twisted Windows "Sumo" installer for Python 2.4 (or your Python version). The Sumo binary includes the Twisted core, as well as a number of extra modules to support specific groups of protocols like mail and web. You'll need the full Sumo version of Twisted installed to run most of the examples in this book. Then go to http://twistedmatrix.com/products/download and find the section labeled "Twisted Dependencies for Windows." There you'll find links to installers for the latest versions of PyOpenSSL and PyCrypto.http://twistedmatrix.com.http://twistedmatrix.com/projects/core/. The Sumo package is the core of Twisted, plus a number of bundled modules from other projects developed under the Twisted umbrella; you'll need the modules in the Sumo package to run most of the examples in this book. Once you've downloaded the package, extract it to a working directory:
$ tar -xjvf ~/downloads/TwistedSumo-2005-03-22.tar.bz2
TwistedSumo-2005-03-22/
TwistedSumo-2005-03-22/bin/
...
TwistedSumo-2005-03-22/README
TwistedSumo-2005-03-22/LICENSE
TwistedSumo-2005-03-22/setup.py
TwistedSumo-2005-03-22/ZopeInterface-3.0.1.tgz
zope.interface package, which is bundled in the Twisted Sumo distribution. Unzip the ZopeInterface tarball:
$ tar -xzvf ZopeInterface-3.0.1.tgz
ZopeInterface-3.0.1/
ZopeInterface-3.0.1/Support/
ZopeInterface-3.0.1/Support/zpkgsetup/
ZopeInterface-3.0.1/Support/zpkgsetup/publication.py
...
ZopeInterface-3.0.1/setup.py
ZopeInterface-3.0.1/setup.cfg
ZopeInterface-3.0.1/MANIFEST
zope.interface package in your python installation's lib/site-packages/twisted directory. You'll need to have administrative/root permissions to do this, so use su or sudo to increase your permission level if necessary:
$ cd ZopeInterface-3.0.1
$ python setup.py install
running install
running build
running build_py
running build_ext
building 'zope.interface._zope_interface_coptimizations' extension
...
running install_lib
copying build/lib.linux-i686-2.4/zope/interface/_zope_interface_coptimizations.so ->
/usr/lib/python2.4/site-packages/zope/interface
writing byte-compilation script '/tmp/tmpdY9dA9.py'
/usr/bin/python -O /tmp/tmpdY9dA9.py
removing /tmp/tmpdY9dA9.py:
$ set PATH=$PATH:/System/Library/Frameworks/Python.framework/Versions/Current/bin
http://twistedmatrix.com/documents/current/api. You'll probably refer to this documentation many times to see which classes a module contains or to see the list of arguments for a specific function. The pages in the API documentation are automatically generated from the source code using lore, a custom documentation tool developed as part of Twisted.http://twistedmatrix.com/projects/core/documentation, and documentation on web modules is at http://twistedmatrix.com/projects/web/documentation. There are links to the full list of projects and documentation on the home page.
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.#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.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 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.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.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.reactor, Deferreds are probably the most important objects used in Twisted. You'll work with Deferreds frequently in Twisted applications, so it's essential to understand how they work. Deferreds can be a little confusing at first, but their purpose is simple: to keep track of an asynchronous action, and to get the result when the action is completed.Deferreds can be illustrated this way: perhaps you've had the experience of going to one of those restaurants where, if there's going to be a wait for a table, you're given a little pager that will buzz when your table is ready. Having the pager is nice, because it gives you freedom to do other things while you're waiting for your table, instead of standing around the front of the restaurant feeling bored. You can take a walk outside, or even go next door and do some shopping. When a table (finally!) becomes available, the pager buzzes, and you head back inside the restaurant to be seated.Deferred is like that pager. It gives your program a way of finding out when some asynchronous task is finished, which frees it up to do other things in the meantime. When a function returns a Deferred object, it's saying that there's going to be some delay before the final result of the function is available. To control what happens when the result does become available, you can assign event handlers to the Deferred.Deferred object. When the action is competed, call the Deferred's callback method with the return value. If the action fails, call Deferred.errback with an exception. Example 2-4 shows a program that uses an asynchronous action to test connectivity to a given server and port.Deferred object, use the Deferred.addCallback method to assign a function to handle the results of the deferred action if it completes successfully. Use the Protocol to send and receive data. Override the dataReceived method to control what happens when data is received from the connection. Use self.transport.write to send data.DataForwardingProtocol, which takes any data received and writes it to self.output. This usage makes it possible to create a simple application, similar to the classic utility netcat, that passes any data received on standard input to a server, while printing any data received from the server to standard output.
from twisted.internet import stdio, reactor, protocol
from twisted.protocols import basic
import re
class DataForwardingProtocol(protocol.Protocol):
def _ _init_ _(self):
self.output = None
self.normalizeNewlines = False
def dataReceived(self, data):
if self.normalizeNewlines:
data = re.sub(r"(\r\n|\n)", "\r\n", data)
if self.output:
self.output.write(data)
class StdioProxyProtocol(DataForwardingProtocol):
def connectionMade(self):
inputForwarder = DataForwardingProtocol()
inputForwarder.output = self.transport
inputForwarder.normalizeNewlines = True
stdioWrapper = stdio.StandardIO(inputForwarder)
self.output = stdioWrapper
print "Connected to server. Press ctrl-C to close connection."
class StdioProxyFactory(protocol.ClientFactory):
protocol = StdioProxyProtocol
def clientConnectionLost(self, transport, reason):
reactor.stop()
def clientConnectionFailed(self, transport, reason):
print reason.getErrorMessage()
reactor.stop()
if __name__ == '_ _main_ _':
import sys
if not len(sys.argv) == 3:
print "Usage: %s host port" % _ _file_ _
sys.exit(1)
reactor.connectTCP(sys.argv[1], int(sys.argv[2]), StdioProxyFactory())
reactor.run()
Protocol object defining your server's behavior. Create a ServerFactory object using the Protocol, and pass it to reactor.listenTCP. Example 2-7 shows a simple echo server that accepts a client connection and then repeats back all client messages.
from twisted.internet import reactor, protocol
from twisted.protocols import basic
class EchoProtocol(basic.LineReceiver):
def lineReceived(self, line):
if line == 'quit':
self.sendLine("Goodbye.")
self.transport.loseConnection()
else:
self.sendLine("You said: " + line)
class EchoServerFactory(protocol.ServerFactory):
protocol = EchoProtocol
if __name__ == "_ _main_ _":
port = 5001
reactor.listenTCP(port, EchoServerFactory())
reactor.run()
$ python echoserver.py
Server running, press ctrl-C to stop.
Connection from 127.0.0.1
Connection from 127.0.0.1
$ python dataforward.py localhost 5001
Connected to server. Press ctrl-C to close connection.
hello
You said: hello
twisted is fun
You said: twisted is fun
quit
Goodbye.
$ How does that work?
twisted.web.client module to interact with web resources, including downloading
pages, using HTTP authentication, uploading files, and working with HTTP headers.GET request, and receives an HTTP response containing the requested page.twisted.web package includes a complete HTTP implementation, saving you the work of developing the necessary Protocol and ClientFactory classes. Furthermore, it includes utility functions that allow you to make an HTTP request with a single function call. To fetch the contents of a web page, use the function twisted.web.client.getPage. Example 3-1 is a Python script called webcat.py, which fetches a URL that you specify.
from twisted.web import client
from twisted.internet import reactor
import sys
def printPage(data):
print data
reactor.stop()
def printError(failure):
print >> sys.stderr, "Error:", failure.getErrorMessage()
reactor.stop()
if len(sys.argv) == 2:
url = sys.argv[1]
client.getPage(url).addCallback(
printPage).addErrback(
printError)
reactor.run()
else:
print "Usage: webcat.py <URL>"
$ python webcat.py http://www.oreilly.com/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>oreilly.com -- Welcome to O'Reilly Media, Inc. -- computer books, software
conferences, online publishing</title>
...GET request, and receives an HTTP response containing the requested page.twisted.web package includes a complete HTTP implementation, saving you the work of developing the necessary Protocol and ClientFactory classes. Furthermore, it includes utility functions that allow you to make an HTTP request with a single function call. To fetch the contents of a web page, use the function twisted.web.client.getPage. Example 3-1 is a Python script called webcat.py, which fetches a URL that you specify.
from twisted.web import client
from twisted.internet import reactor
import sys
def printPage(data):
print data
reactor.stop()
def printError(failure):
print >> sys.stderr, "Error:", failure.getErrorMessage()
reactor.stop()
if len(sys.argv) == 2:
url = sys.argv[1]
client.getPage(url).addCallback(
printPage).addErrback(
printError)
reactor.run()
else:
print "Usage: webcat.py <URL>"
$ python webcat.py http://www.oreilly.com/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>oreilly.com -- Welcome to O'Reilly Media, Inc. -- computer books, software
conferences, online publishing</title>
...
printPage and printError functions are simple event handlers that print the downloaded page contents or an error message, respectively. The most important line in Example 3-1 is the call to Authorization header, as shown in the script webcat3.py in Example 3-3.
from twisted.web import client, error as weberror
from twisted.internet import reactor
import sys, getpass, base64
def printPage(data):
print data
reactor.stop()
def checkHTTPError(failure, url):
failure.trap(weberror.Error)
if failure.value.status == '401':
print >> sys.stderr, failure.getErrorMessage()
# prompt for user name and password
username = raw_input("User name: ")
password = getpass.getpass("Password: ")
basicAuth = base64.encodestring("%s:%s" % (username, password))
authHeader = "Basic " + basicAuth.strip()
# try to fetch the page again with authentication
return client.getPage(
url, headers={"Authorization": authHeader})
else:
return failure
def printError(failure):
print >> sys.stderr, "Error:", failure.getErrorMessage()
reactor.stop()
if len(sys.argv) == 2:
url = sys.argv[1]
client.getPage(url).addErrback(
checkHTTPError, url).addCallback(
printPage).addErrback(
printError)
reactor.run()
else:
print "Usage: %s <URL>" % sys.argv[0]
401 error, it will ask for a username and password and try the request again:
$ python webcat3.py http://example.com/protected/page
401 Authorization Required
User name: multipart/form-data MIME document
. Neither the Python standard library nor Twisted provides an easy way to do this, but you can do it yourself without too much effort. Then pass the encoded form data as the formdata keyword argument to client.getPage or client.downloadPage, along with POST as the HTTP method. You can then work with the results of getPage or downloadPage as you would any other HTTP response. Example 3-4 shows a script named validate.py that uploads a file to the W3C validation service, saves the response to a local file, and then displays it in the user's browser.
from twisted.web import client
import os, tempfile, webbrowser, random
def encodeForm(inputs):
"""
Takes a dict of inputs and returns a multipart/form-data string
containing the utf-8 encoded data. Keys must be strings, values
can be either strings or file-like objects.
"""
getRandomChar = lambda: chr(random.choice(range(97, 123)))
randomChars = [getRandomChar() for x in range(20)]
boundary = "---%s---" % ''.join(randomChars)
lines = [boundary]
for key, val in inputs.items():
header = 'Content-Disposition: form-data; name="%s"' % key
if hasattr(val, 'name'):
header += '; filename="%s"' % os.path.split(val.name)[1]
lines.append(header)
if hasattr(val, 'read'):
lines.append(val.read())
else:
lines.append(val.encode('utf-8'))
lines.append('')
lines.append(boundary)
return "\r\n".join(lines)
def showPage(pageData):
# write data to temp .html file, show file in browser
tmpfd, tmp = tempfile.mkstemp('.html')
os.close(tmpfd)
file(tmp, 'w+b').write(pageData)
webbrowser.open('file://' + tmp)
reactor.stop()
def handleError(failure):
print "Error:", failure.getErrorMessage()
reactor.stop()
if __name__ == "_ _main_ _":
import sys
from twisted.internet import reactor
filename = sys.argv[1]
fileToCheck = file(filename)
form = encodeForm({'uploaded_file': fileToCheck})
postRequest = client.getPage(
'http://validator.w3.org/check',
method='POST',
headers={'Content-Type': 'multipart/form-data; charset=utf-8',
'Content-Length': str(len(form))},
postdata=form)
postRequest.addCallback(showPage).addErrback(handleError)
reactor.run()
ETag header, which identifies the unique revision of the page, or the Last-Modified header, which gives the page's modification time. The next time you request the page, send the headers If-None-Match, with the ETag value, and If-Modified-Since, with the Last-Modified value. If the server supports conditional GET requests, it will return a 304 Unchanged response if the page has not been modified since the last request.getPage and downloadPage functions provided by twisted.web.client are handy, but they don't allow for the level of control necessary to use conditional requests. Therefore, you'll need to use the slightly lower-level HTTPClientFactory interface. Example 3-5 demonstrates using HTTPClientFactory to test whether a page has been updated.
from twisted.web import client
class HTTPStatusChecker(client.HTTPClientFactory):
def _ _init_ _(self, url, headers=None):
client.HTTPClientFactory._ _init_ _(self, url, headers=headers)
self.status = None
self.deferred.addCallback(
lambda data: (data, self.status, self.response_headers))
def noPage(self, reason): # called for non-200 responses
if self.status == '304': # Page hadn't changed
client.HTTPClientFactory.page(self, '')
else:
client.HTTPClientFactory.noPage(self, reason)
def checkStatus(url, contextFactory=None, *args, **kwargs):
scheme, host, port, path = client._parse(url)
factory = HTTPStatusChecker(url, *args, **kwargs)
if scheme == 'https':
from twisted.internet import ssl
if contextFactory is None:
contextFactory = ssl.ClientContextFactory()
reactor.connectSSL(host, port, factory, contextFactory)
else:
reactor.connectTCP(host, port, factory)
return factory.deferred
def handleFirstResult(result, url):
data, status, headers = result
nextRequestHeaders = {}
eTag = headers.get('etag')
if eTag:
nextRequestHeaders['If-None-Match'] = eTag[0]
modified = headers.get('last-modified')
if modified:
nextRequestHeaders['If-Modified-Since'] = modified[0]
return checkStatus(url, headers=nextRequestHeaders).addCallback(
handleSecondResult)
def handleSecondResult(result):
data, status, headers = result
print 'Second request returned status %s:' % status,
if status == '200':
print 'Page changed (or server does not support conditional requests).'
elif status == '304':
print 'Page is unchanged.'
else:
print 'Unexpected Response.'
reactor.stop()
def handleError(failure):
print "Error", failure.getErrorMessage()
reactor.stop()
if __name__ == "_ _main_ _":
import sys
from twisted.internet import reactor
url = sys.argv[1]
checkStatus(url).addCallback(
handleFirstResult, url).addErrback(
handleError)
reactor.run()
Deferred will pass you the results of a page once it's completely downloaded, but sometimes what you really need is to keep an eye on the download as it's happening.twisted.web.client don't give you quite enough control. Define a subclass of client.HTTPDownloader, the factory class used for downloading a web page to a file. By overriding a couple of methods, you can keep track of a download in progress. The webdownload.py script in Example 3-6 shows how.
from twisted.web import client
class HTTPProgressDownloader(client.HTTPDownloader):
def gotHeaders(self, headers):
if self.status == '200': # page data is on the way
if headers.has_key('content-length'):
self.totalLength = int(headers['content-length'][0])
else:
self.totalLength = 0
self.currentLength = 0.0
print ''
return client.HTTPDownloader.gotHeaders(self, headers)
def pagePart(self, data):
if self.status == '200':
self.currentLength += len(data)
if self.totalLength:
percent = "%i%%" % (
(self.currentLength/self.totalLength)*100)
else:
percent = '%dK' % (self.currentLength/1000)
print "\033[1FProgress: " + percent
return client.HTTPDownloader.pagePart(self, data)
def downloadWithProgress(url, file, contextFactory=None, *args, **kwargs):
scheme, host, port, path = client._parse(url)
factory = HTTPProgressDownloader(url, file, *args, **kwargs)
if scheme == 'https':
from twisted.internet import ssl
if contextFactory is None:
contextFactory = ssl.ClientContextFactory()
reactor.connectSSL(host, port, factory, contextFactory)
else:
reactor.connectTCP(host, port, factory)
return factory.deferred
if __name__ == "_ _main_ _":
import sys
from twisted.internet import reactor
def downloadComplete(result):
print "Download Complete."
reactor.stop()
def downloadError(failure):
print "Error:", failure.getErrorMessage()
reactor.stop()
url, outputFile = sys.argv[1:]
downloadWithProgress(url, outputFile).addCallback(
downloadComplete).addErrback(
downloadError)
reactor.run()
http://www.faqs.org/rfcs/rfc2616.html).Protocol that accepts a connection, reads the request, and sends back an HTTP-formatted response.Protocol that accepts a connection, reads the request, and sends back an HTTP-formatted response.GET on the resource www.example.com/index.html, preferably using HTTP version 1.1:
GET /index.html HTTP/1.1
Host: www.example.com
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 17
Connection: Close
Hello HTTP world!
Protocol that accepts input from the client. Look for the blank line that identifies the end of the headers. Then send an HTTP response. Example 4-1 shows a simple HTTP implementation that echoes each request back to the client.
from twisted.protocols import basic
from twisted.internet import protocol, reactor
class HttpEchoProtocol(basic.LineReceiver):
def _ _init_ _(self):
self.lines = []
self.gotRequest = False
def lineReceived(self, line):
self.lines.append(line)
if not line and not self.gotRequest:
self.sendResponse()
self.gotRequest = True
def sendResponse(self):
responseBody = "You said:\r\n\r\n" + "\r\n".join(self.lines)
self.sendLine("HTTP
/1.0 200 OK")
self.sendLine("Content-Type: text/plain")
self.sendLine("Content-Length: %i" % len(responseBody))
self.sendLine("")
self.transport.write(responseBody)
self.transport.loseConnection()
f = protocol.ServerFactory()
f.protocol = HttpEchoProtocol
reactor.listenTCP(8000, f)
reactor.run()HTTPEchoProtocol class in Example 4-1 provides an interesting glimpse into HTTP in action, but it's a long way from being ready for use in a real web server. It doesn't even parse the request to figure out what resource the client is trying to access, or what HTTP method she's using. Before you try to build a real web application, you need a better way to parse and respond to requests. This lab shows you how.twisted.web.http.Request with a process method that processes the current request. The Request object will already contain all the important information about an HTTP request when process is called, so all you have to do is decide how to respond. Example 4-2 demonstrates how to run an HTTP server based on a subclass of http.Request.
from twisted.web import http
class MyRequestHandler(http.Request):
pages = {
'/': '<h1>Home</h1>Home page',
'/test': '<h1>Test</h1>Test page',
}
def process(self):
if self.pages.has_key(self.path):
self.write(self.pages[self.path])
else:
self.setResponseCode(http.NOT_FOUND)
self.write("<h1>Not Found</h1>Sorry, no such page.")
self.finish()
class MyHttp(http.HTTPChannel):
requestFactory = MyRequestHandler
class MyHttpFactory(http.HTTPFactory):
protocol = MyHttp
if __name__ == "_ _main_ _":
from twisted.internet import reactor
reactor.listenTCP(8000, MyHttpFactory())
reactor.run()
http://localhost:8000/) and the page /test (http://localhost:8000/test) in your browser. Figure 4-2 shows you how the page /test will look in your browser.
Request object and work with it to generate a response. Set up a dictionary to map each available path in your web site to a function that will handle requests for that path. Use the Request.args dictionary to access data submitted from an HTML form. Example 4-3 shows a web server that generates one page containing an HTML form, and another page that processes the form and displays the results.
from twisted.web import http
def renderHomePage(request):
colors = 'red', 'blue', 'green'
flavors = 'vanilla', 'chocolate', 'strawberry', 'coffee'
request.write("""
<html>
<head>
<title>Form Test</html>
</head>
<body>
<form action='posthandler' method='post'>
Your name:
<p>
<input type='text' name='name'>
</p>
What's your favorite color?
<p>
""")
for color in colors:
request.write(
"<input type='radio' name='color' value='%s'>%s<br />" % (
color, color.capitalize()))
request.write("""
</p>
What kinds of ice cream do you like?
<p>
""")
for flavor in flavors:
request.write(
"<input type='checkbox' name='flavor' value='%s'>%s<br />" % (
flavor, flavor.capitalize()))
request.write("""
</p>
<input type='submit' />
</form>
</body>
</html>
""")
request.finish()
def handlePost(request):
request.write("""
<html>
<head>
<title>Posted Form Datagg</title>
</head>
<body>
<h1>Form Data</h1>
""")
for key, values in request.args.items():
request.write("<h2>%s</h2>" % key)
request.write("<ul>")
for value in values:
request.write("<li>%s</li>" % value)
request.write("</ul>")
request.write("""
</body>
</html>
""")
request.finish()
class FunctionHandledRequest(http.Request):
pageHandlers = {
'/': renderHomePage,
'/posthandler': handlePost,
}
def process(self):
self.setHeader('Content-Type', 'text/html')
if self.pageHandlers.has_key(self.path):
handler = self.pageHandlers[self.path]
handler(self)
else:
self.setResponseCode(http.NOT_FOUND)
self.write("<h1>Not Found</h1>Sorry, no such page.")
self.finish()
class MyHttp(http.HTTPChannel):
requestFactory = FunctionHandledRequest
class MyHttpFactory(http.HTTPFactory):
protocol = MyHttp
if __name__ == "_ _main_ _":
from twisted.internet import reactor
reactor.listenTCP(8000, MyHttpFactory())
reactor.run()
http://example.com/people
http://example.com/people/charles
http://example.com/people/charles/contact
twisted.web.resource, twisted.web.static, and twisted.web.server modules provide classes for working with requests at a higher level than twisted.web.http.Resource, which you can use to set up a web server that combines several different kinds of resources into a logical hierarchy. Example 4-4 uses these classes to build an application for testing hexadecimal color codes. Request the resource Deferreds, allowing your app to continue doing other things while it's waiting for the results.twisted.enterprise package. twisted.enterprise doesn't actually include SQL drivers; it would be far too much work to support every database you might potentially want to use. Instead, twisted.enterprise provides an asynchronous API on top of the standard DB-API interface used by many Python database modules. When necessary, it uses threads t