| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 | |
| 4 | |
| 5 from __future__ import nested_scopes | |
| 6 | |
| 7 # System Imports | |
| 8 import sys | |
| 9 import os | |
| 10 import errno | |
| 11 import types | |
| 12 import warnings | |
| 13 | |
| 14 # Twisted imports | |
| 15 from twisted.python import util | |
| 16 | |
| 17 # Sibling Imports | |
| 18 from reflect import namedModule | |
| 19 | |
| 20 try: | |
| 21 from os.path import realpath as cacheTransform | |
| 22 except ImportError: | |
| 23 from os.path import abspath as cacheTransform | |
| 24 | |
| 25 class PlugIn: | |
| 26 """I am a Python module registered in a plugins.tml file. | |
| 27 """ | |
| 28 def __init__(self, name, module, **kw): | |
| 29 self.name = name | |
| 30 self.module = module | |
| 31 for key, value in kw.items(): | |
| 32 setattr(self, key, value) | |
| 33 | |
| 34 def isLoaded(self): | |
| 35 """Check to see if the module for this plugin has been imported yet. | |
| 36 | |
| 37 @rtype: C{int} | |
| 38 @return: A true value if the module for this plugin has been loaded, | |
| 39 false otherwise. | |
| 40 """ | |
| 41 return sys.modules.has_key(self.module) | |
| 42 | |
| 43 def load(self): | |
| 44 """Load the module for this plugin. | |
| 45 | |
| 46 @rtype: C{ModuleType} | |
| 47 @return: The module object that is loaded. | |
| 48 """ | |
| 49 return namedModule(self.module) | |
| 50 | |
| 51 def __repr__(self): | |
| 52 if self.isLoaded(): | |
| 53 loaded = ' loaded' | |
| 54 else: | |
| 55 loaded = '' | |
| 56 return "<Plugin %s %s%s>" % (repr(self.name), self.module, loaded) | |
| 57 | |
| 58 class DropIn: | |
| 59 """I am a Python package containing plugins.tml. | |
| 60 """ | |
| 61 def __init__(self, name): | |
| 62 self.name = name | |
| 63 self.plugins = [] | |
| 64 | |
| 65 def register(self, name, module, **kw): | |
| 66 """Register a new plug-in. | |
| 67 """ | |
| 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)) | |
| 72 | |
| 73 def __repr__(self): | |
| 74 return "<Package %s %s>" % (self.name, self.plugins) | |
| 75 | |
| 76 | |
| 77 def _prepCallbacks(debug, progress): | |
| 78 if debug: | |
| 79 try: | |
| 80 debug('Looking for plugin.tml files') | |
| 81 except: | |
| 82 debug = lambda x: sys.stdout.write(x + '\n') | |
| 83 debug('Looking for plugin.tml files') | |
| 84 else: | |
| 85 debug = lambda x: None | |
| 86 if progress: | |
| 87 try: | |
| 88 progress(0.0) | |
| 89 except: | |
| 90 pb = util.makeStatusBar(76) | |
| 91 progress = lambda x, pb=pb: sys.stdout.write(pb(x) + '\r') | |
| 92 progress(0.0) | |
| 93 else: | |
| 94 progress = lambda x: None | |
| 95 return debug, progress | |
| 96 | |
| 97 def getPluginFileList(debugInspection=None, showProgress=None): | |
| 98 """Find plugin.tml files in subdirectories of paths in C{sys.path} | |
| 99 | |
| 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). | |
| 104 | |
| 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). | |
| 110 | |
| 111 @rtype: C{list} of C{str} | |
| 112 @return: A list of the plugin.tml files found. | |
| 113 """ | |
| 114 if isinstance(debugInspection, types.IntType): | |
| 115 warnings.warn( | |
| 116 "int parameter for debugInspection is deprecated, pass None or " | |
| 117 "a function that takes a single argument instead.", | |
| 118 DeprecationWarning, 2 | |
| 119 ) | |
| 120 if isinstance(showProgress, types.IntType): | |
| 121 warnings.warn( | |
| 122 "int parameter for showProgress is deprecated, pass None or " | |
| 123 "a function that takes a single argument instead.", | |
| 124 DeprecationWarning, 2 | |
| 125 ) | |
| 126 debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress
) | |
| 127 exists = os.path.exists | |
| 128 join = os.sep.join | |
| 129 result = [] | |
| 130 loaded = {} | |
| 131 seenNames = {} | |
| 132 | |
| 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)] | |
| 140 | |
| 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"): | |
| 144 try: | |
| 145 paths.remove(p) | |
| 146 except ValueError: | |
| 147 pass | |
| 148 progress = 0.0 | |
| 149 increments = 1.0 / len(paths) | |
| 150 | |
| 151 for (index, d) in zip(range(len(paths)), paths): | |
| 152 showProgress(progress) | |
| 153 if loaded.has_key(d): | |
| 154 debugInspection('Already saw ' + d) | |
| 155 continue | |
| 156 else: | |
| 157 debugInspection('Recursing through ' + d) | |
| 158 try: | |
| 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) | |
| 164 else: | |
| 165 raise | |
| 166 else: | |
| 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] | |
| 169 if not subDirs: | |
| 170 continue | |
| 171 incr = increments * (1.0 / len(subDirs)) | |
| 172 for plugindir in subDirs: | |
| 173 if seenNames.has_key(plugindir): | |
| 174 debugInspection('Seen %s already' % plugindir) | |
| 175 continue | |
| 176 tmlname = join((d, plugindir, "plugins.tml")) | |
| 177 if isAModule(join((d,plugindir))): | |
| 178 seenNames[plugindir] = 1 | |
| 179 if exists(tmlname): | |
| 180 result.append(tmlname) | |
| 181 debugInspection('Found ' + tmlname) | |
| 182 else: | |
| 183 debugInspection('Failed ' + tmlname) | |
| 184 else: | |
| 185 debugInspection('Not a module ' + tmlname) | |
| 186 progress = progress + incr | |
| 187 showProgress(progress) | |
| 188 | |
| 189 showProgress(1.0) | |
| 190 return result | |
| 191 | |
| 192 def loadPlugins(plugInType, fileList, debugInspection=None, showProgress=None): | |
| 193 """Traverse the given list of files and attempt to load plugins from them. | |
| 194 | |
| 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 | |
| 198 plugin.tml files. | |
| 199 | |
| 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} | |
| 203 function. | |
| 204 | |
| 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). | |
| 209 | |
| 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). | |
| 215 | |
| 216 @rtype: C{list} | |
| 217 @return: A list of the C{PlugIn} objects found. | |
| 218 """ | |
| 219 if isinstance(debugInspection, types.IntType): | |
| 220 warnings.warn( | |
| 221 "int parameter for debugInspection is deprecated, pass None or " | |
| 222 "a function that takes a single argument instead.", | |
| 223 DeprecationWarning, 4 | |
| 224 ) | |
| 225 if isinstance(showProgress, types.IntType): | |
| 226 warnings.warn( | |
| 227 "int parameter for showProgress is deprecated, pass None or " | |
| 228 "a function that takes a single argument instead.", | |
| 229 DeprecationWarning, 4 | |
| 230 ) | |
| 231 result = [] | |
| 232 debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress
) | |
| 233 | |
| 234 if not fileList: | |
| 235 raise ValueError("No plugins passed to loadPlugins") | |
| 236 | |
| 237 increments = 1.0 / len(fileList) | |
| 238 progress = 0.0 | |
| 239 | |
| 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} | |
| 246 try: | |
| 247 execfile(tmlFile, ns) | |
| 248 except (IOError, OSError), e: | |
| 249 # guess we don't have permissions for that | |
| 250 debugInspection("Error loading: %s" % e) | |
| 251 continue | |
| 252 | |
| 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,)) | |
| 259 else: | |
| 260 debugInspection("Disqualified %r" % (plugin,)) | |
| 261 progress = progress + incr | |
| 262 showProgress(progress) | |
| 263 debugInspection("Finished loading from %s!" % tmlFile) | |
| 264 | |
| 265 showProgress(1.0) | |
| 266 debugInspection("Returning %r" % (result,)) | |
| 267 return result | |
| 268 | |
| 269 def getPlugIns(plugInType, debugInspection=None, showProgress=None): | |
| 270 """Helper function to get all the plugins of a particular type. | |
| 271 | |
| 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 | |
| 275 plugin.tml files. | |
| 276 | |
| 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). | |
| 281 | |
| 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). | |
| 287 | |
| 288 @rtype: C{list} | |
| 289 @return: A list of C{PlugIn} objects that were found. | |
| 290 """ | |
| 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) | |
| 295 | |
| 296 def _getPlugIns(plugInType, debugInspection=None, showProgress=None): | |
| 297 if isinstance(debugInspection, types.IntType): | |
| 298 warnings.warn( | |
| 299 "int parameter for debugInspection is deprecated, pass None or " | |
| 300 "a function that takes a single argument instead.", | |
| 301 DeprecationWarning, 3 | |
| 302 ) | |
| 303 if isinstance(showProgress, types.IntType): | |
| 304 warnings.warn( | |
| 305 "int parameter for showProgress is deprecated, pass None or " | |
| 306 "a function that takes a single argument instead.", | |
| 307 DeprecationWarning, 3 | |
| 308 ) | |
| 309 debugInspection, showProgress = _prepCallbacks(debugInspection, showProgress
) | |
| 310 | |
| 311 firstHalf = secondHalf = lambda x: None | |
| 312 if showProgress: | |
| 313 firstHalf = lambda x: showProgress(x / 2.0) | |
| 314 secondHalf = lambda x: showProgress(x / 2.0 + 0.5) | |
| 315 | |
| 316 tmlFiles = getPluginFileList(debugInspection, firstHalf) | |
| 317 if not tmlFiles: | |
| 318 return [] | |
| 319 return loadPlugins(plugInType, tmlFiles, debugInspection, secondHalf) | |
| 320 | |
| 321 def isAModule(d): | |
| 322 """This function checks the directory for __init__ files. | |
| 323 """ | |
| 324 suffixes = ['py', 'pyc', 'pyo', 'so', 'pyd', 'dll'] | |
| 325 exists = os.path.exists | |
| 326 join = os.sep.join | |
| 327 for s in suffixes: # bad algorithm, but probably works | |
| 328 if exists(join((d,'__init__.%s' % s))): | |
| 329 return 1 | |
| 330 return 0 | |
| 331 | |
| 332 __all__ = ['PlugIn', 'DropIn', 'getPluginFileList', 'loadPlugins', 'getPlugIns'] | |
| OLD | NEW |