Initial import to Tizen
[profile/ivi/python-twisted.git] / doc / core / howto / plugin.html
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">
2   <head>
3 <title>Twisted Documentation: The Twisted Plugin System</title>
4 <link href="stylesheet.css" rel="stylesheet" type="text/css"/>
5   </head>
6
7   <body bgcolor="white">
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>
10     <div class="content">
11     <span/>
12
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>
20
21     <p>Making an application extensible using the plugin system has
22     several strong advantages over other techniques:</p>
23
24     <ul>
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>
28
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>
34     </ul>
35
36     <h2>Writing Extensible Programs<a name="auto0"/></h2>
37
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>
40
41     <ol>
42       <li>
43         <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.
47         </p>
48
49         <p>
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
55         package.
56         </p>
57       </li>
58
59       <li>
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
61       result.
62       </li>
63     </ol>
64
65     <p>
66     As an example of the first step, consider the following interface
67     definition for a physical modelling system.
68     </p>
69
70     <pre class="python"><p class="py-linenumber"> 1
71  2
72  3
73  4
74  5
75  6
76  7
77  8
78  9
79 10
80 11
81 12
82 13
83 14
84 15
85 16
86 17
87 18
88 19
89 20
90 21
91 22
92 23
93 24
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>
95
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">&quot;&quot;&quot;
98     An object with specific physical properties
99     &quot;&quot;&quot;</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">&quot;&quot;&quot;
102         Returns the pressure this material can support without
103         fracturing at the given temperature.
104
105         @type temperature: C{float}
106         @param temperature: Kelvins
107
108         @rtype: C{float}
109         @return: Pascals
110         &quot;&quot;&quot;</span>
111
112     <span class="py-src-variable">dielectricConstant</span> = <span class="py-src-variable">Attribute</span>(<span class="py-src-string">&quot;&quot;&quot;
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         &quot;&quot;&quot;</span>)
118 </pre>
119
120     <p>In another module, we might have a function that operates on
121     objects providing the <code>IMaterial</code> interface:</p>
122
123     <pre class="python"><p class="py-linenumber">1
124 2
125 3
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>,)
129 </pre>
130
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>
134
135     <pre class="python"><p class="py-linenumber">1
136 2
137 3
138 4
139 5
140 6
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>
143
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>)
147 </pre>
148
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>
152
153     <h2>Extending an Existing Program<a name="auto1"/></h2>
154
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>
160
161     <pre class="python"><p class="py-linenumber"> 1
162  2
163  3
164  4
165  5
166  6
167  7
168  8
169  9
170 10
171 11
172 12
173 13
174 14
175 15
176 16
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>
180
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>)
183
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>
187
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>
190
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>)
193 </pre>
194
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
209     plugins.</p>
210
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
214     plate.</p>
215
216     <h2>Alternate Plugin Packages<a name="auto2"/></h2>
217
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
226     Twisted's.</p>
227
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>
231
232     <pre class="python"><p class="py-linenumber">1
233 2
234 3
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> = []
238 </pre>
239
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>
245
246     <h2>Plugin Caching<a name="auto3"/></h2>
247
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>
257
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
270     Python code:</p>
271
272     <pre class="python"><p class="py-linenumber">1
273 2
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>))
276 </pre>
277
278     <p>As mentioned, it is normal for exceptions to be raised
279     <strong>once</strong> here if plugins have been removed.</p>
280
281     <h2>Further Reading<a name="auto4"/></h2>
282
283     <ul>
284
285       <li><a href="components.html" shape="rect">Components: Interfaces and Adapters</a></li>
286
287     </ul>
288
289   </div>
290
291     <p><a href="index.html">Index</a></p>
292     <span class="version">Version: 12.1.0</span>
293   </body>
294 </html>