1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
from __future__ import nested_scopes
15
from twisted.python import util
18
from reflect import namedModule
21
from os.path import realpath as cacheTransform
23
from os.path import abspath as cacheTransform
26
"""I am a Python module registered in a plugins.tml file.
28
def __init__(self, name, module, **kw):
31
for key, value in kw.items():
32
setattr(self, key, value)
35
"""Check to see if the module for this plugin has been imported yet.
38
@return: A true value if the module for this plugin has been loaded,
41
return sys.modules.has_key(self.module)
44
"""Load the module for this plugin.
47
@return: The module object that is loaded.
49
return namedModule(self.module)
56
return "<Plugin %s %s%s>" % (repr(self.name), self.module, loaded)
59
"""I am a Python package containing plugins.tml.
61
def __init__(self, name):
65
def register(self, name, module, **kw):
66
"""Register a new plug-in.
68
warnings.warn("The twisted.python.plugin system is deprecated. "
69
"See twisted.plugin for the revised edition.",
70
DeprecationWarning, 2)
71
self.plugins.append(PlugIn(name, module, **kw))
74
return "<Package %s %s>" % (self.name, self.plugins)
77
def _prepCallbacks(debug, progress):
80
debug('Looking for plugin.tml files')
82
debug = lambda x: sys.stdout.write(x + '\n')
83
debug('Looking for plugin.tml files')
85
debug = lambda x: None
90
pb = util.makeStatusBar(76)
91
progress = lambda x, pb=pb: sys.stdout.write(pb(x) + '\r')
94
progress = lambda x: None
95
return debug, progress
97
def getPluginFileList(debugInspection=None, showProgress=None):
98
"""Find plugin.tml files in subdirectories of paths in C{sys.path}
100
@type debugInspection: C{None} or a callable taking one argument
101
@param debugInspection: If not None, this is invoked with strings containing
102
debug information about the loading process. If it is any other true value,
103
this debug information is written to stdout (This behavior is deprecated).
105
@type showProgress: C{None} or a callable taking one argument.
106
@param showProgress: If not None, this is invoked with floating point
107
values between 0 and 1 describing the progress of the loading process.
108
If it is any other true value, this progress information is written to
109
stdout. (This behavior is deprecated).
111
@rtype: C{list} of C{str}
112
@return: A list of the plugin.tml files found.
114
if isinstance(debugInspection, types.IntType):
116
"int parameter for debugInspection is deprecated, pass None or "
117
"a function that takes a single argument instead.",
118
DeprecationWarning, 2
120
if isinstance(showProgress, types.IntType):
122
"int parameter for showProgress is deprecated, pass None or "
123
"a function that takes a single argument instead.",
124
DeprecationWarning, 2
126
debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
127
exists = os.path.exists
133
# XXX Some people claim to have found non-strings in sys.path (an empty
134
# list, in particular). Instead of tracking down the cause for their
135
# presence, they decided it was better to discard them unconditionally
136
# without further investigation. At some point, someone should track
137
# down where non-strings are coming from and do something about them.
138
paths = [cacheTransform(p) for p in sys.path
139
if isinstance(p, str) and os.path.isdir(p)]
141
# special case for commonly used directories we *know* shouldn't be checked
142
# and really slow down mktap and such-like in real installations
143
for p in ("/usr/bin", "/usr/local/bin"):
149
increments = 1.0 / len(paths)
151
for (index, d) in zip(range(len(paths)), paths):
152
showProgress(progress)
153
if loaded.has_key(d):
154
debugInspection('Already saw ' + d)
157
debugInspection('Recursing through ' + d)
159
subDirs = os.listdir(d)
160
except OSError, (err, s):
161
# Permission denied, carry on
162
if err == errno.EACCES:
163
debugInspection('Permission denied on ' + d)
167
# filter out files we obviously don't need to check - ones with '.' in them
168
subDirs = [s for s in subDirs if "." not in s]
171
incr = increments * (1.0 / len(subDirs))
172
for plugindir in subDirs:
173
if seenNames.has_key(plugindir):
174
debugInspection('Seen %s already' % plugindir)
176
tmlname = join((d, plugindir, "plugins.tml"))
177
if isAModule(join((d,plugindir))):
178
seenNames[plugindir] = 1
180
result.append(tmlname)
181
debugInspection('Found ' + tmlname)
183
debugInspection('Failed ' + tmlname)
185
debugInspection('Not a module ' + tmlname)
186
progress = progress + incr
187
showProgress(progress)
192
def loadPlugins(plugInType, fileList, debugInspection=None, showProgress=None):
193
"""Traverse the given list of files and attempt to load plugins from them.
195
@type plugInType: C{str}
196
@param plugInType: The type of plugin to search for. This is tested
197
against the C{type} argument to the C{register} function in the
200
@type fileList: C{list} of C{str}
201
@param fileList: A list of the files to attempt to load plugin
202
information from. One name is put in their scope, the C{register}
205
@type debugInspection: C{None} or a callable taking one argument
206
@param debugInspection: If not None, this is invoked with strings containing
207
debug information about the loading process. If it is any other true value,
208
this debug information is written to stdout (This behavior is deprecated).
210
@type showProgress: C{None} or a callable taking one argument.
211
@param showProgress: If not None, this is invoked with floating point
212
values between 0 and 1 describing the progress of the loading process.
213
If it is any other true value, this progress information is written to
214
stdout. (This behavior is deprecated).
217
@return: A list of the C{PlugIn} objects found.
219
if isinstance(debugInspection, types.IntType):
221
"int parameter for debugInspection is deprecated, pass None or "
222
"a function that takes a single argument instead.",
223
DeprecationWarning, 4
225
if isinstance(showProgress, types.IntType):
227
"int parameter for showProgress is deprecated, pass None or "
228
"a function that takes a single argument instead.",
229
DeprecationWarning, 4
232
debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
235
raise ValueError("No plugins passed to loadPlugins")
237
increments = 1.0 / len(fileList)
240
for (index, tmlFile) in zip(range(len(fileList)), fileList):
241
showProgress(progress)
242
debugInspection("Loading from " + tmlFile)
243
pname = os.path.split(os.path.abspath(tmlFile))[-2]
244
dropin = DropIn(pname)
245
ns = {'register': dropin.register, '__file__': tmlFile}
247
execfile(tmlFile, ns)
248
except (IOError, OSError), e:
249
# guess we don't have permissions for that
250
debugInspection("Error loading: %s" % e)
253
ldp = len(dropin.plugins) or 1.0
254
incr = increments * (1.0 / ldp)
255
for plugin in dropin.plugins:
256
if plugInType == plugin.type:
257
result.append(plugin)
258
debugInspection("Found %r" % (plugin,))
260
debugInspection("Disqualified %r" % (plugin,))
261
progress = progress + incr
262
showProgress(progress)
263
debugInspection("Finished loading from %s!" % tmlFile)
266
debugInspection("Returning %r" % (result,))
269
def getPlugIns(plugInType, debugInspection=None, showProgress=None):
270
"""Helper function to get all the plugins of a particular type.
272
@type plugInType: C{str}
273
@param plugInType: The type of plugin to search for. This is tested
274
against the C{type} argument to the C{register} function in the
277
@type debugInspection: C{None} or a callable taking one argument
278
@param debugInspection: If not None, this is invoked with strings containing
279
debug information about the loading process. If it is any other true value,
280
this debug information is written to stdout (This behavior is deprecated).
282
@type showProgress: C{None} or a callable taking one argument.
283
@param showProgress: If not None, this is invoked with floating point
284
values between 0 and 1 describing the progress of the loading process.
285
If it is any other true value, this progress information is written to
286
stdout. (This behavior is deprecated).
289
@return: A list of C{PlugIn} objects that were found.
291
warnings.warn("The twisted.python.plugin system is deprecated. "
292
"See twisted.plugin for the revised edition.",
293
DeprecationWarning, 2)
294
return _getPlugIns(plugInType, debugInspection, showProgress)
296
def _getPlugIns(plugInType, debugInspection=None, showProgress=None):
297
if isinstance(debugInspection, types.IntType):
299
"int parameter for debugInspection is deprecated, pass None or "
300
"a function that takes a single argument instead.",
301
DeprecationWarning, 3
303
if isinstance(showProgress, types.IntType):
305
"int parameter for showProgress is deprecated, pass None or "
306
"a function that takes a single argument instead.",
307
DeprecationWarning, 3
309
debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress)
311
firstHalf = secondHalf = lambda x: None
313
firstHalf = lambda x: showProgress(x / 2.0)
314
secondHalf = lambda x: showProgress(x / 2.0 + 0.5)
316
tmlFiles = getPluginFileList(debugInspection, firstHalf)
319
return loadPlugins(plugInType, tmlFiles, debugInspection, secondHalf)
322
"""This function checks the directory for __init__ files.
324
suffixes = ['py', 'pyc', 'pyo', 'so', 'pyd', 'dll']
325
exists = os.path.exists
327
for s in suffixes: # bad algorithm, but probably works
328
if exists(join((d,'__init__.%s' % s))):
332
__all__ = ['PlugIn', 'DropIn', 'getPluginFileList', 'loadPlugins', 'getPlugIns']