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: Writing a twistd Plugin</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
8 <h1 class="title">Writing a twistd Plugin</h1>
9 <div class="toc"><ol><li><a href="#auto0">Goals</a></li><li><a href="#auto1">Alternatives to twistd plugins</a></li><li><a href="#auto2">Creating the plugin</a></li><li><a href="#auto3">Using cred with your TAP</a></li><li><a href="#auto4">Conclusion</a></li></ol></div>
13 <p>This document describes adding subcommands to
14 the <code>twistd</code> command, as a way to facilitate the deployment
15 of your applications. <em>(This feature was added in Twisted 2.5)</em></p>
17 <p>The target audience of this document are those that have developed
18 a Twisted application which needs a command line-based deployment
21 <p>There are a few prerequisites to understanding this document:</p>
23 <li>A basic understanding of the Twisted Plugin System (i.e.,
24 the <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.plugin.html" title="twisted.plugin">twisted.plugin</a></code> module) is
25 necessary, however, step-by-step instructions will be
26 given. Reading <a href="plugin.html" shape="rect">The Twisted Plugin
27 System</a> is recommended, in particular the <q>Extending an
28 Existing Program</q> section.</li>
29 <li>The <a href="application.html" shape="rect">Application</a> infrastructure
30 is used in <code>twistd</code> plugins; in particular, you should
31 know how to expose your program's functionality as a Service.</li>
32 <li>In order to parse command line arguments, the <code>twistd</code> plugin
34 on <code>twisted.python.usage</code>, which is documented
35 in <a href="options.html" shape="rect">Using usage.Options</a>.</li>
38 <h2>Goals<a name="auto0"/></h2>
40 <p>After reading this document, the reader should be able to expose
41 their Service-using application as a subcommand
42 of <code>twistd</code>, taking into consideration whatever was passed
43 on the command line.</p>
45 <h2>Alternatives to twistd plugins<a name="auto1"/></h2>
46 <p>The major alternative to the twistd plugin mechanism is the <code>.tac</code>
47 file, which is a simple script to be used with the
48 twistd <code>-y/--python</code> parameter. The twistd plugin mechanism
49 exists to offer a more extensible command-line-driven interface to
50 your application. For more information on <code>.tac</code> files, see
51 the document <a href="application.html" shape="rect">Using the Twisted Application
55 <h2>Creating the plugin<a name="auto2"/></h2>
57 <p>The following directory structure is assumed of your project:</p>
60 <li><strong>MyProject</strong> - Top level directory
62 <li><strong>myproject</strong> - Python package
63 <ul><li><strong>__init__.py</strong></li></ul>
70 During development of your project, Twisted plugins can be loaded
71 from a special directory in your project, assuming your top level
72 directory ends up in sys.path. Create a directory
73 named <code>twisted</code> containing a directory
74 named <code>plugins</code>, and add a file
75 named <code>myproject_plugin.py</code> to it. This file will contain your
76 plugin. Note that you should <em>not</em> add any __init__.py files
77 to this directory structure, and the plugin file should <em>not</em>
78 be named <code>myproject.py</code> (because that would conflict with
79 your project's module name).
83 In this file, define an object which <em>provides</em> the interfaces
84 <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.plugin.IPlugin.html" title="twisted.plugin.IPlugin">twisted.plugin.IPlugin</a></code>
85 and <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.application.service.IServiceMaker.html" title="twisted.application.service.IServiceMaker">twisted.application.service.IServiceMaker</a></code>.
88 <p>The <code>tapname</code> attribute of your IServiceMaker provider
89 will be used as the subcommand name in a command
90 like <code class="shell">twistd [subcommand] [args...]</code>, and
91 the <code>options</code> attribute (which should be
92 a <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.python.usage.Options.html" title="twisted.python.usage.Options">usage.Options</a></code>
93 subclass) will be used to parse the given args.</p>
95 <pre class="python"><p class="py-linenumber"> 1
127 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">zope</span>.<span class="py-src-variable">interface</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">implements</span>
129 <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">usage</span>
130 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">plugin</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IPlugin</span>
131 <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-variable">service</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IServiceMaker</span>
132 <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>
134 <span class="py-src-keyword">from</span> <span class="py-src-variable">myproject</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">MyFactory</span>
137 <span class="py-src-keyword">class</span> <span class="py-src-identifier">Options</span>(<span class="py-src-parameter">usage</span>.<span class="py-src-parameter">Options</span>):
138 <span class="py-src-variable">optParameters</span> = [[<span class="py-src-string">"port"</span>, <span class="py-src-string">"p"</span>, <span class="py-src-number">1235</span>, <span class="py-src-string">"The port number to listen on."</span>]]
141 <span class="py-src-keyword">class</span> <span class="py-src-identifier">MyServiceMaker</span>(<span class="py-src-parameter">object</span>):
142 <span class="py-src-variable">implements</span>(<span class="py-src-variable">IServiceMaker</span>, <span class="py-src-variable">IPlugin</span>)
143 <span class="py-src-variable">tapname</span> = <span class="py-src-string">"myproject"</span>
144 <span class="py-src-variable">description</span> = <span class="py-src-string">"Run this! It'll make your dog happy."</span>
145 <span class="py-src-variable">options</span> = <span class="py-src-variable">Options</span>
147 <span class="py-src-keyword">def</span> <span class="py-src-identifier">makeService</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">options</span>):
148 <span class="py-src-string">"""
149 Construct a TCPServer from a factory defined in myproject.
150 """</span>
151 <span class="py-src-keyword">return</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">options</span>[<span class="py-src-string">"port"</span>]), <span class="py-src-variable">MyFactory</span>())
154 <span class="py-src-comment"># Now construct an object which *provides* the relevant interfaces</span>
155 <span class="py-src-comment"># The name of this variable is irrelevant, as long as there is *some*</span>
156 <span class="py-src-comment"># name bound to a provider of IPlugin and IServiceMaker.</span>
158 <span class="py-src-variable">serviceMaker</span> = <span class="py-src-variable">MyServiceMaker</span>()
162 Now running <code class="shell">twistd --help</code> should
163 print <code>myproject</code> in the list of available subcommands,
164 followed by the description that we specified in the
165 plugin. <code class="shell">twistd -n myproject</code> would,
166 assuming we defined a <code>MyFactory</code> factory
167 inside <code>myproject</code>, start a listening server on port 1235
171 <h2>Using cred with your TAP<a name="auto3"/></h2>
174 Twisted ships with a robust authentication framework to use with
175 your application. If your server needs authentication functionality,
176 and you haven't read about <a href="cred.html" shape="rect">twisted.cred</a>
177 yet, read up on it first.
181 If you are building a twistd plugin and you want to support a wide
182 variety of authentication patterns, Twisted provides an easy-to-use
183 mixin for your Options subclass:
184 <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.cred.strcred.AuthOptionMixin.html" title="twisted.cred.strcred.AuthOptionMixin">strcred.AuthOptionMixin</a></code>.
185 The following code is an example of using this mixin:
188 <pre class="python"><p class="py-linenumber"> 1
236 </p><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">cred</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">credentials</span>, <span class="py-src-variable">portal</span>, <span class="py-src-variable">strcred</span>
237 <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">usage</span>
238 <span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">plugin</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IPlugin</span>
239 <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-variable">service</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">IServiceMaker</span>
240 <span class="py-src-keyword">from</span> <span class="py-src-variable">myserver</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">myservice</span>
242 <span class="py-src-keyword">class</span> <span class="py-src-identifier">ServerOptions</span>(<span class="py-src-parameter">usage</span>.<span class="py-src-parameter">Options</span>, <span class="py-src-parameter">strcred</span>.<span class="py-src-parameter">AuthOptionMixin</span>):
243 <span class="py-src-comment"># This part is optional; it tells AuthOptionMixin what</span>
244 <span class="py-src-comment"># kinds of credential interfaces the user can give us.</span>
245 <span class="py-src-variable">supportedInterfaces</span> = (<span class="py-src-variable">credentials</span>.<span class="py-src-variable">IUsernamePassword</span>,)
247 <span class="py-src-variable">optParameters</span> = [
248 [<span class="py-src-string">"port"</span>, <span class="py-src-string">"p"</span>, <span class="py-src-number">1234</span>, <span class="py-src-string">"Server port number"</span>],
249 [<span class="py-src-string">"host"</span>, <span class="py-src-string">"h"</span>, <span class="py-src-string">"localhost"</span>, <span class="py-src-string">"Server hostname"</span>]]
251 <span class="py-src-keyword">class</span> <span class="py-src-identifier">MyServerServiceMaker</span>(<span class="py-src-parameter">object</span>):
252 <span class="py-src-variable">implements</span>(<span class="py-src-variable">IServiceMaker</span>, <span class="py-src-variable">IPlugin</span>)
253 <span class="py-src-variable">tapname</span> = <span class="py-src-string">"myserver"</span>
254 <span class="py-src-variable">description</span> = <span class="py-src-string">"This server does nothing productive."</span>
255 <span class="py-src-variable">options</span> = <span class="py-src-variable">ServerOptions</span>
257 <span class="py-src-keyword">def</span> <span class="py-src-identifier">makeService</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">options</span>):
258 <span class="py-src-string">"""Construct a service object."""</span>
259 <span class="py-src-comment"># The realm is a custom object that your server defines.</span>
260 <span class="py-src-variable">realm</span> = <span class="py-src-variable">myservice</span>.<span class="py-src-variable">MyServerRealm</span>(<span class="py-src-variable">options</span>[<span class="py-src-string">"host"</span>])
262 <span class="py-src-comment"># The portal is something Cred can provide, as long as</span>
263 <span class="py-src-comment"># you have a list of checkers that you'll support. This</span>
264 <span class="py-src-comment"># list is provided my AuthOptionMixin.</span>
265 <span class="py-src-variable">portal</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">Portal</span>(<span class="py-src-variable">realm</span>, <span class="py-src-variable">options</span>[<span class="py-src-string">"credCheckers"</span>])
267 <span class="py-src-comment"># OR, if you know you might get multiple interfaces, and</span>
268 <span class="py-src-comment"># only want to give your application one of them, you</span>
269 <span class="py-src-comment"># also have that option with AuthOptionMixin:</span>
270 <span class="py-src-variable">interface</span> = <span class="py-src-variable">credentials</span>.<span class="py-src-variable">IUsernamePassword</span>
271 <span class="py-src-variable">portal</span> = <span class="py-src-variable">portal</span>.<span class="py-src-variable">Portal</span>(<span class="py-src-variable">realm</span>, <span class="py-src-variable">options</span>[<span class="py-src-string">"credInterfaces"</span>][<span class="py-src-variable">interface</span>])
273 <span class="py-src-comment"># The protocol factory is, like the realm, something you implement.</span>
274 <span class="py-src-variable">factory</span> = <span class="py-src-variable">myservice</span>.<span class="py-src-variable">ServerFactory</span>(<span class="py-src-variable">realm</span>, <span class="py-src-variable">portal</span>)
276 <span class="py-src-comment"># Finally, return a service that will listen for connections.</span>
277 <span class="py-src-keyword">return</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">options</span>[<span class="py-src-string">"port"</span>]), <span class="py-src-variable">factory</span>)
280 <span class="py-src-comment"># As in our example above, we have to construct an object that</span>
281 <span class="py-src-comment"># provides the IPlugin and IServiceMaker interfaces.</span>
283 <span class="py-src-variable">serviceMaker</span> = <span class="py-src-variable">MyServerServiceMaker</span>()
287 Now that you have your TAP configured to support any authentication
288 we can throw at it, you're ready to use it. Here is an example of
289 starting your server using the /etc/passwd file for
290 authentication. (Clearly, this won't work on servers with shadow
294 <pre class="shell" xml:space="preserve">
295 $ twistd myserver --auth passwd:/etc/passwd
299 For a full list of cred plugins supported, see <code class="API"><a href="http://twistedmatrix.com/documents/12.1.0/api/twisted.plugins.html" title="twisted.plugins">twisted.plugins</a></code>, or use the command-line help:
302 <pre class="shell" xml:space="preserve">
303 $ twistd myserver --help-auth
304 $ twistd myserver --help-auth-type passwd
307 <h2>Conclusion<a name="auto4"/></h2>
309 <p>You should now be able to</p>
311 <li>Create a twistd plugin</li>
312 <li>Incorporate authentication into your plugin</li>
313 <li>Use it from your development environment</li>
314 <li>Install it correctly and use it in deployment</li>
320 <p><a href="index.html">Index</a></p>
321 <span class="version">Version: 12.1.0</span>