1 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
3 <title>Twisted Documentation: The Evolution of Finger: making a finger library</title>
4 <link href="../stylesheet.css" rel="stylesheet" type="text/css"/>
8 <h1 class="title">The Evolution of Finger: making a finger library</h1>
9 <div class="toc"><ol><li><a href="#auto0">Introduction</a></li><li><a href="#auto1">Organization</a></li><li><a href="#auto2">Easy Configuration</a></li></ol></div>
14 <h2>Introduction<a name="auto0"/></h2>
16 <p> This is the tenth part of the Twisted tutorial <a href="index.html" shape="rect">Twisted from Scratch, or The Evolution of Finger</a>.</p>
18 <p>In this part, we separate the application code that launches a finger service
19 from the library code which defines a finger service, placing the application in
20 a Twisted Application Configuration (.tac) file. We also move configuration
21 (such as HTML templates) into separate files. Configuration and deployment with
22 .tac and twistd are introduced in <a href="../application.html" shape="rect">Using the
23 Twisted Application Framework</a>.</p>
25 <h2>Organization<a name="auto1"/></h2>
27 <p>Now this code, while quite modular and well-designed, isn't
28 properly organized. Everything above the <code>application=</code> belongs in a
29 module, and the HTML templates all belong in separate files.
32 <p>We can use the <code>templateFile</code> and <code>templateDirectory</code>
33 attributes to indicate what HTML template file to use for each Page, and where
36 <div class="py-listing"><pre><p class="py-linenumber"> 1
67 </p><span class="py-src-comment"># organized-finger.tac</span>
68 <span class="py-src-comment"># eg: twistd -ny organized-finger.tac</span>
70 <span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span>
72 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">protocol</span>, <span class="py-src-variable">reactor</span>, <span class="py-src-variable">defer</span>
73 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
74 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">web</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">resource</span>, <span class="py-src-variable">server</span>
75 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">internet</span>, <span class="py-src-variable">service</span>, <span class="py-src-variable">strports</span>
76 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
78 <span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">'finger'</span>, <span class="py-src-variable">uid</span>=<span class="py-src-number">1</span>, <span class="py-src-variable">gid</span>=<span class="py-src-number">1</span>)
79 <span class="py-src-variable">f</span> = <span class="py-src-variable">finger</span>.<span class="py-src-variable">FingerService</span>(<span class="py-src-string">'/etc/users'</span>)
80 <span class="py-src-variable">serviceCollection</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">IServiceCollection</span>(<span class="py-src-variable">application</span>)
81 <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">79</span>, <span class="py-src-variable">finger</span>.<span class="py-src-variable">IFingerFactory</span>(<span class="py-src-variable">f</span>)
82 ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
84 <span class="py-src-variable">site</span> = <span class="py-src-variable">server</span>.<span class="py-src-variable">Site</span>(<span class="py-src-variable">resource</span>.<span class="py-src-variable">IResource</span>(<span class="py-src-variable">f</span>))
85 <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8000</span>, <span class="py-src-variable">site</span>
86 ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
88 <span class="py-src-variable">internet</span>.<span class="py-src-variable">SSLServer</span>(<span class="py-src-number">443</span>, <span class="py-src-variable">site</span>, <span class="py-src-variable">finger</span>.<span class="py-src-variable">ServerContextFactory</span>()
89 ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
91 <span class="py-src-variable">i</span> = <span class="py-src-variable">finger</span>.<span class="py-src-variable">IIRCClientFactory</span>(<span class="py-src-variable">f</span>)
92 <span class="py-src-variable">i</span>.<span class="py-src-variable">nickname</span> = <span class="py-src-string">'fingerbot'</span>
93 <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-string">'irc.freenode.org'</span>, <span class="py-src-number">6667</span>, <span class="py-src-variable">i</span>
94 ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
96 <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8889</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">finger</span>.<span class="py-src-variable">IPerspectiveFinger</span>(<span class="py-src-variable">f</span>))
97 ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
98 </pre><div class="caption">Source listing - <a href="listings/finger/organized-finger.tac"><span class="filename">listings/finger/organized-finger.tac</span></a></div></div>
101 Note that our program is now quite separated. We have:
103 <li>Code (in the module)</li>
104 <li>Configuration (file above)</li>
105 <li>Presentation (templates)</li>
106 <li>Content (<code>/etc/users</code>)</li>
107 <li>Deployment (twistd)</li>
110 Prototypes don't need this level of separation, so our earlier examples all
111 bunched together. However, real applications do. Thankfully, if we write our
112 code correctly, it is easy to achieve a good separation of parts.
116 <h2>Easy Configuration<a name="auto2"/></h2>
118 <p>We can also supply easy configuration for common cases with a makeService
119 method that will also help build .tap files later:</p>
121 <div class="py-listing"><pre><p class="py-linenumber"> 1
159 </p><span class="py-src-comment"># Easy configuration</span>
160 <span class="py-src-comment"># makeService from finger module</span>
162 <span class="py-src-keyword">def</span> <span class="py-src-identifier">makeService</span>(<span class="py-src-parameter">config</span>):
163 <span class="py-src-comment"># finger on port 79</span>
164 <span class="py-src-variable">s</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">MultiService</span>()
165 <span class="py-src-variable">f</span> = <span class="py-src-variable">FingerService</span>(<span class="py-src-variable">config</span>[<span class="py-src-string">'file'</span>])
166 <span class="py-src-variable">h</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">79</span>, <span class="py-src-variable">IFingerFactory</span>(<span class="py-src-variable">f</span>))
167 <span class="py-src-variable">h</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
169 <span class="py-src-comment"># website on port 8000</span>
170 <span class="py-src-variable">r</span> = <span class="py-src-variable">resource</span>.<span class="py-src-variable">IResource</span>(<span class="py-src-variable">f</span>)
171 <span class="py-src-variable">r</span>.<span class="py-src-variable">templateDirectory</span> = <span class="py-src-variable">config</span>[<span class="py-src-string">'templates'</span>]
172 <span class="py-src-variable">site</span> = <span class="py-src-variable">server</span>.<span class="py-src-variable">Site</span>(<span class="py-src-variable">r</span>)
173 <span class="py-src-variable">j</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8000</span>, <span class="py-src-variable">site</span>)
174 <span class="py-src-variable">j</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
176 <span class="py-src-comment"># ssl on port 443</span>
177 <span class="py-src-keyword">if</span> <span class="py-src-variable">config</span>.<span class="py-src-variable">get</span>(<span class="py-src-string">'ssl'</span>):
178 <span class="py-src-variable">k</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">SSLServer</span>(<span class="py-src-number">443</span>, <span class="py-src-variable">site</span>, <span class="py-src-variable">ServerContextFactory</span>())
179 <span class="py-src-variable">k</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
181 <span class="py-src-comment"># irc fingerbot</span>
182 <span class="py-src-keyword">if</span> <span class="py-src-variable">config</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-string">'ircnick'</span>):
183 <span class="py-src-variable">i</span> = <span class="py-src-variable">IIRCClientFactory</span>(<span class="py-src-variable">f</span>)
184 <span class="py-src-variable">i</span>.<span class="py-src-variable">nickname</span> = <span class="py-src-variable">config</span>[<span class="py-src-string">'ircnick'</span>]
185 <span class="py-src-variable">ircserver</span> = <span class="py-src-variable">config</span>[<span class="py-src-string">'ircserver'</span>]
186 <span class="py-src-variable">b</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPClient</span>(<span class="py-src-variable">ircserver</span>, <span class="py-src-number">6667</span>, <span class="py-src-variable">i</span>)
187 <span class="py-src-variable">b</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
189 <span class="py-src-comment"># Pespective Broker on port 8889</span>
190 <span class="py-src-keyword">if</span> <span class="py-src-variable">config</span>.<span class="py-src-variable">has_key</span>(<span class="py-src-string">'pbport'</span>):
191 <span class="py-src-variable">m</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(
192 <span class="py-src-variable">int</span>(<span class="py-src-variable">config</span>[<span class="py-src-string">'pbport'</span>]),
193 <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">IPerspectiveFinger</span>(<span class="py-src-variable">f</span>)))
194 <span class="py-src-variable">m</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
196 <span class="py-src-keyword">return</span> <span class="py-src-variable">s</span>
197 </pre><div class="caption">Source listing - <a href="listings/finger/finger_config.py"><span class="filename">listings/finger/finger_config.py</span></a></div></div>
199 <p>And we can write simpler files now:</p>
201 <div class="py-listing"><pre><p class="py-linenumber"> 1
218 </p><span class="py-src-comment"># simple-finger.tac</span>
219 <span class="py-src-comment"># eg: twistd -ny simple-finger.tac</span>
221 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">service</span>
223 <span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span>
225 <span class="py-src-variable">options</span> = { <span class="py-src-string">'file'</span>: <span class="py-src-string">'/etc/users'</span>,
226 <span class="py-src-string">'templates'</span>: <span class="py-src-string">'/usr/share/finger/templates'</span>,
227 <span class="py-src-string">'ircnick'</span>: <span class="py-src-string">'fingerbot'</span>,
228 <span class="py-src-string">'ircserver'</span>: <span class="py-src-string">'irc.freenode.net'</span>,
229 <span class="py-src-string">'pbport'</span>: <span class="py-src-number">8889</span>,
230 <span class="py-src-string">'ssl'</span>: <span class="py-src-string">'ssl=0'</span> }
232 <span class="py-src-variable">ser</span> = <span class="py-src-variable">finger</span>.<span class="py-src-variable">makeService</span>(<span class="py-src-variable">options</span>)
233 <span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">'finger'</span>, <span class="py-src-variable">uid</span>=<span class="py-src-number">1</span>, <span class="py-src-variable">gid</span>=<span class="py-src-number">1</span>)
234 <span class="py-src-variable">ser</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">service</span>.<span class="py-src-variable">IServiceCollection</span>(<span class="py-src-variable">application</span>))
235 </pre><div class="caption">Source listing - <a href="listings/finger/simple-finger.tac"><span class="filename">listings/finger/simple-finger.tac</span></a></div></div>
237 <pre class="shell" xml:space="preserve">
238 % twisted -ny simple-finger.tac
242 <p>Note: the finger <em>user</em> still has ultimate power: he can use
243 <code>makeService</code>, or he can use the lower-level interface if he has
244 specific needs (maybe an IRC server on some other port? Maybe we want the
245 non-SSL webserver to listen only locally? etc. etc.) This is an important
246 design principle: never force a layer of abstraction: allow usage of layers of
249 <p>The pasta theory of design:</p>
252 <li>Spaghetti: each piece of code interacts with every other piece of
253 code [can be implemented with GOTO, functions, objects]</li>
254 <li>Lasagna: code has carefully designed layers. Each layer is, in
255 theory independent. However low-level layers usually cannot be
256 used easily, and high-level layers depend on low-level layers.</li>
257 <li>Ravioli: each part of the code is useful by itself. There is a thin
258 layer of interfaces between various parts [the sauce]. Each part
259 can be usefully be used elsewhere.</li>
260 <li>...but sometimes, the user just wants to order <q>Ravioli</q>, so one
261 coarse-grain easily definable layer of abstraction on top of it all
268 <p><a href="../index.html">Index</a></p>
269 <span class="version">Version: 12.1.0</span>