~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

fix NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2007 Canonical Ltd
 
1
# Copyright (C) 2004, 2005 Canonical Ltd
2
2
#
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
37
37
import imp
38
38
import re
39
39
import types
40
 
import zipfile
 
40
import zipimport
41
41
 
42
42
from bzrlib import (
43
43
    config,
44
44
    osutils,
 
45
    plugins,
45
46
    )
46
 
from bzrlib import plugins as _mod_plugins
47
47
""")
48
48
 
49
 
from bzrlib.symbol_versioning import deprecated_function, zero_ninetyone
50
49
from bzrlib.trace import mutter, warning, log_exception_quietly
51
50
 
52
51
 
57
56
    """Get the DEFAULT_PLUGIN_PATH"""
58
57
    global DEFAULT_PLUGIN_PATH
59
58
    if DEFAULT_PLUGIN_PATH is None:
60
 
        path = [osutils.pathjoin(config.config_dir(), 'plugins')]
61
 
        if getattr(sys, 'frozen', None):    # bzr.exe
62
 
            # We need to use relative path to system-wide plugin
63
 
            # directory because bzrlib from standalone bzr.exe
64
 
            # could be imported by another standalone program
65
 
            # (e.g. bzr-config; or TortoiseBzr/Olive if/when they
66
 
            # will become standalone exe). [bialix 20071123]
67
 
            # __file__ typically is
68
 
            # C:\Program Files\Bazaar\lib\library.zip\bzrlib\plugin.pyc
69
 
            # then plugins directory is
70
 
            # C:\Program Files\Bazaar\plugins
71
 
            # so relative path is ../../../plugins
72
 
            path.append(osutils.abspath(osutils.pathjoin(
73
 
                osutils.dirname(__file__), '../../../plugins')))
74
 
        DEFAULT_PLUGIN_PATH = os.pathsep.join(path)
 
59
        DEFAULT_PLUGIN_PATH = osutils.pathjoin(config.config_dir(), 'plugins')
75
60
    return DEFAULT_PLUGIN_PATH
76
61
 
77
62
 
78
 
@deprecated_function(zero_ninetyone)
79
63
def all_plugins():
80
64
    """Return a dictionary of the plugins."""
81
 
    return dict((name, plugin.module) for name, plugin in plugins().items())
 
65
    result = {}
 
66
    for name, plugin in plugins.__dict__.items():
 
67
        if isinstance(plugin, types.ModuleType):
 
68
            result[name] = plugin
 
69
    return result
82
70
 
83
71
 
84
72
def disable_plugins():
92
80
    _loaded = True
93
81
 
94
82
 
95
 
def _strip_trailing_sep(path):
96
 
    return path.rstrip("\\/")
97
 
 
98
 
 
99
83
def set_plugins_path():
100
84
    """Set the path for plugins to be loaded from."""
101
85
    path = os.environ.get('BZR_PLUGIN_PATH',
102
86
                          get_default_plugin_path()).split(os.pathsep)
103
 
    # Get rid of trailing slashes, since Python can't handle them when
104
 
    # it tries to import modules.
105
 
    path = map(_strip_trailing_sep, path)
106
87
    # search the plugin path before the bzrlib installed dir
107
 
    path.append(os.path.dirname(_mod_plugins.__file__))
108
 
    _mod_plugins.__path__ = path
 
88
    path.append(os.path.dirname(plugins.__file__))
 
89
    plugins.__path__ = path
109
90
    return path
110
91
 
111
92
 
142
123
 
143
124
    The python module path for bzrlib.plugins will be modified to be 'dirs'.
144
125
    """
145
 
    # We need to strip the trailing separators here as well as in the
146
 
    # set_plugins_path function because calling code can pass anything in to
147
 
    # this function, and since it sets plugins.__path__, it should set it to
148
 
    # something that will be valid for Python to use (in case people try to
149
 
    # run "import bzrlib.plugins.PLUGINNAME" after calling this function).
150
 
    _mod_plugins.__path__ = map(_strip_trailing_sep, dirs)
 
126
    plugins.__path__ = dirs
151
127
    for d in dirs:
152
128
        if not d:
153
129
            continue
157
133
        else:
158
134
            # it might be a zip: try loading from the zip.
159
135
            load_from_zip(d)
 
136
            continue
160
137
 
161
138
 
162
139
# backwards compatability: load_from_dirs was the old name
192
169
                    break
193
170
            else:
194
171
                continue
195
 
        if getattr(_mod_plugins, f, None):
 
172
        if getattr(plugins, f, None):
196
173
            mutter('Plugin name %s already loaded', f)
197
174
        else:
198
175
            # mutter('add plugin name %s', f)
206
183
        except Exception, e:
207
184
            ## import pdb; pdb.set_trace()
208
185
            if re.search('\.|-| ', name):
209
 
                sanitised_name = re.sub('[-. ]', '_', name)
210
 
                warning("Unable to load %r in %r as a plugin because file path"
211
 
                        " isn't a valid module name; try renaming it to %r."
212
 
                        % (name, d, sanitised_name))
 
186
                warning('Unable to load plugin %r from %r: '
 
187
                    'It is not a valid python module name.' % (name, d))
213
188
            else:
214
189
                warning('Unable to load plugin %r from %r' % (name, d))
215
190
            log_exception_quietly()
219
194
    """Load all the plugins in a zip."""
220
195
    valid_suffixes = ('.py', '.pyc', '.pyo')    # only python modules/packages
221
196
                                                # is allowed
222
 
 
 
197
    if '.zip' not in zip_name:
 
198
        return
223
199
    try:
224
 
        index = zip_name.rindex('.zip')
225
 
    except ValueError:
 
200
        ziobj = zipimport.zipimporter(zip_name)
 
201
    except zipimport.ZipImportError:
 
202
        # not a valid zip
226
203
        return
227
 
    archive = zip_name[:index+4]
228
 
    prefix = zip_name[index+5:]
229
 
 
230
204
    mutter('Looking for plugins in %r', zip_name)
 
205
    
 
206
    import zipfile
231
207
 
232
208
    # use zipfile to get list of files/dirs inside zip
233
 
    try:
234
 
        z = zipfile.ZipFile(archive)
235
 
        namelist = z.namelist()
236
 
        z.close()
237
 
    except zipfile.error:
238
 
        # not a valid zip
239
 
        return
240
 
 
241
 
    if prefix:
242
 
        prefix = prefix.replace('\\','/')
243
 
        if prefix[-1] != '/':
244
 
            prefix += '/'
 
209
    z = zipfile.ZipFile(ziobj.archive)
 
210
    namelist = z.namelist()
 
211
    z.close()
 
212
    
 
213
    if ziobj.prefix:
 
214
        prefix = ziobj.prefix.replace('\\','/')
245
215
        ix = len(prefix)
246
216
        namelist = [name[ix:]
247
217
                    for name in namelist
248
218
                    if name.startswith(prefix)]
249
 
 
 
219
    
250
220
    mutter('Names in archive: %r', namelist)
251
221
    
252
222
    for name in namelist:
278
248
    
279
249
        if not plugin_name:
280
250
            continue
281
 
        if getattr(_mod_plugins, plugin_name, None):
 
251
        if getattr(plugins, plugin_name, None):
282
252
            mutter('Plugin name %s already loaded', plugin_name)
283
253
            continue
284
254
    
285
255
        try:
286
 
            exec "import bzrlib.plugins.%s" % plugin_name in {}
 
256
            plugin = ziobj.load_module(plugin_name)
 
257
            setattr(plugins, plugin_name, plugin)
287
258
            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))
 
262
            continue
288
263
        except KeyboardInterrupt:
289
264
            raise
290
265
        except Exception, e:
294
269
            log_exception_quietly()
295
270
 
296
271
 
297
 
def plugins():
298
 
    """Return a dictionary of the plugins.
299
 
    
300
 
    Each item in the dictionary is a PlugIn object.
301
 
    """
302
 
    result = {}
303
 
    for name, plugin in _mod_plugins.__dict__.items():
304
 
        if isinstance(plugin, types.ModuleType):
305
 
            result[name] = PlugIn(name, plugin)
306
 
    return result
307
 
 
308
 
 
309
272
class PluginsHelpIndex(object):
310
273
    """A help index that returns help topics for plugins."""
311
274
 
372
335
    def get_help_topic(self):
373
336
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
374
337
        return self.module.__name__[len('bzrlib.plugins.'):]
375
 
 
376
 
 
377
 
class PlugIn(object):
378
 
    """The bzrlib representation of a plugin.
379
 
 
380
 
    The PlugIn object provides a way to manipulate a given plugin module.
381
 
    """
382
 
 
383
 
    def __init__(self, name, module):
384
 
        """Construct a plugin for module."""
385
 
        self.name = name
386
 
        self.module = module
387
 
 
388
 
    def path(self):
389
 
        """Get the path that this plugin was loaded from."""
390
 
        if getattr(self.module, '__path__', None) is not None:
391
 
            return os.path.abspath(self.module.__path__[0])
392
 
        elif getattr(self.module, '__file__', None) is not None:
393
 
            return os.path.abspath(self.module.__file__)
394
 
        else:
395
 
            return repr(self.module)
396
 
 
397
 
    def __str__(self):
398
 
        return "<%s.%s object at %s, name=%s, module=%s>" % (
399
 
            self.__class__.__module__, self.__class__.__name__, id(self),
400
 
            self.name, self.module)
401
 
 
402
 
    __repr__ = __str__
403
 
 
404
 
    def test_suite(self):
405
 
        """Return the plugin's test suite."""
406
 
        if getattr(self.module, 'test_suite', None) is not None:
407
 
            return self.module.test_suite()
408
 
        else:
409
 
            return None
410
 
 
411
 
    def version_info(self):
412
 
        """Return the plugin's version_tuple or None if unknown."""
413
 
        version_info = getattr(self.module, 'version_info', None)
414
 
        if version_info is not None and len(version_info) == 3:
415
 
            version_info = tuple(version_info) + ('final', 0)
416
 
        return version_info
417
 
    
418
 
    def _get__version__(self):
419
 
        version_info = self.version_info()
420
 
        if version_info is None:
421
 
            return "unknown"
422
 
        if version_info[3] == 'final':
423
 
            version_string = '%d.%d.%d' % version_info[:3]
424
 
        else:
425
 
            version_string = '%d.%d.%d%s%d' % version_info
426
 
        return version_string
427
 
 
428
 
    __version__ = property(_get__version__)