259
268
load_from_dirs = load_from_path
271
def _find_plugin_module(dir, name):
272
"""Check if there is a valid python module that can be loaded as a plugin.
274
:param dir: The directory where the search is performed.
275
:param path: An existing file path, either a python file or a package
278
:return: (name, path, description) name is the module name, path is the
279
file to load and description is the tuple returned by
282
path = osutils.pathjoin(dir, name)
283
if os.path.isdir(path):
284
# Check for a valid __init__.py file, valid suffixes depends on -O and
285
# can be .py, .pyc and .pyo
286
for suffix, mode, kind in imp.get_suffixes():
287
if kind not in (imp.PY_SOURCE, imp.PY_COMPILED):
288
# We don't recognize compiled modules (.so, .dll, etc)
290
init_path = osutils.pathjoin(path, '__init__' + suffix)
291
if os.path.isfile(init_path):
292
return name, init_path, (suffix, mode, kind)
294
for suffix, mode, kind in imp.get_suffixes():
295
if name.endswith(suffix):
296
# Clean up the module name
297
name = name[:-len(suffix)]
298
if kind == imp.C_EXTENSION and name.endswith('module'):
299
name = name[:-len('module')]
300
return name, path, (suffix, mode, kind)
301
# There is no python module here
302
return None, None, (None, None, None)
305
def _load_plugin_module(name, dir):
306
"""Load plugine name from dir.
308
:param name: The plugin name in the bzrlib.plugins namespace.
309
:param dir: The directory the plugin is loaded from for error messages.
311
if ('bzrlib.plugins.%s' % name) in PluginImporter.blacklist:
314
exec "import bzrlib.plugins.%s" % name in {}
315
except KeyboardInterrupt:
317
except errors.IncompatibleAPI, e:
318
trace.warning("Unable to load plugin %r. It requested API version "
319
"%s of module %s but the minimum exported version is %s, and "
320
"the maximum is %s" %
321
(name, e.wanted, e.api, e.minimum, e.current))
323
trace.warning("%s" % e)
324
if re.search('\.|-| ', name):
325
sanitised_name = re.sub('[-. ]', '_', name)
326
if sanitised_name.startswith('bzr_'):
327
sanitised_name = sanitised_name[len('bzr_'):]
328
trace.warning("Unable to load %r in %r as a plugin because the "
329
"file path isn't a valid module name; try renaming "
330
"it to %r." % (name, dir, sanitised_name))
332
trace.warning('Unable to load plugin %r from %r' % (name, dir))
333
trace.log_exception_quietly()
334
if 'error' in debug.debug_flags:
335
trace.print_exception(sys.exc_info(), sys.stderr)
262
338
def load_from_dir(d):
263
339
"""Load the plugins in directory d.
265
341
d must be in the plugins module path already.
342
This function is called once for each directory in the module path.
267
# Get the list of valid python suffixes for __init__.py?
268
# this includes .py, .pyc, and .pyo (depending on if we are running -O)
269
# but it doesn't include compiled modules (.so, .dll, etc)
270
valid_suffixes = [suffix for suffix, mod_type, flags in imp.get_suffixes()
271
if flags in (imp.PY_SOURCE, imp.PY_COMPILED)]
272
package_entries = ['__init__'+suffix for suffix in valid_suffixes]
273
344
plugin_names = set()
274
for f in os.listdir(d):
275
path = osutils.pathjoin(d, f)
276
if os.path.isdir(path):
277
for entry in package_entries:
278
# This directory should be a package, and thus added to
280
if os.path.isfile(osutils.pathjoin(path, entry)):
282
else: # This directory is not a package
285
for suffix_info in imp.get_suffixes():
286
if f.endswith(suffix_info[0]):
287
f = f[:-len(suffix_info[0])]
288
if suffix_info[2] == imp.C_EXTENSION and f.endswith('module'):
289
f = f[:-len('module')]
345
for p in os.listdir(d):
346
name, path, desc = _find_plugin_module(d, p)
348
if name == '__init__':
349
# We do nothing with the __init__.py file in directories from
350
# the bzrlib.plugins module path, we may want to, one day
352
continue # We don't load __init__.py in the plugins dirs
353
elif getattr(_mod_plugins, name, None) is not None:
354
# The module has already been loaded from another directory
355
# during a previous call.
356
# FIXME: There should be a better way to report masked plugins
358
trace.mutter('Plugin name %s already loaded', name)
294
continue # We don't load __init__.py again in the plugin dir
295
elif getattr(_mod_plugins, f, None):
296
trace.mutter('Plugin name %s already loaded', f)
298
# trace.mutter('add plugin name %s', f)
360
plugin_names.add(name)
301
362
for name in plugin_names:
303
exec "import bzrlib.plugins.%s" % name in {}
304
except KeyboardInterrupt:
306
except errors.IncompatibleAPI, e:
307
trace.warning("Unable to load plugin %r. It requested API version "
308
"%s of module %s but the minimum exported version is %s, and "
309
"the maximum is %s" %
310
(name, e.wanted, e.api, e.minimum, e.current))
312
trace.warning("%s" % e)
313
## import pdb; pdb.set_trace()
314
if re.search('\.|-| ', name):
315
sanitised_name = re.sub('[-. ]', '_', name)
316
if sanitised_name.startswith('bzr_'):
317
sanitised_name = sanitised_name[len('bzr_'):]
318
trace.warning("Unable to load %r in %r as a plugin because the "
319
"file path isn't a valid module name; try renaming "
320
"it to %r." % (name, d, sanitised_name))
322
trace.warning('Unable to load plugin %r from %r' % (name, d))
323
trace.log_exception_quietly()
324
if 'error' in debug.debug_flags:
325
trace.print_exception(sys.exc_info(), sys.stderr)
363
_load_plugin_module(name, d)
485
523
return version_string
487
525
__version__ = property(_get__version__)
528
class _PluginImporter(object):
529
"""An importer tailored to bzr specific needs.
531
This is a singleton that takes care of:
532
- disabled plugins specified in 'blacklist',
533
- plugins that needs to be loaded from specific directories.
540
self.blacklist = set()
541
self.specific_paths = {}
543
def find_module(self, fullname, parent_path=None):
544
"""Search a plugin module.
546
Disabled plugins raise an import error, plugins with specific paths
547
returns a specific loader.
549
:return: None if the plugin doesn't need special handling, self
552
if not fullname.startswith('bzrlib.plugins.'):
554
if fullname in self.blacklist:
555
raise ImportError('%s is disabled' % fullname)
556
if fullname in self.specific_paths:
560
def load_module(self, fullname):
561
"""Load a plugin from a specific directory."""
562
# We are called only for specific paths
563
plugin_dir = self.specific_paths[fullname]
565
maybe_package = False
566
for p in os.listdir(plugin_dir):
567
if os.path.isdir(osutils.pathjoin(plugin_dir, p)):
568
# We're searching for files only and don't want submodules to
569
# be recognized as plugins (they are submodules inside the
573
suffix, mode, kind) = _find_plugin_module(plugin_dir, p)
575
candidate = (name, path, suffix, mode, kind)
576
if kind == imp.PY_SOURCE:
577
# We favour imp.PY_SOURCE (which will use the compiled
578
# version if available) over imp.PY_COMPILED (which is used
579
# only if the source is not available)
581
if candidate is None:
582
raise ImportError('%s cannot be loaded from %s'
583
% (fullname, plugin_dir))
586
mod = imp.load_module(fullname, f, path, (suffix, mode, kind))
587
# The plugin can contain modules, so be ready
588
mod.__path__ = [plugin_dir]
589
mod.__package__ = fullname
595
# Install a dedicated importer for plugins requiring special handling
596
PluginImporter = _PluginImporter()
597
sys.meta_path.append(PluginImporter)