<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: The Evolution of Finger: making a finger library</title><link href="../../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">The Evolution of Finger: making a finger library</h1><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><div class="content"><span></span><h2>Introduction<a name="auto0"></a></h2><p> This is the tenth part of the Twisted tutorial <a href="index.html">Twisted from Scratch, or The Evolution of Finger</a>.</p><p>In this part, we separate the application code that launches a finger service from the library code which defines a finger service, placing the application in a Twisted Application Configuration (.tac) file. We also move configuration (such as HTML templates) into separate files.</p><h2>Organization<a name="auto1"></a></h2><p>Now this code, while quite modular and well-designed, isn't properly organized. Everything above the <code>application=</code> belongs in a module, and the HTML templates all belong in separate files. </p><p>We can use the templateFile and templateDirectory attributes to indicate what HTML template file to use for each Page, and where to look for it.</p><div class="py-listing"><pre> <span class="py-src-comment"># organized-finger.tac </span><span class="py-src-comment"># eg: twistd -ny organized-finger.tac </span> <span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span> <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> <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> <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> <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> <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> <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>) <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>) <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>) <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>) ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>) <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>)) <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> ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</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">finger</span>.<span class="py-src-variable">ServerContextFactory</span>() ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>) <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>) <span class="py-src-variable">i</span>.<span class="py-src-variable">nickname</span> = <span class="py-src-string">'fingerbot'</span> <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> ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>) <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>)) ).<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">serviceCollection</span>) </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><p> Note that our program is now quite separated. We have: <ul><li>Code (in the module)</li><li>Configuration (file above)</li><li>Presentation (templates)</li><li>Content (/etc/users)</li><li>Deployment (twistd)</li></ul> Prototypes don't need this level of separation, so our earlier examples all bunched together. However, real applications do. Thankfully, if we write our code correctly, it is easy to achieve a good separation of parts. </p><h2>Easy Configuration<a name="auto2"></a></h2><p>We can also supply easy configuration for common cases with a makeService method that will also help build .tap files later:</p><div class="py-listing"><pre> <span class="py-src-comment"># Easy configuration </span><span class="py-src-comment"># makeService from finger module </span> <span class="py-src-keyword">def</span> <span class="py-src-identifier">makeService</span>(<span class="py-src-parameter">config</span>): <span class="py-src-comment"># finger on port 79 </span> <span class="py-src-variable">s</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">MultiService</span>() <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>]) <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>)) <span class="py-src-variable">h</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>) <span class="py-src-comment"># website on port 8000 </span> <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>) <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>] <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>) <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>) <span class="py-src-variable">j</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>) <span class="py-src-comment"># ssl on port 443 </span> <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>): <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>()) <span class="py-src-variable">k</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>) <span class="py-src-comment"># irc fingerbot </span> <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>): <span class="py-src-variable">i</span> = <span class="py-src-variable">IIRCClientFactory</span>(<span class="py-src-variable">f</span>) <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>] <span class="py-src-variable">ircserver</span> = <span class="py-src-variable">config</span>[<span class="py-src-string">'ircserver'</span>] <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>) <span class="py-src-variable">b</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>) <span class="py-src-comment"># Pespective Broker on port 8889 </span> <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>): <span class="py-src-variable">m</span> = <span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>( <span class="py-src-variable">int</span>(<span class="py-src-variable">config</span>[<span class="py-src-string">'pbport'</span>]), <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>))) <span class="py-src-variable">m</span>.<span class="py-src-variable">setServiceParent</span>(<span class="py-src-variable">s</span>) <span class="py-src-keyword">return</span> <span class="py-src-variable">s</span> </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><p>And we can write simpler files now:</p><div class="py-listing"><pre> <span class="py-src-comment"># simple-finger.tac </span><span class="py-src-comment"># eg: twistd -ny simple-finger.tac </span> <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> <span class="py-src-keyword">import</span> <span class="py-src-variable">finger</span> <span class="py-src-variable">options</span> = { <span class="py-src-string">'file'</span>: <span class="py-src-string">'/etc/users'</span>, <span class="py-src-string">'templates'</span>: <span class="py-src-string">'/usr/share/finger/templates'</span>, <span class="py-src-string">'ircnick'</span>: <span class="py-src-string">'fingerbot'</span>, <span class="py-src-string">'ircserver'</span>: <span class="py-src-string">'irc.freenode.net'</span>, <span class="py-src-string">'pbport'</span>: <span class="py-src-number">8889</span>, <span class="py-src-string">'ssl'</span>: <span class="py-src-string">'ssl=0'</span> } <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>) <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>) <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>)) </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><pre class="shell"> % twisted -ny simple-finger.tac </pre><p>Note: the finger <em>user</em> still has ultimate power: he can use makeService, or he can use the lower-level interface if he has specific needs (maybe an IRC server on some other port? maybe we want the non-SSL webserver to listen only locally? etc. etc.) This is an important design principle: never force a layer of abstraction: allow usage of layers of abstractions.</p><p>The pasta theory of design:</p><ul><li>Spaghetti: each piece of code interacts with every other piece of code [can be implemented with GOTO, functions, objects]</li><li>Lasagna: code has carefully designed layers. Each layer is, in theory independent. However low-level layers usually cannot be used easily, and high-level layers depend on low-level layers.</li><li>Ravioli: each part of the code is useful by itself. There is a thin layer of interfaces between various parts [the sauce]. Each part can be usefully be used elsewhere.</li><li>...but sometimes, the user just wants to order <q>Ravioli</q>, so one coarse-grain easily definable layer of abstraction on top of it all can be useful.</li></ul></div><p><a href="../../howto/index.html">Index</a></p><span class="version">Version: 2.5.0</span></body></html>