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/12.1.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/12.1.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/12.1.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/12.1.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/12.1.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
228 <p>You may want to support <code class="shell">yourproject/plugins/</code>
229 directories for ease of development. To do so, you should make <code class="shell">yourproject/plugins/__init__.py</code> contain at least
230 the following lines.</p>
232 <pre class="python"><p class="py-linenumber">1
235 </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>
236 <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>))
237 <span class="py-src-variable">__all__</span> = []
240 <p>The key behavior here is that interfaces are essentially paired
241 with a particular plugin package. If plugins are installed in a
242 different package than the one the code which relies on the
243 interface they provide, they will not be found when the
244 application goes to load them.</p>
246 <h2>Plugin Caching<a name="auto3"/></h2>
248 <p>In the course of using the Twisted plugin system, you may
249 notice <code class="shell">dropin.cache</code> files appearing at
250 various locations. These files are used to cache information
251 about what plugins are present in the directory which contains
252 them. At times, this cached information may become out of date.
253 Twisted uses the mtimes of various files involved in the plugin
254 system to determine when this cache may have become invalid.
255 Twisted will try to re-write the cache each time it tries to use
256 it but finds it out of date.</p>
258 <p>For a site-wide install, it may not (indeed, should not) be
259 possible for applications running as normal users to rewrite the
260 cache file. While these applications will still run and find
261 correct plugin information, they may run more slowly than they
262 would if the cache was up to date, and they may also report
263 exceptions if certain plugins have been removed but which the
264 cache still references. For these reasons, when installing or
265 removing software which provides Twisted plugins, the site
266 administrator should be sure the cache is regenerated.
267 Well-behaved package managers for such software should take this
268 task upon themselves, since it is trivially automatable. The
269 canonical way to regenerate the cache is to run the following
272 <pre class="python"><p class="py-linenumber">1
274 </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>
275 <span class="py-src-variable">list</span>(<span class="py-src-variable">getPlugins</span>(<span class="py-src-variable">IPlugin</span>))
278 <p>As mentioned, it is normal for exceptions to be raised
279 <strong>once</strong> here if plugins have been removed.</p>
281 <h2>Further Reading<a name="auto4"/></h2>
285 <li><a href="components.html" shape="rect">Components: Interfaces and Adapters</a></li>
291 <p><a href="index.html">Index</a></p>
292 <span class="version">Version: 12.1.0</span>