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 Twisted Plugin System</title>
4
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
8
<h1 class="title">The Twisted Plugin System</h1>
9
<div class="toc"><ol><li><a href="#auto0">Writing Extensible Programs</a></li><li><a href="#auto1">Extending an Existing Program</a></li><li><a href="#auto2">Alternate Plugin Packages</a></li><li><a href="#auto3">Plugin Caching</a></li><li><a href="#auto4">Further Reading</a></li></ol></div>
13
<p>The purpose of this guide is to describe the preferred way to
14
write extensible Twisted applications (and consequently, also to
15
describe how to extend applications written in such a way). This
16
extensibility is achieved through the definition of one or more
17
APIs and a mechanism for collecting code plugins which
18
implement this API to provide some additional functionality.
19
At the base of this system is 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.</p>
21
<p>Making an application extensible using the plugin system has
22
several strong advantages over other techniques:</p>
25
<li>It allows third-party developers to easily enhance your
26
software in a way that is loosely coupled: only the plugin API
27
is required to remain stable.</li>
29
<li>It allows new plugins to be discovered flexibly. For
30
example, plugins can be loaded and saved when a program is first
31
run, or re-discovered each time the program starts up, or they
32
can be polled for repeatedly at runtime (allowing the discovery
33
of new plugins installed after the program has started).</li>
36
<h2>Writing Extensible Programs<a name="auto0"/></h2>
38
<p>Taking advantage of <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.html" title="twisted.plugin">twisted.plugin</a></code> is
39
a two step process:</p>
44
Define an interface which plugins will be required to implement.
45
This is done using the zope.interface package in the same way one
46
would define an interface for any other purpose.
50
A convention for defining interfaces is do so in a file named like
51
<em>ProjectName/projectname/iprojectname.py</em>. The rest of this
52
document will follow that convention: consider the following
53
interface definition be in <code>Matsim/matsim/imatsim.py</code>, an
54
interface definition module for a hypothetical material simulation
60
At one or more places in your program, invoke <code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.getPlugins.html" title="twisted.plugin.getPlugins">twisted.plugin.getPlugins</a></code> and iterate over its
66
As an example of the first step, consider the following interface
67
definition for a physical modelling system.
70
<pre class="python"><p class="py-linenumber"> 1
94
</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">Interface</span>, <span class="py-src-variable">Attribute</span>
96
<span class="py-src-keyword">class</span> <span class="py-src-identifier">IMaterial</span>(<span class="py-src-parameter">Interface</span>):
97
<span class="py-src-string">"""
98
An object with specific physical properties
99
"""</span>
100
<span class="py-src-keyword">def</span> <span class="py-src-identifier">yieldStress</span>(<span class="py-src-parameter">temperature</span>):
101
<span class="py-src-string">"""
102
Returns the pressure this material can support without
103
fracturing at the given temperature.
105
@type temperature: C{float}
106
@param temperature: Kelvins
110
"""</span>
112
<span class="py-src-variable">dielectricConstant</span> = <span class="py-src-variable">Attribute</span>(<span class="py-src-string">"""
113
@type dielectricConstant: C{complex}
114
@ivar dielectricConstant: The relative permittivity, with the
115
real part giving reflective surface properties and the
116
imaginary part giving the radio absorption coefficient.
117
"""</span>)
120
<p>In another module, we might have a function that operates on
121
objects providing the <code>IMaterial</code> interface:</p>
123
<pre class="python"><p class="py-linenumber">1
126
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">displayMaterial</span>(<span class="py-src-parameter">m</span>):
127
<span class="py-src-keyword">print</span> <span class="py-src-string">'A material with yield stress %s at 500 K'</span> % (<span class="py-src-variable">m</span>.<span class="py-src-variable">yieldStress</span>(<span class="py-src-number">500</span>),)
128
<span class="py-src-keyword">print</span> <span class="py-src-string">'Also a dielectric constant of %s.'</span> % (<span class="py-src-variable">m</span>.<span class="py-src-variable">dielectricConstant</span>,)
131
<p>The last piece of required code is that which collects
132
<code>IMaterial</code> providers and passes them to the
133
<code>displayMaterial</code> function.</p>
135
<pre class="python"><p class="py-linenumber">1
141
</p><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">getPlugins</span>
142
<span class="py-src-keyword">from</span> <span class="py-src-variable">matsim</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">imatsim</span>
144
<span class="py-src-keyword">def</span> <span class="py-src-identifier">displayAllKnownMaterials</span>():
145
<span class="py-src-keyword">for</span> <span class="py-src-variable">material</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">getPlugins</span>(<span class="py-src-variable">imatsim</span>.<span class="py-src-variable">IMaterial</span>):
146
<span class="py-src-variable">displayMaterial</span>(<span class="py-src-variable">material</span>)
149
<p>Third party developers may now contribute different materials
150
to be used by this modelling system by implementing one or more
151
plugins for the <code>IMaterial</code> interface.</p>
153
<h2>Extending an Existing Program<a name="auto1"/></h2>
155
<p>The above code demonstrates how an extensible program might be
156
written using Twisted's plugin system. How do we write plugins
157
for it, though? Essentially, we create objects which provide the
158
required interface and then make them available at a particular
159
location. Consider the following example.</p>
161
<pre class="python"><p class="py-linenumber"> 1
177
</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>
178
<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>
179
<span class="py-src-keyword">from</span> <span class="py-src-variable">matsim</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">imatsim</span>
181
<span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleMaterial</span>(<span class="py-src-parameter">object</span>):
182
<span class="py-src-variable">implements</span>(<span class="py-src-variable">IPlugin</span>, <span class="py-src-variable">imatsim</span>.<span class="py-src-variable">IMaterial</span>)
184
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">yieldStressFactor</span>, <span class="py-src-parameter">dielectricConstant</span>):
185
<span class="py-src-variable">self</span>.<span class="py-src-variable">_yieldStressFactor</span> = <span class="py-src-variable">yieldStressFactor</span>
186
<span class="py-src-variable">self</span>.<span class="py-src-variable">dielectricConstant</span> = <span class="py-src-variable">dielectricConstant</span>
188
<span class="py-src-keyword">def</span> <span class="py-src-identifier">yieldStress</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">temperature</span>):
189
<span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">_yieldStressFactor</span> * <span class="py-src-variable">temperature</span>
191
<span class="py-src-variable">steelPlate</span> = <span class="py-src-variable">SimpleMaterial</span>(<span class="py-src-number">2.06842719e11</span>, <span class="py-src-number">2.7</span> + <span class="py-src-number">0.2j</span>)
192
<span class="py-src-variable">brassPlate</span> = <span class="py-src-variable">SimpleMaterial</span>(<span class="py-src-number">1.03421359e11</span>, <span class="py-src-number">1.4</span> + <span class="py-src-number">0.5j</span>)
195
<p><code>steelPlate</code> and <code>brassPlate</code> now provide both
196
<code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.IPlugin.html" title="twisted.plugin.IPlugin">IPlugin</a></code> and <code>IMaterial</code>.
197
All that remains is to make this module available at an appropriate
198
location. For this, there are two options. The first of these is
199
primarily useful during development: if a directory which
200
has been added to <code>sys.path</code> (typically by adding it to the
201
<code class="shell">PYTHONPATH</code> environment variable) contains a
202
<em>directory</em> named <code class="shell">twisted/plugins/</code>,
203
each <code class="shell">.py</code> file in that directory will be loaded
204
as a source of plugins. This directory <em>must not</em> be a Python
205
package: including <code class="shell">__init__.py</code> will cause the
206
directory to be skipped and no plugins loaded from it. Second, each
207
module in the installed version of Twisted's <code class="shell">
208
twisted.plugins</code> package will also be loaded as a source of
211
<p>Once this plugin is installed in one of these two ways,
212
<code>displayAllKnownMaterials</code> can be run and we will see
213
two pairs of output: one for a steel plate and one for a brass
216
<h2>Alternate Plugin Packages<a name="auto2"/></h2>
218
<p><code class="API"><a href="http://twistedmatrix.com/documents/10.0.0/api/twisted.plugin.getPlugins.html" title="twisted.plugin.getPlugins">getPlugins</a></code> takes one
219
additional argument not mentioned above. If passed in, the 2nd argument
220
should be a module or package to be used instead of
221
<code>twisted.plugins</code> as the plugin meta-package. If you
222
are writing a plugin for a Twisted interface, you should never
223
need to pass this argument. However, if you have developed an
224
interface of your own, you may want to mandate that plugins for it
225
are installed in your own plugins package, rather than in
226
Twisted's. In this case, you probably also want to support <code class="shell">yourproject/plugins/</code> directories for ease of
227
development. To do so, you should make the <code class="shell">__init__.py</code> for that package contain at least
228
the following lines.</p>
230
<pre class="python"><p class="py-linenumber">1
233
</p><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">pluginPackagePaths</span>
234
<span class="py-src-variable">__path__</span>.<span class="py-src-variable">extend</span>(<span class="py-src-variable">pluginPackagePaths</span>(<span class="py-src-variable">__name__</span>))
235
<span class="py-src-variable">__all__</span> = []
238
<p>The key behavior here is that interfaces are essentially paired
239
with a particular plugin package. If plugins are installed in a
240
different package than the one the code which relies on the
241
interface they provide, they will not be found when the
242
application goes to load them.</p>
244
<h2>Plugin Caching<a name="auto3"/></h2>
246
<p>In the course of using the Twisted plugin system, you may
247
notice <code class="shell">dropin.cache</code> files appearing at
248
various locations. These files are used to cache information
249
about what plugins are present in the directory which contains
250
them. At times, this cached information may become out of date.
251
Twisted uses the mtimes of various files involved in the plugin
252
system to determine when this cache may have become invalid.
253
Twisted will try to re-write the cache each time it tries to use
254
it but finds it out of date.</p>
256
<p>For a site-wide install, it may not (indeed, should not) be
257
possible for applications running as normal users to rewrite the
258
cache file. While these applications will still run and find
259
correct plugin information, they may run more slowly than they
260
would if the cache was up to date, and they may also report
261
exceptions if certain plugins have been removed but which the
262
cache still references. For these reasons, when installing or
263
removing software which provides Twisted plugins, the site
264
administrator should be sure the cache is regenerated.
265
Well-behaved package managers for such software should take this
266
task upon themselves, since it is trivially automatable. The
267
canonical way to regenerate the cache is to run the following
270
<pre class="python"><p class="py-linenumber">1
272
</p><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-variable">getPlugins</span>
273
<span class="py-src-variable">list</span>(<span class="py-src-variable">getPlugins</span>(<span class="py-src-variable">IPlugin</span>))
276
<p>As mentioned, it is normal for exceptions to be raised
277
<strong>once</strong> here if plugins have been removed.</p>
279
<h2>Further Reading<a name="auto4"/></h2>
283
<li><a href="components.html" shape="rect">Components: Interfaces and Adapters</a></li>
289
<p><a href="index.html">Index</a></p>
290
<span class="version">Version: 10.0.0</span>
b'\\ No newline at end of file'