~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

  • Committer: Vincent Ladeuil
  • Date: 2008-01-03 08:49:38 UTC
  • mfrom: (3111.1.31 175524)
  • mto: This revision was merged to the branch mainline in revision 3158.
  • Revision ID: v.ladeuil+lp@free.fr-20080103084938-7kvurk5uvde2ui54
Fix bug #175524, http test servers are 1.1 compliant

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2007 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 zipimport
 
40
import zipfile
41
41
 
42
42
from bzrlib import (
43
43
    config,
44
44
    osutils,
45
 
    plugins,
46
45
    )
 
46
from bzrlib import plugins as _mod_plugins
47
47
""")
48
48
 
 
49
from bzrlib.symbol_versioning import deprecated_function, zero_ninetyone
49
50
from bzrlib.trace import mutter, warning, log_exception_quietly
50
51
 
51
52
 
56
57
    """Get the DEFAULT_PLUGIN_PATH"""
57
58
    global DEFAULT_PLUGIN_PATH
58
59
    if DEFAULT_PLUGIN_PATH is None:
59
 
        DEFAULT_PLUGIN_PATH = osutils.pathjoin(config.config_dir(), 'plugins')
 
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)
60
75
    return DEFAULT_PLUGIN_PATH
61
76
 
62
77
 
 
78
@deprecated_function(zero_ninetyone)
63
79
def all_plugins():
64
80
    """Return a dictionary of the plugins."""
65
 
    result = {}
66
 
    for name, plugin in plugins.__dict__.items():
67
 
        if isinstance(plugin, types.ModuleType):
68
 
            result[name] = plugin
69
 
    return result
 
81
    return dict((name, plugin.module) for name, plugin in plugins().items())
70
82
 
71
83
 
72
84
def disable_plugins():
80
92
    _loaded = True
81
93
 
82
94
 
 
95
def _strip_trailing_sep(path):
 
96
    return path.rstrip("\\/")
 
97
 
 
98
 
83
99
def set_plugins_path():
84
100
    """Set the path for plugins to be loaded from."""
85
101
    path = os.environ.get('BZR_PLUGIN_PATH',
86
102
                          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)
87
106
    # search the plugin path before the bzrlib installed dir
88
 
    path.append(os.path.dirname(plugins.__file__))
89
 
    plugins.__path__ = path
 
107
    path.append(os.path.dirname(_mod_plugins.__file__))
 
108
    _mod_plugins.__path__ = path
90
109
    return path
91
110
 
92
111
 
123
142
 
124
143
    The python module path for bzrlib.plugins will be modified to be 'dirs'.
125
144
    """
126
 
    plugins.__path__ = dirs
 
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)
127
151
    for d in dirs:
128
152
        if not d:
129
153
            continue
133
157
        else:
134
158
            # it might be a zip: try loading from the zip.
135
159
            load_from_zip(d)
136
 
            continue
137
160
 
138
161
 
139
162
# backwards compatability: load_from_dirs was the old name
169
192
                    break
170
193
            else:
171
194
                continue
172
 
        if getattr(plugins, f, None):
 
195
        if getattr(_mod_plugins, f, None):
173
196
            mutter('Plugin name %s already loaded', f)
174
197
        else:
175
198
            # mutter('add plugin name %s', f)
183
206
        except Exception, e:
184
207
            ## import pdb; pdb.set_trace()
185
208
            if re.search('\.|-| ', name):
186
 
                warning('Unable to load plugin %r from %r: '
187
 
                    'It is not a valid python module name.' % (name, d))
 
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))
188
213
            else:
189
214
                warning('Unable to load plugin %r from %r' % (name, d))
190
215
            log_exception_quietly()
194
219
    """Load all the plugins in a zip."""
195
220
    valid_suffixes = ('.py', '.pyc', '.pyo')    # only python modules/packages
196
221
                                                # is allowed
197
 
    if '.zip' not in zip_name:
198
 
        return
 
222
 
199
223
    try:
200
 
        ziobj = zipimport.zipimporter(zip_name)
201
 
    except zipimport.ZipImportError:
202
 
        # not a valid zip
 
224
        index = zip_name.rindex('.zip')
 
225
    except ValueError:
203
226
        return
 
227
    archive = zip_name[:index+4]
 
228
    prefix = zip_name[index+5:]
 
229
 
204
230
    mutter('Looking for plugins in %r', zip_name)
205
 
    
206
 
    import zipfile
207
231
 
208
232
    # use zipfile to get list of files/dirs inside zip
209
 
    z = zipfile.ZipFile(ziobj.archive)
210
 
    namelist = z.namelist()
211
 
    z.close()
212
 
    
213
 
    if ziobj.prefix:
214
 
        prefix = ziobj.prefix.replace('\\','/')
 
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 += '/'
215
245
        ix = len(prefix)
216
246
        namelist = [name[ix:]
217
247
                    for name in namelist
218
248
                    if name.startswith(prefix)]
219
 
    
 
249
 
220
250
    mutter('Names in archive: %r', namelist)
221
251
    
222
252
    for name in namelist:
248
278
    
249
279
        if not plugin_name:
250
280
            continue
251
 
        if getattr(plugins, plugin_name, None):
 
281
        if getattr(_mod_plugins, plugin_name, None):
252
282
            mutter('Plugin name %s already loaded', plugin_name)
253
283
            continue
254
284
    
255
285
        try:
256
 
            plugin = ziobj.load_module(plugin_name)
257
 
            setattr(plugins, plugin_name, plugin)
 
286
            exec "import bzrlib.plugins.%s" % plugin_name in {}
258
287
            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
263
288
        except KeyboardInterrupt:
264
289
            raise
265
290
        except Exception, e:
269
294
            log_exception_quietly()
270
295
 
271
296
 
 
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
 
272
309
class PluginsHelpIndex(object):
273
310
    """A help index that returns help topics for plugins."""
274
311
 
335
372
    def get_help_topic(self):
336
373
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
337
374
        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__)