1
# plugin.py - plugin base class for computerjanitor
2
# Copyright (C) 2008 Canonical, Ltd.
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, version 3 of the License.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
import computerjanitor
23
_ = computerjanitor.setup_gettext()
28
"""Base class for plugins.
30
These plugins only do one thing: identify cruft. See the 'get_cruft'
35
def get_condition(self):
36
if hasattr(self, "_condition"):
37
return self._condition
41
def set_condition(self, condition):
42
self._condition = condition
44
condition = property(get_condition, set_condition)
46
def set_application(self, app):
47
"""Set the Application instance this plugin belongs to.
49
This is used by the plugin manager when creating the plugin
50
instance. In a perfect world, this would be done via the
51
__init__ method, but since I took a wrong left turn, I ended
52
up in an imperfect world, and therefore giving the Application
53
instance to __init__ would mandate that all sub-classes would
54
have to deal with that explicitly. That is a lot of unnecessary
55
make-work, which we should avoid. Therefore, we do it via this
58
The class may access the Application instance via the
65
def do_cleanup_cruft(self):
66
"""Find cruft and clean it up
68
This is a helper method
70
for cruft in self.get_cruft():
75
"""Find some cruft in the system.
77
This method MUST return an iterator (see 'yield' statement).
78
This interface design allows cruft to be collected piecemeal,
79
which makes it easier to show progress in the user interface.
81
The base class default implementation of this raises an
82
exception. Subclasses MUST override this method.
86
raise computerjanitor.UnimplementedMethod(self.get_cruft)
88
def post_cleanup(self):
89
"""Does plugin wide cleanup after the individual cleanup
92
This is useful for stuff that needs to be proccessed
93
in batches (e.g. for performance reasons) like package
99
class PluginManager(object):
101
"""Class to find and load plugins.
103
Plugins are stored in files named '*_plugin.py' in the list of
104
directories given to the initializer.
108
def __init__(self, app, plugin_dirs):
110
self._plugin_dirs = plugin_dirs
113
def get_plugin_files(self):
114
"""Return all filenames in which plugins may be stored."""
118
for dirname in self._plugin_dirs:
119
basenames = [x for x in os.listdir(dirname)
120
if x.endswith("_plugin.py")]
121
logging.debug(_("Plugin modules in %s: %s") %
122
(dirname, " ".join(basenames)))
123
names += [os.path.join(dirname, x) for x in basenames]
127
def _find_plugins(self, module):
128
"""Find and instantiate all plugins in a module."""
130
for dummy, member in inspect.getmembers(module):
131
if inspect.isclass(member) and issubclass(member, Plugin):
132
plugins.append(member)
133
logging.debug(_("Plugins in %s: %s") %
134
(module, " ".join(str(x) for x in plugins)))
135
return [plugin() for plugin in plugins]
137
def _load_module(self, filename):
138
"""Load a module from a filename."""
139
logging.debug(_("Loading module %s") % filename)
140
module_name, dummy = os.path.splitext(os.path.basename(filename))
141
f = file(filename, "r")
142
module = imp.load_module(module_name, f, filename,
143
(".py", "r", imp.PY_SOURCE))
147
def get_plugins(self, condition=None, callback=None):
148
"""Return all plugins that have been found.
150
If callback is specified, it is called after each plugin has
151
been found, with the following arguments: filename, index of
152
filename in list of files to be examined (starting with 0), and
153
total number of files to be examined. The purpose of this is to
154
allow the callback to inform the user in case things take a long
159
if self._plugins is None:
161
filenames = self.get_plugin_files()
162
for i in range(len(filenames)):
164
callback(filenames[i], i, len(filenames))
165
module = self._load_module(filenames[i])
166
for plugin in self._find_plugins(module):
167
plugin.set_application(self._app)
168
self._plugins.append(plugin)
170
return [p for p in self._plugins
171
if p.condition == condition or condition == "*"]