Plugins

This module provides the architecture for creating and handling xdress plugins.

author:Anthony Scopatz <scopatz@gmail.com>

The purpose of xdress is to be as modular and extensible as possible, allowing for developers to build and execute their own tools as needed. As such, xdress has a very nimble plugin interface that easily handles run control, adding arguments to the command line interface, setting up & validating the run control, command execution, and teardown. In fact, the entire xdress execution is based on this plugin architecture. You can be certain that this is well supported feature and not some hack’d add on.

Writing Plugins

Writing plugins is easy! You simply need to have a variable named XDressPlugin in a module. Say your module is called mymod and lives in a package mypack, then xdress would know this plugin by the name "mypack.mymod". This is exactly the same string that you would use to do an absolute import of mymod.

To expose this plugin to an xdress execution, either add it to the plugins variable in your xdressrc.py file:

from xdress.utils import DEFAULT_PLUGINS
plugins = list(DEFAULT_PLUGINS) + ['mypack.mymod']

Or you can add it on the command line:

~ $ xdress --plugins xdress.stlwrap xdress.autoall xdress.cythongen mypack.mymod

Note that in both of the above cases we retain normal functionality by including the default plugins that come with xdress.

The XDressPlugin variable must be callable with no arguments and return a variable with certain attributes. Normally this is done as a class but through the magic of duck typing it doesn’t have to be. The Plugin class is provided as a base class which implements a minimal, zero-work interface. This is useful for inheriting your modules plugin from. You need only override the attributes you want. Again, inheriting from Plugin is suggested but not required.

Interface

requires:

This is a list of module names or a function that returns such a list. The names in this list will be loaded and executed in order prior to this plugin. If multiple plugins require the same upstream plugin, the upstream on will only be run once.

defaultrc:

This is a dictionary or run control instance that maps run control parameters to their default values if they are otherwise not specified. To make a parameter have to be given by the user, set the value to the singleton xdress.utils.NotSpecified. Parameters with the same name in different plugins will clobber each other, with the last plugin’s value being ultimately assigned. The exception to this is if a later plugin’s parameter value is NotSpecified then the previous plugin value will be retained. See the RunControl class for more details. Generally it is not advised for two plugins to share run control parameter names unless you really know what you are doing.

rcupdaters:

This may be a dict, another mapping, a function which returns a mapping. The keys are the string names of the run control parameters. The values are callables which indicate how to update or merge two rc parameters with this key. The callable should take two instances and return a copy that represents the merger, e.g. lambda old, new: old + new. One useful example is for paths. Normally you want new paths to prepend old ones:

rcupdaters = {'includes': lambda old, new: list(new) + list(old)}

If a callable is not supplied for an rc parameter then the the default behaviour is to simply override the old value with the new one.

rcdocs:

This may be a dict, another mapping, a function which returns a mapping. The keys are the string names of the run control parameters. The values are docstrings for the rc parameters.

update_argparser(parser):
 

This is method that takes an argparse.ArgumentParser() instance and modifies it in-place. This allows for run control parameters to be exposed as command line arguments and options. Default arguments in parser.add_argument() values should not be given, or should only be set to Not Specified. This is to prevent collisions with the run controller. Default values should instead be given in the plugin’s defaultrc. Thus argument names or the dest keyword argument should match the keys in defaultrc.

setup(rc):

Performs all setup tasks needed for this plugin. This may include validation and munging of the run control object (rc) as well as creating directories and files in the OS environment. If needed, the rc should be modified in-place so that changes propagate to other plugins and further calls on this plugin. This should return None.

execute(rc):

Performs the heavy lifting of the plugin, which may require a run controller.If needed, the rc should be modified in-place so that changes propagate to other plugins and further calls on this plugin. This should return None.

teardown(rc):

Performs any cleanup tasks needed by the plugin, including removing temporary files. If needed, the rc should be modified in-place so that changes propagate to other plugins and further calls on this plugin. This should return None.

report_debug(rc):
 

Generates and returns a message to report in the debug.txt file in the event that execute() fails and additional debugging information is requested. This message is a string.

Example

Here is simple, if morbid, plugin example:

from xdress.plugins import Plugin

class XDressPlugin(Plugin):
    '''Which famous person was executed?'''

    # everything should require base, it is useful!
    requires = ('xdress.base',)

    defaultrc = {
        'choices': ['J. Edgar Hoover', 'Hua Mulan', 'Leslie'],
        'answer': 'Joan of Arc',
        }

    rcupdaters = {'choices': lambda old, new: list(new) + list(old)}

    rcdocs = {'choices': "Possible answers.",
              'answer': "The correct answer"}

    def update_argparser(self, parser):
        # Note, no 'default=' keyword arguments are given
        parser.add_argument('-c', '--choices', action='store', dest='choices',
                            nargs="+", help="famous people chocies")
        parser.add_argument('-a', '--answer', action='store', dest='answer',
                            help="person who was executed")

    def setup(self, rc):
        '''Ensures that Joan of Arc is a choice.'''
        if 'Joan of Arc' not in rc.choices:
            rc.choices.append('Joan of Arc')

    def execute(self, rc):
        '''Kills Joan...'''
        if rc.answer == 'Joan of Arc':
            print('Joan has met an untimely demise!')
        else:
            raise ValueError('Joan of Arc was executed, not ' + rc.answer)

    def report_debug(self, rc):
        return "the possible choices were " + str(rc.choices)

Plugins API

class xdress.plugins.Plugin[source]

A base plugin for other xdress pluigins to inherit.

The __init__() method may take no arguments or keyword arguments.

execute(rc)[source]

Performs the actual work of the plugin, which may require a run controller.

Parameters:rc : xdress.utils.RunControl
report_debug(rc)[source]

A message to report in the event that execute() fails and additional debugging information is requested.

Parameters:

rc : xdress.utils.RunControl

Returns:

message : str or None

A debugging message to report. If None is returned, this plugin is skipped in the debug output.

setup(rc)[source]

Performs all setup tasks needed for this plugin. This may include validation and munging of the run control object as well as creating the portions of the OS environment.

Parameters:rc : xdress.utils.RunControl
teardown(rc)[source]

Performs any cleanup tasks needed by the plugin.

Parameters:rc : xdress.utils.RunControl
update_argparser(parser)[source]

This method takes an argparse.ArgumentParser() instance and modifies it in-place. This allows for run control parameters to be modified from as command line arguments.

Parameters:

parser : argparse.ArgumentParser

The parser to be updated. Arguments defaults should not be given, or if given should only be xdress.utils.Not Specified. This is to prevent collisions with the run controller. Default values should instead be given in this class’s defaultrc attribute or method. Argument names or the dest keyword argument should match the keys in defaultrc.

defaultrc = {}

This may be a dict, RunControl instance, or other mapping or a function which returns any of these. The keys are string names of the run control parameters and the values are the associated default values.

rcdocs = {}

This may be a dict, another mapping, a function which returns a mapping. The keys are the string names of the run control parameters. The values are docstrings for the rc parameters.

rcupdaters = {}

This may be a dict, another mapping, a function which returns a mapping. The keys are the string names of the run control parameters. The values are callables which indicate how to update or merge two rc parameters with this key. The callable should take two instances and return a copy that represents the merger, e.g. lambda old, new: old + new. One useful example is for paths. Normally you want new paths to prepend old ones:

rcupdaters = {'includes': lambda old, new: list(new) + list(old)}

If a callable is not supplied for an rc parameter then the the default behaviour is to simply override the old value with the new one.

requires = ()

This is a sequence of strings, or a function which returns such, that lists the module names of other plugins that this plugin requires.

class xdress.plugins.Plugins(modnames, loaddeps=True)[source]

This is a class for managing the instantiation and execution of plugins.

The execution and control of plugins should happen in the following order:

  1. build_cli()
  2. merge_rcs()
  3. setup()
  4. execute()
  5. teardown()
  6. exit()
Parameters:

modnames : list of str

The module names where the plugins live. Plugins must have the name ‘XDressPlugin’ in the these modules.

loaddeps: bool, optional :

Flag for automatically loading dependencies, should only be False in a limited set of circumstances.

build_cli()[source]

Builds and returns a command line interface based on the plugins.

Returns:parser : argparse.ArgumentParser
execute()[source]

Preforms all plugin executions.

exit(err=0)[source]

Exits the process, possibly printing debug info.

merge_rcs()[source]

Finds all of the default run controllers and returns a new and full default RunControl() instance. This has also merged all of the rc updaters in the process.

setup()[source]

Performs all plugin setup tasks.

teardown()[source]

Preforms all plugin teardown tasks.

xdress.plugins.summarize_rcdocs(modnames, headersep='=', maxdflt=2000)[source]

For a list of plugin module names, return a rST string that summarizes the docstrings for all run control parameters.