Differences between revisions 1 and 9 (spanning 8 versions)
Revision 1 as of 2009-04-18 09:30:43
Size: 2084
Editor: tarek
Comment: workin on it
Revision 9 as of 2009-04-20 14:37:52
Size: 2234
Editor: tarek
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
== example == = Example =

This is a generalization of the technique used in setuptools so people
can write plugins for commands.

Let's take an example: you have a command where you create a list of
files to build a file list (a manifest).

You provide a default system to build this list but you know some people
will probably provide other strategies to build that list.
Line 4: Line 13:
So let's declare a user option, called "manifest-makers", where an
ordered list of plugins name can be declared.

We also declare a new attribute called `extensible_options`, to declare
the list of options that are used to extend the command.
Line 5: Line 20:
        class MyCmd(sdist): class MyCmd(Command):
Line 7: Line 22:
            # this is a regular user option
            manifest_makers = ['svn', 'template', 'hg']
    user_options = [('manifest-makers', None,
                     'Plugins to build the manifest file')]
Line 10: Line 25:
            # this manages all extensions
            extensions = Extensible('manifest_makers')
    extensible_options = ['manifest-makers']
Line 13: Line 27:
            def run(self):
                # this will build the filelist by running the plugins
                self.extensions.run('manifest_makers')
    def initialize_options(self):
        # this is a regular user option
        self.manifest_makers = ['svn', 'template', 'hg']
        self.files = []
Line 17: Line 32:
    def finalize_options(self):
        pass
Line 18: Line 35:
        dist = Distribution()
        cmd = MyCmd(dist)
        cmd.ensure_finalized()
        cmd.run()
    def run(self):
        # this will build the filelist by running the plugins
        self.run_extension('manifest-makers')
Line 25: Line 41:
Notice that self.extensions.load(self) is implicitely called by Command. What happened ? In the initialize options, we declared default values for
the manifest_makers attribute : three plugins called 'svn', 'template' and 'hg'.
Line 27: Line 44:
== code == The Command will load these plugins using setuptools entry point called: "distutils.!MyCmd.manifest_makers".
It will load them at the end of the option finalization.
Line 29: Line 47:
Then, a new API called "run_extension" allows !MyCmd to run these plugins.

Each plugin receives the command and the name of the option in argument and is free
to work over the command and its distribution.

For example, the signature for the svn plugin is :
Line 30: Line 54:
"""Extendable command"""
import os
import pkg_resources
def svn(cmd, name):
    # work done here on the command
}}}
Line 34: Line 58:
class Extensible(object): Read more about how to create plugins with entry points, and what they are, here : http://lucumr.pocoo.org/2006/7/30/setuptools-plugins
Line 36: Line 60:
    def __init__(self, *options):
        self.options = options
        self._ext = {}
= Implementation =
Line 40: Line 62:
    def load(self, cmd):
        for opt in self.options:
            ep = ExtensionPoint(cmd, opt)
            ep.load()
            self._ext[opt] = ep

    def run(self, name):
        self._ext[name].run()

class ExtensionPoint(object):

    def __init__(self, cmd, name):
        self.name = name
        self.cmd = cmd
        self.entries = []

    def load(self, name=None):
        if name is None:
            name = self.cmd.get_command_name()
            entry_point = 'distutils.%s:%s' % (name, self.name)
        else:
            entry_point = name
        self._entries = [entry for entry in [self._load(ep) for ep in
                         pkg_resources.iter_entry_points(entry_point)]
                         if entry is not None]

    def _load(self, entry_point):
        values = getattr(self.cmd, self.name)
        if not isinstance((list, tuple), values):
            values = [values]
        entry_point = entry_point.load()
        if entry_point.name not in values:
            return None
        return entry_point

    def run(self):
        for ep in self._entries:
            entry_point(self.cmd, self.name)

    def apply(self, name=None):
        self.load(name)
        self.run()
}}}
 * http://svn.plone.org/svn/collective/collective.releaser/branches/refactor/collective/releaser/commands/extendable.py
 * http://svn.plone.org/svn/collective/collective.releaser/branches/refactor/collective/releaser/tests/test_extendable.py

Example

This is a generalization of the technique used in setuptools so people can write plugins for commands.

Let's take an example: you have a command where you create a list of files to build a file list (a manifest).

You provide a default system to build this list but you know some people will probably provide other strategies to build that list.

So let's declare a user option, called "manifest-makers", where an ordered list of plugins name can be declared.

We also declare a new attribute called extensible_options, to declare the list of options that are used to extend the command.

class MyCmd(Command):

    user_options = [('manifest-makers', None,
                     'Plugins to build the manifest file')]

    extensible_options = ['manifest-makers']

    def initialize_options(self):
        # this is a regular user option
        self.manifest_makers = ['svn', 'template', 'hg']
        self.files = []

    def finalize_options(self):
        pass

    def run(self):
        # this will build the filelist by running the plugins
        self.run_extension('manifest-makers')

What happened ? In the initialize options, we declared default values for the manifest_makers attribute : three plugins called 'svn', 'template' and 'hg'.

The Command will load these plugins using setuptools entry point called: "distutils.MyCmd.manifest_makers". It will load them at the end of the option finalization.

Then, a new API called "run_extension" allows MyCmd to run these plugins.

Each plugin receives the command and the name of the option in argument and is free to work over the command and its distribution.

For example, the signature for the svn plugin is :

def svn(cmd, name):
    # work done here on the command

Read more about how to create plugins with entry points, and what they are, here : http://lucumr.pocoo.org/2006/7/30/setuptools-plugins

Implementation

Distutils/PluginSystem (last edited 2009-04-22 09:16:36 by tarek)

Unable to edit the page? See the FrontPage for instructions.