Sophie

Sophie

distrib > Mandriva > 2008.1 > x86_64 > media > main-release > by-pkgid > db93d7191b12a3d5ce887da46fc79bf1 > files > 191

python-twisted-core-doc-2.5.0-3mdv2008.1.x86_64.rpm

<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: The Twisted Plugin System</title><link href="../howto/stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">The Twisted Plugin System</h1><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><div class="content"><span></span><p>The purpose of this guide is to describe the preferred way to
    write extensible Twisted applications (and consequently, also to
    describe how to extend applications written in such a way).  This
    extensibility is achieved through the definition of one or more
    APIs and a mechanism for collecting code plugins which
    implementation this API to provide some additional functionality.
    At the base of this system is the <code class="API">twisted.plugin</code> module.</p><p>Making an application extensible using the plugin system has
    several strong advantages over other techniques:</p><ul><li>It allows third-party developers to easily enhance your
      software in a way that is loosely coupled: only the plugin API
      is required to remain stable.</li><li>It allows new plugins to be discovered flexibly.  For
      example, plugins can be loaded and saved when a program is first
      run, or re-discovered each time the program starts up, or they
      can be polled for repeatedly at runtime (allowing the discovery
      of new plugins installed after the program has started).</li></ul><h2>Writing Extensible Programs<a name="auto0"></a></h2><p>Taking advantage of <code class="API">twisted.plugin</code> is
    a two step process:</p><ol><li><p>
        Define an interface which plugins will be required to implement. 
        This is done using the <code class="API">zope.interface</code>
        package in the same way one would define an interface for any other
        purpose.
        </p><p>
        A convention for defining interfaces is do so in a file named like
        <em>ProjectName/projectname/iprojectname.py</em>.  The rest of this
        document will follow that convention: consider the following
        interface definition be in <code>Matsim/matsim/imatsim.py</code>, an
        interface definition module for a hypothetical material simulation
        package.
        </p></li><li>
      At one or more places in your program, invoke <code class="API">twisted.plugin.getPlugins</code> and iterate over its
      result.
      </li></ol><p>
    As an example of the first step, consider the following interface
    definition for a physical modelling system.
    </p><pre class="python">
<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>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">IMaterial</span>(<span class="py-src-parameter">Interface</span>):
    <span class="py-src-string">&quot;&quot;&quot;
    An object with specific physical properties
    &quot;&quot;&quot;</span>
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">yieldStress</span>(<span class="py-src-parameter">temperature</span>):
        <span class="py-src-string">&quot;&quot;&quot;
        Returns the pressure this material can support without
        fracturing at the given temperature.

	@type temperature: C{float}
	@param temperature: Kelvins

	@rtype: C{float}
	@return: Pascals
        &quot;&quot;&quot;</span>

    <span class="py-src-variable">dielectricConstant</span> = <span class="py-src-variable">Attribute</span>(<span class="py-src-string">&quot;&quot;&quot;
        @type dielectricConstant: C{complex}
        @ivar dielectricConstant: The relative permittivity, with the
        real part giving reflective surface properties and the
        imaginary part giving the radio absorption coefficient.
        &quot;&quot;&quot;</span>)
</pre><p>In another module, we might have a function that operates on
    objects providing the <code>IMaterial</code> interface:</p><pre class="python">
<span class="py-src-keyword">def</span> <span class="py-src-identifier">displayMaterial</span>(<span class="py-src-parameter">m</span>):
    <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>),)
    <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>,)
</pre><p>The last piece of required code is that which collects
    <code>IMaterial</code> providers and passes them to the
    <code>displayMaterial</code> function.</p><pre class="python">
<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>
<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>

<span class="py-src-keyword">def</span> <span class="py-src-identifier">displayAllKnownMaterials</span>():
    <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>):
        <span class="py-src-variable">displayMaterial</span>(<span class="py-src-variable">material</span>)
</pre><p>Third party developers may now contribute different materials
    to be used by this modelling system by implementing one or more
    plugins for the <code>IMaterial</code> interface.</p><h2>Extending an Existing Program<a name="auto1"></a></h2><p>The above code demonstrates how an extensible program might be
    written using Twisted's plugin system.  How do we write plugins
    for it, though?  Essentially, we create objects which provide the
    required interface and then make them available at a particular
    location.  Consider the following example.</p><pre class="python">
<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">matsim</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">imatsim</span>

<span class="py-src-keyword">class</span> <span class="py-src-identifier">SimpleMaterial</span>(<span class="py-src-parameter">object</span>):
    <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>)

    <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>):
        <span class="py-src-variable">self</span>.<span class="py-src-variable">_yieldStressFactor</span> = <span class="py-src-variable">yieldStressFactor</span>
        <span class="py-src-variable">self</span>.<span class="py-src-variable">dielectricConstant</span> = <span class="py-src-variable">dielectricConstant</span>

    <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>):
        <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>

<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>)
<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>)
</pre><p><code>steelPlate</code> and <code>brassPlate</code> now provide
    both <code class="API">IPlugin</code> and <code>IMaterial</code>.
    All that remains is to make this module available at an
    appropriate location.  For this, there are two options.  The first
    of these is primarily useful during development: if a directory
    which has been added to <code>sys.path</code> (typically by adding
    it to the <code class="shell">PYTHONPATH</code> environment
    variable) contains a <em>directory</em> (not a Python package)
    named <code class="shell">twisted/plugins/</code>, each <code class="shell">.py</code> file in that directory will be loaded as
    a source of plugins.  Second, each module in the installed version
    of Twisted's <code class="shell">twisted.plugins</code> package
    will also be loaded as a source of plugins.</p><p>Once this plugin is installed in one of these two ways,
    <code>displayAllKnownMaterials</code> can be run and we will see
    two pairs of output: one for a steel plate and one for a brass
    plate.</p><h2>Alternate Plugin Packages<a name="auto2"></a></h2><p><code class="API">getPlugins</code> takes one additional
    argument not mentioned above.  If passed in, the 2nd argument
    should be a module or package to be used instead of
    <code>twisted.plugins</code> as the plugin meta-package.  If you
    are writing a plugin for a Twisted interface, you should never
    need to pass this argument.  However, if you have developed an
    interface of your own, you may want to mandate that plugins for it
    are installed in your own plugins package, rather than in
    Twisted's.  In this case, you probably also want to support <code class="shell">yourproject/plugins/</code> directories for ease of
    development.  To do so, you should make the <code class="shell">__init__.py</code> for that package contain at least
    the following lines.</p><pre class="python">
<span class="py-src-keyword">import</span> <span class="py-src-variable">os</span>, <span class="py-src-variable">sys</span>
<span class="py-src-variable">__path__</span> = [<span class="py-src-variable">os</span>.<span class="py-src-variable">path</span>.<span class="py-src-variable">abspath</span>(<span class="py-src-variable">os</span>.<span class="py-src-variable">path</span>.<span class="py-src-variable">join</span>(<span class="py-src-variable">x</span>, <span class="py-src-string">'yourproject'</span>, <span class="py-src-string">'plugins'</span>)) 
            <span class="py-src-keyword">for</span> <span class="py-src-variable">x</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">sys</span>.<span class="py-src-variable">path</span>]

<span class="py-src-variable">__all__</span> = []
</pre><p>The key behavior here is that interfaces are essentially paired
    with a particular plugin package.  If plugins are installed in a
    different package than the one the code which relies on the
    interface they provide, they will not be found when the
    application goes to load them.</p><h2>Plugin Caching<a name="auto3"></a></h2><p>In the course of using the Twisted plugin system, you may
    notice <code class="shell">dropin.cache</code> files appearing at
    various locations.  These files are used to cache information
    about what plugins are present in the directory which contains
    them.  At times, this cached information may become out of date.
    Twisted uses the mtimes of various files involved in the plugin
    system to determine when this cache may have become invalid.
    Twisted will try to re-write the cache each time it tries to use
    it but finds it out of date.</p><p>For a site-wide install, it may not (indeed, should not) be
    possible for applications running as normal users to rewrite the
    cache file.  While these applications will still run and find
    correct plugin information, they may run more slowly than they
    would if the cache was up to date, and they may also report
    exceptions if certain plugins have been removed but which the
    cache still references.  For these reasons, when installing or
    removing software which provides Twisted plugins, the site
    administrator should be sure the cache is regenerated.
    Well-behaved package managers for such software should take this
    task upon themselves, since it is trivially automatable.  The
    canonical way to regenerate the cache is to run the following
    Python code:</p><pre class="python">
<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">getPlugin</span>
<span class="py-src-variable">list</span>(<span class="py-src-variable">getPlugin</span>(<span class="py-src-variable">IPlugin</span>))
</pre><p>As mentioned, it is normal for exceptions to be raised
    <strong>once</strong> here if plugins have been removed.</p><h2>Further Reading<a name="auto4"></a></h2><ul><li><a href="components.html">Components: Interfaces and Adapters</a></li></ul></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 2.5.0</span></body></html>