1
2
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('doapfiend')
77
78
79
80
81
82
83
84 builtin_plugins = ['url', 'homepage', 'n3', 'xml', 'text', 'sourceforge',
85 'pypi', 'freshmeat', 'ohloh', 'fields']
86
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
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
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
118
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
134
135
136 warn("Unable to load plugin %s: %s" % \
137 (entry_point, err_msg), RuntimeWarning)
138 continue
139 if plugin.__module__.startswith('doapfiend.plugins'):
140 if builtin:
141 yield plugin
142 elif others:
143 yield plugin
144