1
# Copyright (C) 2004, 2005 Canonical Ltd
1
# Copyright (C) 2004, 2005, 2007 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
42
42
from bzrlib import (
46
from bzrlib import plugins as _mod_plugins
49
from bzrlib.symbol_versioning import deprecated_function, one_three
49
50
from bzrlib.trace import mutter, warning, log_exception_quietly
60
61
return DEFAULT_PLUGIN_PATH
64
"""Return a dictionary of the plugins."""
66
for name, plugin in plugins.__dict__.items():
67
if isinstance(plugin, types.ModuleType):
72
64
def disable_plugins():
73
65
"""Disable loading plugins.
75
def _strip_trailing_sep(path):
76
return path.rstrip("\\/")
83
79
def set_plugins_path():
84
80
"""Set the path for plugins to be loaded from."""
85
81
path = os.environ.get('BZR_PLUGIN_PATH',
86
82
get_default_plugin_path()).split(os.pathsep)
87
# search the plugin path before the bzrlib installed dir
88
path.append(os.path.dirname(plugins.__file__))
89
plugins.__path__ = path
83
bzr_exe = bool(getattr(sys, 'frozen', None))
84
if bzr_exe: # expand path for bzr.exe
85
# We need to use relative path to system-wide plugin
86
# directory because bzrlib from standalone bzr.exe
87
# could be imported by another standalone program
88
# (e.g. bzr-config; or TortoiseBzr/Olive if/when they
89
# will become standalone exe). [bialix 20071123]
90
# __file__ typically is
91
# C:\Program Files\Bazaar\lib\library.zip\bzrlib\plugin.pyc
92
# then plugins directory is
93
# C:\Program Files\Bazaar\plugins
94
# so relative path is ../../../plugins
95
path.append(osutils.abspath(osutils.pathjoin(
96
osutils.dirname(__file__), '../../../plugins')))
97
# Get rid of trailing slashes, since Python can't handle them when
98
# it tries to import modules.
99
path = map(_strip_trailing_sep, path)
100
if not bzr_exe: # don't look inside library.zip
101
# search the plugin path before the bzrlib installed dir
102
path.append(os.path.dirname(_mod_plugins.__file__))
103
_mod_plugins.__path__ = path
124
138
The python module path for bzrlib.plugins will be modified to be 'dirs'.
126
plugins.__path__ = dirs
140
# We need to strip the trailing separators here as well as in the
141
# set_plugins_path function because calling code can pass anything in to
142
# this function, and since it sets plugins.__path__, it should set it to
143
# something that will be valid for Python to use (in case people try to
144
# run "import bzrlib.plugins.PLUGINNAME" after calling this function).
145
_mod_plugins.__path__ = map(_strip_trailing_sep, dirs)
130
149
mutter('looking for plugins in %s', d)
131
150
if os.path.isdir(d):
134
# it might be a zip: try loading from the zip.
139
154
# backwards compatability: load_from_dirs was the old name
172
if getattr(plugins, f, None):
187
if getattr(_mod_plugins, f, None):
173
188
mutter('Plugin name %s already loaded', f)
175
190
# mutter('add plugin name %s', f)
183
198
except Exception, e:
184
199
## import pdb; pdb.set_trace()
185
200
if re.search('\.|-| ', name):
186
warning('Unable to load plugin %r from %r: '
187
'It is not a valid python module name.' % (name, d))
201
sanitised_name = re.sub('[-. ]', '_', name)
202
warning("Unable to load %r in %r as a plugin because file path"
203
" isn't a valid module name; try renaming it to %r."
204
% (name, d, sanitised_name))
189
206
warning('Unable to load plugin %r from %r' % (name, d))
190
207
log_exception_quietly()
210
@deprecated_function(one_three)
193
211
def load_from_zip(zip_name):
194
212
"""Load all the plugins in a zip."""
195
213
valid_suffixes = ('.py', '.pyc', '.pyo') # only python modules/packages
197
if '.zip' not in zip_name:
200
ziobj = zipimport.zipimporter(zip_name)
201
except zipimport.ZipImportError:
216
index = zip_name.rindex('.zip')
219
archive = zip_name[:index+4]
220
prefix = zip_name[index+5:]
204
222
mutter('Looking for plugins in %r', zip_name)
208
224
# use zipfile to get list of files/dirs inside zip
209
z = zipfile.ZipFile(ziobj.archive)
210
namelist = z.namelist()
214
prefix = ziobj.prefix.replace('\\','/')
226
z = zipfile.ZipFile(archive)
227
namelist = z.namelist()
229
except zipfile.error:
234
prefix = prefix.replace('\\','/')
235
if prefix[-1] != '/':
216
238
namelist = [name[ix:]
217
239
for name in namelist
218
240
if name.startswith(prefix)]
220
242
mutter('Names in archive: %r', namelist)
222
244
for name in namelist:
249
271
if not plugin_name:
251
if getattr(plugins, plugin_name, None):
273
if getattr(_mod_plugins, plugin_name, None):
252
274
mutter('Plugin name %s already loaded', plugin_name)
256
plugin = ziobj.load_module(plugin_name)
257
setattr(plugins, plugin_name, plugin)
278
exec "import bzrlib.plugins.%s" % plugin_name in {}
258
279
mutter('Load plugin %s from zip %r', plugin_name, zip_name)
259
except zipimport.ZipImportError, e:
260
mutter('Unable to load plugin %r from %r: %s',
261
plugin_name, zip_name, str(e))
263
280
except KeyboardInterrupt:
265
282
except Exception, e:
269
286
log_exception_quietly()
290
"""Return a dictionary of the plugins.
292
Each item in the dictionary is a PlugIn object.
295
for name, plugin in _mod_plugins.__dict__.items():
296
if isinstance(plugin, types.ModuleType):
297
result[name] = PlugIn(name, plugin)
272
301
class PluginsHelpIndex(object):
273
302
"""A help index that returns help topics for plugins."""
335
364
def get_help_topic(self):
336
365
"""Return the modules help topic - its __name__ after bzrlib.plugins.."""
337
366
return self.module.__name__[len('bzrlib.plugins.'):]
369
class PlugIn(object):
370
"""The bzrlib representation of a plugin.
372
The PlugIn object provides a way to manipulate a given plugin module.
375
def __init__(self, name, module):
376
"""Construct a plugin for module."""
381
"""Get the path that this plugin was loaded from."""
382
if getattr(self.module, '__path__', None) is not None:
383
return os.path.abspath(self.module.__path__[0])
384
elif getattr(self.module, '__file__', None) is not None:
385
path = os.path.abspath(self.module.__file__)
386
if path[-4:] in ('.pyc', '.pyo'):
387
pypath = path[:-4] + '.py'
388
if os.path.isfile(pypath):
392
return repr(self.module)
395
return "<%s.%s object at %s, name=%s, module=%s>" % (
396
self.__class__.__module__, self.__class__.__name__, id(self),
397
self.name, self.module)
401
def test_suite(self):
402
"""Return the plugin's test suite."""
403
if getattr(self.module, 'test_suite', None) is not None:
404
return self.module.test_suite()
408
def version_info(self):
409
"""Return the plugin's version_tuple or None if unknown."""
410
version_info = getattr(self.module, 'version_info', None)
411
if version_info is not None and len(version_info) == 3:
412
version_info = tuple(version_info) + ('final', 0)
415
def _get__version__(self):
416
version_info = self.version_info()
417
if version_info is None:
419
if version_info[3] == 'final':
420
version_string = '%d.%d.%d' % version_info[:3]
422
version_string = '%d.%d.%d%s%d' % version_info
423
return version_string
425
__version__ = property(_get__version__)