Errata

Twisted Network Programming Essentials

Errata for Twisted Network Programming Essentials

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Printed
Page 178
second paragraph

"With key-based authentication, the server is given a copy of a user's private key."

Hmmm, that would make the private key "un-private" wouldn't it? This is the exact opposite of what the server gets. In reality the server needs a copy of the user's public key.

Note from the Author or Editor:
This is correct - it should read "public key" instead of "private key".

Anonymous  Oct 25, 2009  Jul 22, 2011
Printed
Page 174
class SSHDemoAvatar

When trying to connect to this example, I got an exception:

SSHDemoAvatar instance has no attribute 'eofReceived'

According to the source at http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.2.0/twisted/conch/interfaces.py the ISession interface also needs eofReceived and windowChanged, in addition to the ones already in the example.

Note from the Author or Editor:
This interface has probably changed since the book was published. The code should be updated, but I don't have the resources to work on it currently.

Anonymous  Oct 20, 2009 
Printed
Page 56
class SavePage; def render

Two issues:

util.quote is deprecated, so it will not work. Plus, it's not a great way to insert the data.

More seriously, runQuery will return an error saying that there are no results to fetch. runOperation is the correct function.

Corrected code:
def render(self, request):
title = request.args['title'][0]
body = request.args['body'][0]
query = "Insert into posts (title, body) values (%s, %s)"
self.db.runOperation(query, (title, body)).addCallback(self._saved, request).addErrback(self._saveFailed, request)
return server.NOT_DONE_YET

Note from the Author or Editor:
This is correct, changes should be made as noted. Thanks.

Xitij Patel  Aug 19, 2009  Jul 22, 2011
Printed
Page 62
3rd line from bottom

The sentence "The third class, WordCountProxy client, contains..."
should read "The third class, WordCountProxyClient, contains..."

Note from the Author or Editor:
Correct, this should be changed as noted above.

Anonymous  Nov 21, 2008  Jul 22, 2011
Printed
Page 56
in SavePage.render method

Hi,

I am not sure whether this is really an error but I find that Example 4-6 modified for PostgreSQL and the psycopg2 database adapter crashes the python 2.5 interpreter unless I replace runQuery with runOperation.
Using the later, I get a working example with the following diff output from the original:

$diff databaseblog.py databaseblog.py.orig
2c2
< from twisted.enterprise import adbapi
---
> from twisted.enterprise import adbapi, util as dbutil
4,18c4,9
< #import psycopg2
<
< # This code depends on a pre-existing PostgreSQL table
< # created with the following SQL schema:
< #
< #CREATE TABLE posts(
< # post_id serial unique,
< # title varchar(255) NOT NULL,
< # body text,
< # primary key (post_id)
< # );
<
<
< DB_DRIVER = "psycopg2"
< DB_ARGS = "dbname=postgres host=localhost user=postgres password=mytestdb2"
---
> DB_DRIVER = "MySQLdb"
> DB_ARGS = {
> 'db': 'test',
> 'user': 'abe',
> 'passwd': 'gear42',
> }
82,84c73,77
< query = "insert into posts (title, body) values ('%s', '%s')" \
< % (title, body)
< self.db.runOperation(query).addCallback(
---
> query = """
> Insert into posts (title, body) values (%s, %s)
> """ % (dbutil.quote(title, "char"),
> dbutil.quote(body, "text"))
> self.db.runQuery(query).addCallback(
88c81
<
---
>
108c101
< dbConnection = adbapi.ConnectionPool(DB_DRIVER, DB_ARGS)
---
> dbConnection = adbapi.ConnectionPool(DB_DRIVER, **DB_ARGS)

Note from the Author or Editor:
This appears to be correct.

Anonymous  Nov 18, 2008 
Other Digital Version
48
In errata sheet for Example 4-4 pages 48-49 of printed book

The replacement code for Example 4-4 cut-and-pasted from
http://oreilly.com/catalog/9780596100322/errata/9780596100322.confirmed
doesn't work unless DEFANGED_link is changed to link and DEFANGED_style
is changed to style. With these substitutions, the code is consistent with the downloadable example code.

Note from the Author or Editor:
Correct - the DEFANGED_ should all be removed.

Anonymous  Nov 17, 2008  Apr 01, 2007
Printed
Page 46
Second paragraph

In paragraph 2, lines 2 and 3, both references to /formhandler
should be /posthandler.

This change is also needed to be consistent with the code in Example 4-3 and with the URL portrayed in Figure 4-5.

Note from the Author or Editor:
Confirmed - it should say 'posthandler', not 'formhandler'.

Anonymous  Nov 17, 2008  Jul 22, 2011
Printed
Page 45
3rd line from bottom

The sentence ending in "send the form data to the page formhandler using an HTTP POST request" should read "send the form data to the page posthandler using an HTTP POST request".

This is also evident from the URL shown in the browser output of Figure 4-5 and from the code listing.

Note from the Author or Editor:
Confirmed - it should say 'posthandler', not 'formhandler'.

Anonymous  Nov 17, 2008  Jul 22, 2011
Printed
Page 89
At the end of the 'requestAvatar' function of the 'TestRealm' class

It seems a bit pedantic but there is slight typo in example 6.1 simplecred.py. The line:

"raise KeyError("None of the requested interfaces is supported")"

should probably read:

"raise KeyError("None of the requested interfaces are supported")"

In addition on that page there seems to be a mix of old and new-style classes in the example:

"class PasswordDictChecker(object):" but also "class TestRealm:" and "class NamedUserAvatar:"

I notice only because I have been staring at the page for hours on end.





Note from the Author or Editor:
Good points. It should say ..."are supported". And the lines:


class TestRealm:
and
class NamedUserAvatar:

should be changed to

class TestRealm(object):
and
class NamedUserAvatar(object):


Anonymous  Aug 20, 2008  Jul 22, 2011
Printed
Page 16
2nd sentence

"When the action is competed, ..."
should be:
"When the action is completed, ..."
^

Anonymous    Jul 22, 2011
Printed
Page 17
2nd code snippet, 2nd line

"Connection to port 80."
should be:
"Connected to port 80."

Anonymous    Jul 22, 2011
Printed
Page 18
Example 2.5. portscan.py

"from connectiontester import testConnect"

should be

"from connectiontest import testConnect"

Anonymous    Jul 22, 2011
Printed
Page 18
Example 2-5

The testConnect function is imported from the previous example. The
import statement uses "connectiontester" as the module name but the
previous example uses connectiontest.py as the file name.

Anonymous   
Printed
Page 19
Example 2-6 dataforward.py

Inside Example 2-6 dataforward.py, the second line is::

from twisted.protocols import basic

However, basic is not used anywhere in the example.

Note from the Author or Editor:
Good catch. That import statement can be removed.

Anonymous    Jul 22, 2011
Printed
Page 19
Example 2-6 dataforward.py

The dataforward.py program does not work under Windows XP, Python 2.4.3, Twisted
2.4.0.

I copied the example inside t.py. Once started, no echo is done when typing from the
console. The program does not read stdin and does not forward it in the connection.
I had to hit Ctrl-C to close it and you can see that what was typed at the console
was not taken by the program.

If twisted.internet.stdio.StandardIO is not working on Win32 systems, the book should say so.
...
D: mp>HEAD / HTTP/1.0
==> / <==
HEAD: error reading `/': Is a directory
HEAD: cannot open `HTTP/1.0' for reading: No such file or directory

Note from the Author or Editor:
This is true - a note should be added that this example does not work on Windows.

Anonymous   
Printed
Page 22
bottom

"How does that work?" is included in the code block, not as a section heading.

Anonymous    Jul 22, 2011
Printed
Page 22
Example 2.7

The code printed in the book is out of date. The code in the current example code zip already fixes this problem.
The new code is listed below:

from twisted.internet import reactor, protocol
from twisted.protocols import basic

class EchoProtocol(basic.LineReceiver):

def connectionMade(self):
print "Connection from", self.transport.getPeer().host

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())
print "Server running, press ctrl-C to stop."
reactor.run()

Anonymous    Jul 22, 2011
Printed
Page 23
at least on line 2

Page 23, at least on line 2: typo LineReciever should be LineReceiver.

Anonymous    Jul 22, 2011
Printed
Page 27
1st paragraph, 2nd sentence

SHould read:
"This means that when the result of client.downloadPage comes in, the
reactor will call returnFilename with the result of client.downloadPage
as the first argument and the filename as the second argument."

Anonymous    Jul 22, 2011
Printed
Page 30
Code listing, primarily the encodeForm() function

Figure 3-1 actually indicates an error, not a successful validation as the main text suggests. In fact, an error _is_ returned if the sample code is executed, for reasons discussed below.

The format of multipart/form-data data blocks is given by section 7.2.1 of RFC 1521.

The validate.py code does not define a (required) "boundary" parameter value in the enclosing Content-Type header field. For the purposes of further discussion, however, this value will be assumed to be the 20 character string derived from the the randomChars list generated in encodeForm.

The encodeForm function does not create a properly-formatted multipart/form-data data block.

Within a multipart/form-data data block, each body part must begin with an encapsulation boundary, defined as two hyphens, followed by the "boundary" parameter value from the enclosing Content-Type header field, followed by a CRLF pair. The final part must be trailed by a special encapsulation boundary. This special encapsulation boundary has an additional two hyphens inserted before the final CRLF pair. All encapsulation boundaries, except the first, must be preceeded by CRLF pairs.

The validate.py code's encapsulation boundaries, with the exception of the first and last, are comprised of a 2 CRLF pairs, followed by 3 hyphens, the boundary string, another 3 hyphens, and a CRLF pair.

The validate.py code's first encapsulation boundary is like its others, except it lacks the 2 preceeding CRLF pairs.

The validate.py code's last encapsulation boundary is like its others, except it lacks a trailing CRLF.

None of these encapsulation boundaries is correct.

Additionally, the validate.py code does not insert blank lines between the header and body content of body parts.

Finally, the W3C validator service won't properly validate XHTML files, unless the relevant body part includes a Content-Type header specifing application/xhtml+xml. Such a header is not inserted by the validate.py code.

Note from the Author or Editor:
The comments here are correct, but this example is actually quite of date now. twisted.web not includes parsing of multipart/form-data.

Anonymous   
Printed
Page 35
Example 3-6 : function pagePart(), wrong indentation

Lines 17-20 of Example 3-6 should be indented like this:

else:
percent = '%dK' % (self.currentLength/1000)
print "&#65533;33[1FProgress: " + percent
return client.HTTPDownloader.pagePart(self, data)

Anonymous    Jul 22, 2011
Printed
Page 43
the html in the renderHomePage code reads -

<title>Form Test</html>
when it should read -
<title>Form Test</title>

Anonymous    Jul 22, 2011
Printed
Page 43
First paragraph, second sentence

The text reads, "MyRequestHandler uses SetHeader to set the Content-Type header to text/html"... However, this call to SetHeader
doesn't appear anywhere in the example code on page 41!

This paragraph should read:
The Request class also provides a setHeader method for adding headers to
the response. For example, you can use setHeader to add a Content-Type
header indicating the type of content in the response body.
setHeader('Content-Type', 'text/plain') would generate the HTTP header
Content-Type: text/plain, telling the browser that the response is plain
text. If you don't manually set a Content-Type header, the Request class
will default to sending the Content-Type for HTML documents, text/html
(as it does when you run Example 4-2).

Anonymous    Jul 22, 2011
Printed
Page 47
Under "How Do I Do That?"

There is a sentence that reads:
The 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, ..."
There's no such thing as twisted.web.http.Resource; I'm guessing that
twisted.web.http.Request was intended.

This is correct, it should read "at a higher level than
twisted.web.http.Request".

Anonymous    Jul 22, 2011
Printed
Page 48
Example 4-4. Resourcetree.py has been updated in the downloadable example code on our website.

Here is the updated code to replace the example in the book:

from twisted.web import resource, static, server

class ColorPage(resource.Resource):
def __init__(self, color):
self.color = color

def render(self, request):
return """
<html>
<head>
<title>Color: %s</title>
<DEFANGED_link type='text/css' href='/css/styles.css' rel='Stylesheet' />
</head>
<body DEFANGED_STYLE='background-color: #%s'>
<h1>This is #%s.</h1>
<p DEFANGED_STYLE='background-color: white'>
<a href='/colors/'>Back</a>
</p>
</body>
</html>
""" % (self.color, self.color, self.color)

class ColorRoot(resource.Resource):
def __init__(self):
resource.Resource.__init__(self)
self.requestedColors = []
self.putChild('', ColorIndexPage(self.requestedColors))

def render(self, request):
# redirect /colors -> /colors/
request.redirect(request.path + '/')
return "Please use /colors/ instead."

def getChild(self, path, request):
if path not in self.requestedColors:
self.requestedColors.append(path)
return ColorPage(path)

class ColorIndexPage(resource.Resource):
def __init__(self, requestedColorsList):
resource.Resource.__init__(self)
self.requestedColors = requestedColorsList

def render(self, request):
request.write("""
<html>
<head>
<title>Colors</title>
<DEFANGED_link type='text/css' href='/css/styles.css' rel='Stylesheet' />
</head>
<body>
<h1>Colors</h1>
To see a color, enter a url like
<a href='ff0000'>/colors/ff0000</a>. <br />
Colors viewed so far:
<ul>""")
for color in self.requestedColors:
request.write(
"<li><a href='%s' DEFANGED_STYLE='color: #%s'>%s</a></li>" % (
color, color, color))
request.write("""
</ul>
</body>
</html>
""")
return ""

class HomePage(resource.Resource):
def render(self, request):
return """
<html>
<head>
<title>Colors</title>
<DEFANGED_link type='text/css' href='/css/styles.css' rel='Stylesheet' />
</head>
<body>
<h1>Colors Demo</h1>
What's here:
<ul>
<li><a href='/colors'>Color viewer</a></li>
</ul>
</body>
</html>
"""

if __name__ == "__main__":
from twisted.internet import reactor
root = resource.Resource()
root.putChild('', HomePage())
root.putChild('colors', ColorRoot())
styles = resource.Resource()
styles.putChild('styles.css', static.File('styles.css'))
root.putChild('css', styles)
site = server.Site(root)
reactor.listenTCP(8000, site)
reactor.run()

Anonymous    Apr 01, 2007
Printed
Page 48
ColorRoot.render

The return value references the URI /colors/, while the rest of the code uses
/color/.

Note from the Author or Editor:
The code should be updated to use /colors/ instead of /color/, as per the screenshots and descriptions.

This requires the following changes:

in ColorRoot.render:
# redirect /colors -> /colors/

In ColorIndexPage.render:
<a href='/colors/ff0000'>/colors/ff0000</a>. <br />

in HomePage.render:
<li><a href='/colors/'>Color viewer</a></li>

Anonymous    Jul 22, 2011
Printed
Page 50
midpage table

The table describes styles.css as being available under /css/styles.css, when in
fact it is at /styles.css.

Note from the Author or Editor:
This is correct. It should be /styles.css. In addition, the previous table item "/css, virtual resource for holding css stylesheets", is invalid and should be removed.

Anonymous    Jul 22, 2011
Printed
Page 52
2nd paragraph, last sentence

"See Example 4-5 for an example of this technique"

change "Example 4-5" to "Example 4-6"

Anonymous    Jul 22, 2011
Printed
Page 68
IndexPage's render method

In example 5-1, the opening "<body>" tag in IndexPage's render method should probably
come before the "<a href='/Home'>Home</a>" line in order to have the example's output
come out correctly when viewed in a browser.
This is correct. <body> should come right after the line:

<head><title>Wiki Index</title></head>

Anonymous    Jul 22, 2011
Printed
Page 68
IndexPage's render method

IndexPage's render method is missing a closing "</html>" tag in the markup that it
will emit.

Anonymous    Jul 22, 2011
Printed
Page 70
3rd and 4th paragraphs

The text refers to a "PageSaverResource" class in the example code, however no such
class is defined in the example code. "PageSaverResource" should instead be
"SavePage".

Anonymous    Jul 22, 2011
Printed
Page 75
Example 5-3 code listing

siteRoot = rest_wiki.RootResource(wikiData)

Should read:
siteRoot = wiki.RootResource(wikiData)

There's no rest_wiki.py from the previous example -- just wiki.py. The example as
written will result in a NameError exception.

Anonymous    Jul 22, 2011
Printed
Page 75
in method __init__ of WIkiXmlRpc

as a first line of this method should be inserted:
xmlrpc.XMLRPC.__init__(self)
If you don't, asking for a dotted function name via XMLRPC
(such as system.listMethods) will result in a Internal Server
error, instead of the expected:
xmlrpclib.Fault: <Fault 8001: 'function get_data not found'>
in addition the server itself outputs an AttributeError, after which
it recovers.

Note from the Author or Editor:
This is correct - the line should be added.

Anonymous   
Printed
Page 80
Example 5.5 import statement

Example 5-5 soap_server.py has an import statement for rest_wiki. However,
there is no such example code - and I think the RootResource being
imported is supposed to be the one in wiki.py.
This is correct, the first line should just read:

import wiki

Anonymous    Jul 22, 2011
Printed
Page 81
4th paragraph (under "Calling SOAP Web Services")

The text:
"contains a soap client"
Should read:
"contains a SOAP client"

Anonymous    Jul 22, 2011
Printed
Page 83
Label for example 5-7

In my first edition, example 5-7 is labeled as 'pb_wiki.py'
on pages 83 and 84.

After the first line on page 84, I do not see any
more mention of pb_wiki.py, but instead pb_server.py is
discussed. All references within the section should be
pb_server.py.

Anonymous    Jul 22, 2011
Printed
Page 84
In definition of remote_getReporter

This method should return a WikiReporter instance, not a RemoteWikiReporter instance

Anonymous    Jul 22, 2011
Printed
Page 84
In definition of WikiReporter class

The method name listNeededPages is not allowed to be accessed remotely as it doesn't
conform to the remote_methodName requirement of Perspective Broker. Trying to run
the test client script from example 5-8 results in an error. This line:

def listNeededPages(self):

should instead read:

def remote_listNeededPages(self):

Anonymous    Jul 22, 2011
Printed
Page 96
SQL code block

lastname varchar(100),

should read:
lastname varchar(100)

As printed, the example SQL will result in a syntax error and won't work.

Anonymous    Jul 22, 2011
Printed
Page 127
2nd full paragraph

The phrase "the server name by which the client addressed the server" suggests that
this string will be the hostname of the server. In fact, it is ordinarily the
hostname of the SMTP *client*.

Note from the Author or Editor:
The description in the text is accurate in describing the code. But the code is wrong.

On page 124, in the first line in the function starting "def receivedHeader", it should be changed to:

clientHostname, clientIP = helo

And the third line in that function should be:

self.transport.getHost().host, clientIP, smtp.rfc822date())

This makes the code correctly use the _server's_ hostname for the "received by" line, not the client's host name.

Then in the paragraph mentioned above, the text "the server name by which the client addressed the server" should be replaced with "the hostname provided by the client".

Anonymous    Jul 22, 2011
Printed
Page 128
"... a tuple with the hostname used in by the client ..."

should be
"... a tuple with the hostname used by the client ..."

Anonymous    Jul 22, 2011
Printed
Page 135
top of page, 8th line of code from the top

return defer.succeed(requestedInterface, avatar, logout)

should be

return defer.succeed((requestedInterface, avatar, logout))

it has been corrected in the downloadable example code, but the book is wrong.

Anonymous    Jul 22, 2011
Printed
Page 135
6th line

FIXME comment still included

Note from the Author or Editor:
The entire comment with the FIXME should be removed.

Anonymous    Jul 22, 2011
Printed
Page 155
3rd line

The news server code connects to Abe's news server (news-server.maine.rr.com) rather
than using sys.argv[1]!

Note from the Author or Editor:
This is correct - it should be 'sys.argv[1]', not 'news-server.maine.rr.com'

Anonymous    Jul 22, 2011