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 templateFile and templateDirectory attributes to indicate
33
what HTML template file to use for each Page, and where to look for it.</p>
35
<div class="py-listing"><pre><p class="py-linenumber"> 1
66
</p><span class="py-src-comment"># organized-finger.tac</span>
67
<span class="py-src-comment"># eg: twistd -ny organized-finger.tac</span>
69
<span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span>
71
<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>
72
<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>
73
<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>
74
<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>
75
<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>
77
<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>)
78
<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>)
79
<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>)
80
<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>)
81
).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
83
<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>))
84
<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>
85
).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
87
<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>()
88
).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
90
<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>)
91
<span class="py-src-variable">i</span>.<span class="py-src-variable">nickname</span> = <span class="py-src-string">'fingerbot'</span>
92
<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>
93
).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
95
<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>))
96
).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>)
97
</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>
100
Note that our program is now quite separated. We have:
102
<li>Code (in the module)</li>
103
<li>Configuration (file above)</li>
104
<li>Presentation (templates)</li>
105
<li>Content (/etc/users)</li>
106
<li>Deployment (twistd)</li>
109
Prototypes don't need this level of separation, so our earlier examples all
110
bunched together. However, real applications do. Thankfully, if we write our
111
code correctly, it is easy to achieve a good separation of parts.
115
<h2>Easy Configuration<a name="auto2"/></h2>
117
<p>We can also supply easy configuration for common cases with a makeService method that will also help build .tap files later:</p>
119
<div class="py-listing"><pre><p class="py-linenumber"> 1
157
</p><span class="py-src-comment"># Easy configuration</span>
158
<span class="py-src-comment"># makeService from finger module</span>
160
<span class="py-src-keyword">def</span> <span class="py-src-identifier">makeService</span>(<span class="py-src-parameter">config</span>):
161
<span class="py-src-comment"># finger on port 79</span>
162
<span class="py-src-variable">s</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">MultiService</span>()
163
<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>])
164
<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>))
165
<span class="py-src-variable">h</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
167
<span class="py-src-comment"># website on port 8000</span>
168
<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>)
169
<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>]
170
<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>)
171
<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>)
172
<span class="py-src-variable">j</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
174
<span class="py-src-comment"># ssl on port 443</span>
175
<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>):
176
<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>())
177
<span class="py-src-variable">k</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
179
<span class="py-src-comment"># irc fingerbot</span>
180
<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>):
181
<span class="py-src-variable">i</span> = <span class="py-src-variable">IIRCClientFactory</span>(<span class="py-src-variable">f</span>)
182
<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>]
183
<span class="py-src-variable">ircserver</span> = <span class="py-src-variable">config</span>[<span class="py-src-string">'ircserver'</span>]
184
<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>)
185
<span class="py-src-variable">b</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
187
<span class="py-src-comment"># Pespective Broker on port 8889</span>
188
<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>):
189
<span class="py-src-variable">m</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(
190
<span class="py-src-variable">int</span>(<span class="py-src-variable">config</span>[<span class="py-src-string">'pbport'</span>]),
191
<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>)))
192
<span class="py-src-variable">m</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>)
194
<span class="py-src-keyword">return</span> <span class="py-src-variable">s</span>
195
</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>
197
<p>And we can write simpler files now:</p>
199
<div class="py-listing"><pre><p class="py-linenumber"> 1
216
</p><span class="py-src-comment"># simple-finger.tac</span>
217
<span class="py-src-comment"># eg: twistd -ny simple-finger.tac</span>
219
<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>
221
<span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span>
223
<span class="py-src-variable">options</span> = { <span class="py-src-string">'file'</span>: <span class="py-src-string">'/etc/users'</span>,
224
<span class="py-src-string">'templates'</span>: <span class="py-src-string">'/usr/share/finger/templates'</span>,
225
<span class="py-src-string">'ircnick'</span>: <span class="py-src-string">'fingerbot'</span>,
226
<span class="py-src-string">'ircserver'</span>: <span class="py-src-string">'irc.freenode.net'</span>,
227
<span class="py-src-string">'pbport'</span>: <span class="py-src-number">8889</span>,
228
<span class="py-src-string">'ssl'</span>: <span class="py-src-string">'ssl=0'</span> }
230
<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>)
231
<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>)
232
<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>))
233
</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>
235
<pre class="shell" xml:space="preserve">
236
% twisted -ny simple-finger.tac
240
<p>Note: the finger <em>user</em> still has ultimate power: he can use
241
makeService, or he can use the lower-level interface if he has
242
specific needs (maybe an IRC server on some other port? maybe we
243
want the non-SSL webserver to listen only locally? etc. etc.)
244
This is an important design principle: never force a layer of abstraction:
245
allow usage of layers of abstractions.</p>
247
<p>The pasta theory of design:</p>
250
<li>Spaghetti: each piece of code interacts with every other piece of
251
code [can be implemented with GOTO, functions, objects]</li>
252
<li>Lasagna: code has carefully designed layers. Each layer is, in
253
theory independent. However low-level layers usually cannot be
254
used easily, and high-level layers depend on low-level layers.</li>
255
<li>Ravioli: each part of the code is useful by itself. There is a thin
256
layer of interfaces between various parts [the sauce]. Each part
257
can be usefully be used elsewhere.</li>
258
<li>...but sometimes, the user just wants to order <q>Ravioli</q>, so one
259
coarse-grain easily definable layer of abstraction on top of it all
266
<p><a href="../index.html">Index</a></p>
267
<span class="version">Version: 10.0.0</span>
b'\\ No newline at end of file'