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()))