~certify-web-dev/twisted/certify-trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
<?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">
  <head>
<title>Twisted Documentation: Writing a twistd Plugin</title>
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
  </head>

  <body bgcolor="white">
    <h1 class="title">Writing a twistd Plugin</h1>
    <div class="toc"><ol><li><a href="#auto0">Goals</a></li><li><a href="#auto1">A note on .tap files</a></li><li><a href="#auto2">Alternatives to twistd plugins</a></li><li><a href="#auto3">Creating the plugin</a></li><li><a href="#auto4">Using cred with your TAP</a></li><li><a href="#auto5">Conclusion</a></li></ol></div>
    <div class="content">
<span/>

<p>This document describes adding subcommands to
the <code>twistd</code> command, as a way to facilitate the deployment
of your applications. <em>(This feature was added in Twisted 2.5)</em></p>

<p>The target audience of this document are those that have developed
a Twisted application which needs a command line-based deployment
mechanism.</p>

<p>There are a few prerequisites to understanding this document:</p>
<ul>
  <li>A basic understanding of the Twisted Plugin System (i.e.,
      the <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.html" title="twisted.plugin">twisted.plugin</a></code> module) is
      necessary, however, step-by-step instructions will be
      given. Reading <a href="plugin.html" shape="rect">The Twisted Plugin
      System</a> is recommended, in particular the <q>Extending an
      Existing Program</q> section.</li>
  <li>The <a href="application.html" shape="rect">Application</a> infrastructure
    is used in <code>twistd</code> plugins; in particular, you should
    know how to expose your program's functionality as a Service.</li>
  <li>In order to parse command line arguments, the <code>twistd</code> plugin
    mechanism relies
    on <code>twisted.python.usage</code>, which is documented
    in <a href="options.html" shape="rect">Using usage.Options</a>.</li>
</ul>

<h2>Goals<a name="auto0"/></h2>

<p>After reading this document, the reader should be able to expose
their Service-using application as a subcommand
of <code>twistd</code>, taking into consideration whatever was passed
on the command line.</p>

<h2>A note on .tap files<a name="auto1"/></h2>

<p>Readers may be confused about a historical file type associated
with Twisted, the <code>.tap</code> file. This was a kind of file that
was generated by a program named <code>mktap</code> and
which <code>twistd</code> can read. <code>.tap</code> files are
deprecated; this document has nothing to do with them, although the
technology described herein is very closely related to the old
system. Existing plugins that were written for the mktap system are
compatible with this <code>twistd</code> plugin system; the following
commands,
</p>

<pre class="shell" xml:space="preserve">
$ mktap [foo] [options...]
$ twistd -n -f [foo].tap
</pre>

<p>
are equivalent to the command:</p>

<pre class="shell" xml:space="preserve">$ twistd -n [foo] [options...]</pre>

<h2>Alternatives to twistd plugins<a name="auto2"/></h2>
<p>The major alternative to the twistd plugin mechanism is the <code>.tac</code>
file, which is a simple script to be used with the
twistd <code>-y/--python</code> parameter. The twistd plugin mechanism
exists to offer a more extensible command-line-driven interface to
your application. For more information on <code>.tac</code> files, see
the document <a href="application.html" shape="rect">Using the Twisted Application
Framework</a>.</p>


<h2>Creating the plugin<a name="auto3"/></h2>

<p>The following directory structure is assumed of your project:</p>

<ul>
  <li><strong>MyProject</strong> - Top level directory
    <ul>
      <li><strong>myproject</strong> - Python package
        <ul><li><strong>__init__.py</strong></li></ul>
      </li>
    </ul>
  </li>
</ul>

<p>
  During development of your project, Twisted plugins can be loaded
  from a special directory in your project, assuming your top level
  directory ends up in sys.path. Create a directory
  named <code>twisted</code> containing a directory
  named <code>plugins</code>, and add a file
  named <code>myproject_plugin.py</code> to it. This file will contain your
  plugin. Note that you should <em>not</em> add any __init__.py files
  to this directory structure, and the plugin file should <em>not</em>
  be named <code>myproject.py</code> (because that would conflict with
  your project's module name).
</p>

<p>
  In this file, define an object which <em>provides</em> the interfaces
  <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.IPlugin.html" title="twisted.plugin.IPlugin">twisted.plugin.IPlugin</a></code>
  and <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.application.service.IServiceMaker.html" title="twisted.application.service.IServiceMaker">twisted.application.service.IServiceMaker</a></code>.
</p>

<p>The <code>tapname</code> attribute of your IServiceMaker provider
will be used as the subcommand name in a command
like <code class="shell">twistd [subcommand] [args...]</code>, and
the <code>options</code> attribute (which should be
a <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.python.usage.Options.html" title="twisted.python.usage.Options">usage.Options</a></code>
subclass) will be used to parse the given args.</p>

<pre class="python"><p class="py-linenumber"> 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
</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>

<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>
<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>
<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>
<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-keyword">from</span> <span class="py-src-variable">myproject</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">MyFactory</span>


<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>):
    <span class="py-src-variable">optParameters</span> = [[<span class="py-src-string">&quot;port&quot;</span>, <span class="py-src-string">&quot;p&quot;</span>, <span class="py-src-number">1235</span>, <span class="py-src-string">&quot;The port number to listen on.&quot;</span>]]


<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyServiceMaker</span>(<span class="py-src-parameter">object</span>):
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">IServiceMaker</span>, <span class="py-src-variable">IPlugin</span>)
    <span class="py-src-variable">tapname</span> = <span class="py-src-string">&quot;myproject&quot;</span>
    <span class="py-src-variable">description</span> = <span class="py-src-string">&quot;Run this! It'll make your dog happy.&quot;</span>
    <span class="py-src-variable">options</span> = <span class="py-src-variable">Options</span>

    <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>):
        <span class="py-src-string">&quot;&quot;&quot;
        Construct a TCPServer from a factory defined in myproject.
        &quot;&quot;&quot;</span>
        <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">&quot;port&quot;</span>]), <span class="py-src-variable">MyFactory</span>())


<span class="py-src-comment"># Now construct an object which *provides* the relevant interfaces</span>
<span class="py-src-comment"># The name of this variable is irrelevant, as long as there is *some*</span>
<span class="py-src-comment"># name bound to a provider of IPlugin and IServiceMaker.</span>

<span class="py-src-variable">serviceMaker</span> = <span class="py-src-variable">MyServiceMaker</span>()
</pre>

<p>
  Now running <code class="shell">twistd --help</code> should
  print <code>myproject</code> in the list of available subcommands,
  followed by the description that we specified in the
  plugin. <code class="shell">twistd -n myproject</code> would,
  assuming we defined a <code>MyFactory</code> factory
  inside <code>myproject</code>, start a listening server on port 1235
  with that factory.
</p>

<h2>Using cred with your TAP<a name="auto4"/></h2>

<p>
  Twisted ships with a robust authentication framework to use with
  your application. If your server needs authentication functionality,
  and you haven't read about <a href="cred.html" shape="rect">twisted.cred</a>
  yet, read up on it first.
</p>

<p>
  If you are building a twistd plugin and you want to support a wide
  variety of authentication patterns, Twisted provides an easy-to-use
  mixin for your Options subclass:
  <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.cred.strcred.AuthOptionMixin.html" title="twisted.cred.strcred.AuthOptionMixin">strcred.AuthOptionMixin</a></code>.
  The following code is an example of using this mixin:
</p>

<pre class="python"><p class="py-linenumber"> 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
</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>
<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>
<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>
<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>
<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>

<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>):
    <span class="py-src-comment"># This part is optional; it tells AuthOptionMixin what</span>
    <span class="py-src-comment"># kinds of credential interfaces the user can give us.</span>
    <span class="py-src-variable">supportedInterfaces</span> = (<span class="py-src-variable">credentials</span>.<span class="py-src-variable">IUsernamePassword</span>,)

    <span class="py-src-variable">optParameters</span> = [
        [<span class="py-src-string">&quot;port&quot;</span>, <span class="py-src-string">&quot;p&quot;</span>, <span class="py-src-number">1234</span>, <span class="py-src-string">&quot;Server port number&quot;</span>],
        [<span class="py-src-string">&quot;host&quot;</span>, <span class="py-src-string">&quot;h&quot;</span>, <span class="py-src-string">&quot;localhost&quot;</span>, <span class="py-src-string">&quot;Server hostname&quot;</span>]]

<span class="py-src-keyword">class</span> <span class="py-src-identifier">MyServerServiceMaker</span>(<span class="py-src-parameter">object</span>):
    <span class="py-src-variable">implements</span>(<span class="py-src-variable">IServiceMaker</span>, <span class="py-src-variable">IPlugin</span>)
    <span class="py-src-variable">tapname</span> = <span class="py-src-string">&quot;myserver&quot;</span>
    <span class="py-src-variable">description</span> = <span class="py-src-string">&quot;This server does nothing productive.&quot;</span>
    <span class="py-src-variable">options</span> = <span class="py-src-variable">ServerOptions</span>

    <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>):
        <span class="py-src-string">&quot;&quot;&quot;Construct a service object.&quot;&quot;&quot;</span>
        <span class="py-src-comment"># The realm is a custom object that your server defines.</span>
        <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">&quot;host&quot;</span>])

        <span class="py-src-comment"># The portal is something Cred can provide, as long as</span>
        <span class="py-src-comment"># you have a list of checkers that you'll support. This</span>
        <span class="py-src-comment"># list is provided my AuthOptionMixin.</span>
        <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">&quot;credCheckers&quot;</span>])

        <span class="py-src-comment"># OR, if you know you might get multiple interfaces, and</span>
        <span class="py-src-comment"># only want to give your application one of them, you</span>
        <span class="py-src-comment"># also have that option with AuthOptionMixin:</span>
        <span class="py-src-variable">interface</span> = <span class="py-src-variable">credentials</span>.<span class="py-src-variable">IUsernamePassword</span>
        <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">&quot;credInterfaces&quot;</span>][<span class="py-src-variable">interface</span>])

        <span class="py-src-comment"># The protocol factory is, like the realm, something you implement.</span>
        <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>)

        <span class="py-src-comment"># Finally, return a service that will listen for connections.</span>
        <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">&quot;port&quot;</span>]), <span class="py-src-variable">factory</span>)


<span class="py-src-comment"># As in our example above, we have to construct an object that</span>
<span class="py-src-comment"># provides the IPlugin and IServiceMaker interfaces.</span>

<span class="py-src-variable">serviceMaker</span> = <span class="py-src-variable">MyServerServiceMaker</span>()
</pre>

<p>
  Now that you have your TAP configured to support any authentication
  we can throw at it, you're ready to use it. Here is an example of
  starting your server using the /etc/passwd file for
  authentication. (Clearly, this won't work on servers with shadow
  passwords.)
</p>

<pre class="shell" xml:space="preserve">
$ twistd myserver --auth passwd:/etc/passwd
</pre>

<p>
  For a full list of cred plugins supported, see <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugins.html" title="twisted.plugins">twisted.plugins</a></code>, or use the command-line help:
</p>

<pre class="shell" xml:space="preserve">
$ twistd myserver --help-auth
$ twistd myserver --help-auth-type passwd
</pre>

<h2>Conclusion<a name="auto5"/></h2>

<p>You should now be able to</p>
<ul>
  <li>Create a twistd plugin</li>
  <li>Incorporate authentication into your plugin</li>
  <li>Use it from your development environment</li>
  <li>Install it correctly and use it in deployment</li>
</ul>


</div>

    <p><a href="index.html">Index</a></p>
    <span class="version">Version: 10.0.0</span>
  </body>
</html>