2
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
3
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
<html xmlns="http://www.w3.org/1999/xhtml">
7
<title>The World of Software is a World of Constant
12
<p><em><strong>Note:</strong> This document is relevant for the
13
version of Twisted that was current at <a
14
href="http://www.python10.com">IPC10</a>. It has since been
15
superseded by many changes to the Python API. It is remaining
16
unchanged for historical reasons, but please refer to
17
documentation for the specific system you are looking for and
18
not these papers for current information.</em></p>
20
<h1>The World of Software is a World of Constant Change</h1>
22
<p>Twisted has undergone several major revisions since Moshe
23
Zadka and I wrote the <a href="ipc10paper.html">"The Twisted
24
Network Framework"</a>. Most of these changes have not deviated
25
from the central vision of the framework, but almost all of the
26
code listings have been re-visited and enhanced in some
29
<p>So, while the paper was correct at the time that it was
30
originally written, a few things have changed which have
31
invalidated portions of it.</p>
33
<p>Most significant is the fact that almost all methods which
34
pass callbacks of some kind have been changed to take no
35
callback or error-callback arguments, and instead return an
37
class="API">twisted.python.defer.Deferred</code>. This means
38
that an asynchronous function can be easily identified visually
39
because it will be of the form: <code
40
class="python">async_obj.asyncMethod("foo")<b>.addCallbacks(succeded,
41
failed)</b></code>. There is also a utility method <code
42
class="python">addCallback</code> which makes it more
43
convenient to pass additional arguments to a callback function
44
and omit special-case error handling.</p>
46
<p>While it is still backwards compatible, <code
47
class="API">twisted.internet.passport</code> has been re-named
48
to <code class="API">twisted.cred</code>, and the various
49
classes in it have been split out into submodules of that
50
package, and the various remote-object superclasses have been
51
moved out of twisted.spread.pb and put into
52
twisted.spread.flavors.</p>
54
<p><code class="python">Application.listenOn</code> has been
55
replaced with the more descripively named <code
56
class="python">Application.listenTCP</code>, <code
57
class="python">Application.listenUDP</code>, and <code
58
class="python">Application.listenSSL</code>.</p>
60
<p><code class="API">twisted.web.widgets</code> has progressed
61
quite far since the paper was written! One description
62
specifically given in the paper is no longer correct:</p>
65
The namespace for evaluating the template expressions is
66
obtained by scanning the class hierarchy for attributes, and
67
getting each of those attributes from the current instance.
68
This means that all methods will be bound methods, so
69
indicating "self" explicitly is not required. While it is
70
possible to override the method for creating namespaces,
71
using this default has the effect of associating all
72
presentation code for a particular widget in one class, along
73
with its template. If one is working with a non-programmer
74
designer, and the template is in an external file, it is
75
always very clear to the designer what functionality is
76
available to them in any given scope, because there is a list
77
of available methods for any given class.
79
<p>This is still possible to avoid breakages in old code, but
80
after some experimentation, it became clear that simply passing
81
<code class="python">self</code> was an easier method for
82
creating the namespace, both for designers and programmers.</p>
83
<p>In addition, since the advent of Zope3, interoperability
84
with Zope has become increasingly interesting possibility for
85
the Twisted development team, since it would be desirable if
86
Twisted could use their excellent strategy for
87
content-management, while still maintaining Twisted's
88
advantages in the arena of multi-protocol servers. Of
89
particular interest has been Zope Presentation Templates, since
90
they seem to be a truly robust solution for keeping design
91
discrete from code, compatible with the event-based method in
92
which twisted.web.widgets processes web requests. <code
93
class="API">twisted.web.widgets.ZopePresentationTemplate</code>
94
may be opening soon in a theatre near you!</p>
96
<p>The following code examples are corrected or modernized
97
versions of the ones that appear in the paper.</p>
100
Listing 9: A remotely accessible object and accompanying call
104
class MyObject(pb.Referenceable):
105
def remote_doIt(self):
110
def myCallback(result):
111
print result # result will be 'did it'
112
def myErrback(stacktrace):
113
print 'oh no, mr. bill!'
115
myRemoteReference.doIt().addCallbacks(myCallback,
121
Listing 10: An object responding to its calling perspective
124
class Greeter(pb.Viewable):
125
def view_greet(self, actor):
126
return "Hello %s!\n" % actor.perspectiveName
130
remoteGreeter.greet().addCallback(sys.stdout.write)
137
Listing 12: A client for Echoer objects.
139
from twisted.spread import pb
140
from twisted.internet import main
141
def gotObject(object):
142
print "got object:",object
143
object.echo("hello network".addCallback(gotEcho)
145
print 'server echoed:',echo
147
def gotNoObject(reason):
148
print "no object:",reason
150
pb.getObjectAt("localhost", 8789, gotObject, gotNoObject, 30)
156
Listing 13: A PB server using twisted's "passport"
159
from twisted.spread import pb
160
from twisted.internet import main
161
class SimplePerspective(pb.Perspective):
162
def perspective_echo(self, text):
165
class SimpleService(pb.Service):
166
def getPerspectiveNamed(self, name):
167
return SimplePerspective(name, self)
168
if __name__ == '__main__':
170
app = main.Application("pbecho")
171
pbecho.SimpleService("pbecho",app).getPerspectiveNamed("guest").makeIdentity("guest")
172
app.listenTCP(pb.portno, pb.BrokerFactory(pb.AuthRoot(app)))
178
Listing 14: Connecting to an Authorized Service
180
from twisted.spread import pb
181
from twisted.internet import main
182
def success(message):
183
print "Message received:",message
186
print "Failure...",error
188
def connected(perspective):
189
perspective.echo("hello world").addCallbacks(success, failure)
192
pb.connect("localhost", pb.portno, "guest", "guest",
193
"pbecho", "guest", 30).addCallbacks(connected,
200
Listing 15: A Twisted GUI application
202
from twisted.internet import main, ingtkernet
203
from twisted.spread.ui import gtkutil
207
def __init__(self, echoer):
210
w = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL)
211
vb = gtk.GtkVBox(); b = gtk.GtkButton("Echo:")
212
self.entry = gtk.GtkEntry(); self.outry = gtk.GtkEntry()
214
map(vb.add, [b, self.entry, self.outry])
215
b.connect('clicked', self.clicked)
216
w.connect('destroy', gtk.mainquit)
218
def clicked(self, b):
219
txt = self.entry.get_text()
220
self.entry.set_text("")
221
self.echoer.echo(txt).addCallback(self.outry.set_text)
222
l = gtkutil.Login(EchoClient, None, initialService="pbecho")
229
Listing 16: an event-based web widget.
231
from twisted.spread import pb
232
from twisted.python import defer
233
from twisted.web import widgets
234
class EchoDisplay(widgets.Gadget, widgets.Presentation):
235
template = """<H1>Welcome to my widget, displaying %%%%echotext%%%%.</h1>
236
<p>Here it is: %%%%getEchoPerspective()%%%%</p>"""
237
echotext = 'hello web!'
238
def getEchoPerspective(self):
240
pb.connect("localhost", pb.portno,
241
"guest", "guest", "pbecho", "guest", 1).
242
addCallbacks(self.makeListOf, self.formatTraceback)
244
def makeListOf(self, echoer):
245
return [echoer.echo(self.echotext).addCallback(lambda x: [x])]
246
if __name__ == "__main__":
247
from twisted.web import server
248
from twisted.internet import main
249
a = main.Application("pbweb")
250
a.listenTCP(8080, server.Site(EchoDisplay()))