doapfiend.plugins

Package doapfiend :: Package plugins |
[hide private]
[frames] | no frames]

# Source Code for Package doapfiend.plugins

      1
      2  # pylint: disable-msg=W0142,C0103
      3
      4
      5  """
      6  Writing Plugins
      7  ===============
      8
      9  doapfiend supports setuptools_ entry point plugins.
     10
     11  There are two basic rules for plugins:
     12
     13   - Plugin classes should subclass `doapfiend.plugins.Plugin`_.
     14   - Plugins may implement any of the methods described in the class
     15     PluginInterface in doapfiend.plugins.base. Please note that this class is for
     16     documentary purposes only; plugins may not subclass PluginInterface.
     17
     18  Setuptools: http://peak.telecommunity.com/DevCenter/setuptools
     19  Doapfiend Plugins: http://trac.doapspace.org/doapfiend/wiki/DoapfiendPlugins
     20
     21  Registering
     22  -----------
     23
     24  For doapfiend to find a plugin, it must be part of a package that uses
     25  setuptools, and the plugin must be included in the entry points defined
     26  in the setup.py for the package::
     27
     28    setup(name='Some plugin',
     29          ...
     30          entry_points = {
     31              'doapfiend.plugins': [
     32                  'someplugin = someplugin:SomePlugin'
     33                  ]
     34              },
     35          ...
     36          )
     37
     38  Once the package is installed with install or develop, doapfiend will be able
     39  to load the plugin.
     40
     41  Defining options
     42  ----------------
     43
     44  All plugins must implement the methods ``add_options(self, parser, env)``
     45  and ``configure(self, options, conf)``. Subclasses of doapfiend.plugins.Plugin
     46  that want the standard options should call the superclass methods.
     47
     48  doapfiend uses optparse.OptionParser from the standard library to parse
     49  arguments. A plugin's ``add_options()`` method receives a parser
     50  instance. It's good form for a plugin to use that instance only to add
     51  additional arguments that take only long arguments (--like-this). Most
     52  of doapfiend's built-in arguments get their default value from an environment
     53  variable. This is a good practice because it allows options to be
     54  utilized when run through some other means than the doapfiendtests script.
     55
     56  A plugin's ``configure()`` method receives the parsed ``OptionParser`` options
     57  object, as well as the current config object. Plugins should configure their
     58  behavior based on the user-selected settings, and may raise exceptions
     59  if the configured behavior is nonsensical.
     60
     61  Logging
     62  -------
     63
     64  doapfiend uses the logging classes from the standard library. To enable users
     65  to view debug messages easily, plugins should use ``logging.getLogger()`` to
     66  acquire a logger in the ``doapfiend.plugins`` namespace.
     67
     68  """
     69
     70  import logging
     71  import pkg_resources
     72  from warnings import warn
     73  from inspect import isclass
     74  from doapfiend.plugins.base import Plugin
     75
     76  LOG = logging.getLogger(__name__)
     77
     78  # +==== IMPORTANT ====+
     79  #If you add any builtin plugins in doapfiend.plugins you must add them
     80  #to this list for them to be loaded. It's okay to add other Python modules
     81  #in the doapfiend.plugins namespace, but they won't be recognized as a plugin
     82  #unless listed here:
     83
     84  builtin_plugins = ['n3', 'xml', 'text', 'sourceforge', 'pypi', 'freshmeat',
     85          'homepage', 'url']
     86
     87 -def call_plugins(plugins, method, *arg, **kw):
     88      """Call all method on plugins in list, that define it, with provided
     89      arguments. The first response that is not None is returned.
     90      """
     91      for plug in plugins:
     92          func = getattr(plug, method, None)
     93          if func is None:
     94              continue
     95          LOG.debug("call plugin %s: %s", plug.name, method)
     96          result = func(*arg, **kw)
     97          if result is not None:
     98              return result
     99      return None
    100
    101 -def load_plugins(builtin=True, others=True):
    102      """Load plugins, either builtin, others, or both.
    103      """
    104      loaded = []
    105      if builtin:
    106          for name in builtin_plugins:
    107              try:
    108                  parent = __import__(__name__, globals(), locals(), [name])
    109                  #print name
    110                  pmod = getattr(parent, name)
    111                  for entry in dir(pmod):
    112                      obj = getattr(pmod, entry)
    113                      if (isclass(obj)
    114                          and issubclass(obj, Plugin)
    115                          and obj is not Plugin
    116                          and not obj in loaded):
    117                          #LOG.debug("load builtin plugin %s (%s)" % (name, obj))
    118                          #print "load builtin plugin %s (%s)" % (name, obj)
    119                          yield obj
    120                          loaded.append(obj)
    121              except KeyboardInterrupt:
    122                  raise
    123              except Exception, e:
    124                  warn("Unable to load builtin plugin %s: %s" % (name, e),
    125                       RuntimeWarning)
    126      for entry_point in pkg_resources.iter_entry_points('doapfiend.plugins'):
    127          LOG.debug("load plugin %s" % entry_point)
    128          try:
    129              plugin = entry_point.load()
    130          except KeyboardInterrupt:
    131              raise
    132          except Exception, err_msg:
    133              # never want a plugin load to exit doapfiend
    134              # but we can't log here because the logger is not yet
    135              # configured
    136              warn("Unable to load plugin %s: %s" % \
    137                      (entry_point, err_msg), RuntimeWarning)
    138              continue
    139          #print plugin.__module__
    140          if plugin.__module__.startswith('doapfiend.plugins'):
    141              if builtin:
    142                  yield plugin
    143          elif others:
    144              yield plugin
    145