~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

(vila) Forbid more operations on ReadonlyTransportDecorator (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
 
18
17
"""bzr python plugin support.
19
18
 
20
19
When load_plugins() is invoked, any python module in any directory in
30
29
called.
31
30
"""
32
31
 
 
32
from __future__ import absolute_import
 
33
 
33
34
import os
34
35
import sys
35
36
 
36
37
from bzrlib import osutils
37
38
 
38
39
from bzrlib.lazy_import import lazy_import
39
 
 
40
40
lazy_import(globals(), """
41
41
import imp
42
42
import re
49
49
    errors,
50
50
    trace,
51
51
    )
 
52
from bzrlib.i18n import gettext
52
53
from bzrlib import plugins as _mod_plugins
53
54
""")
54
55
 
55
 
from bzrlib.symbol_versioning import (
56
 
    deprecated_function,
57
 
    deprecated_in,
58
 
    )
59
 
 
60
56
 
61
57
DEFAULT_PLUGIN_PATH = None
62
58
_loaded = False
63
59
_plugins_disabled = False
64
60
 
65
61
 
 
62
plugin_warnings = {}
 
63
# Map from plugin name, to list of string warnings about eg plugin
 
64
# dependencies.
 
65
 
 
66
 
66
67
def are_plugins_disabled():
67
68
    return _plugins_disabled
68
69
 
77
78
    load_plugins([])
78
79
 
79
80
 
 
81
def describe_plugins(show_paths=False):
 
82
    """Generate text description of plugins.
 
83
 
 
84
    Includes both those that have loaded, and those that failed to 
 
85
    load.
 
86
 
 
87
    :param show_paths: If true,
 
88
    :returns: Iterator of text lines (including newlines.)
 
89
    """
 
90
    from inspect import getdoc
 
91
    loaded_plugins = plugins()
 
92
    all_names = sorted(list(set(
 
93
        loaded_plugins.keys() + plugin_warnings.keys())))
 
94
    for name in all_names:
 
95
        if name in loaded_plugins:
 
96
            plugin = loaded_plugins[name]
 
97
            version = plugin.__version__
 
98
            if version == 'unknown':
 
99
                version = ''
 
100
            yield '%s %s\n' % (name, version)
 
101
            d = getdoc(plugin.module)
 
102
            if d:
 
103
                doc = d.split('\n')[0]
 
104
            else:
 
105
                doc = '(no description)'
 
106
            yield ("  %s\n" % doc)
 
107
            if show_paths:
 
108
                yield ("   %s\n" % plugin.path())
 
109
            del plugin
 
110
        else:
 
111
            yield "%s (failed to load)\n" % name
 
112
        if name in plugin_warnings:
 
113
            for line in plugin_warnings[name]:
 
114
                yield "  ** " + line + '\n'
 
115
        yield '\n'
 
116
 
 
117
 
80
118
def _strip_trailing_sep(path):
81
119
    return path.rstrip("\\/")
82
120
 
101
139
        try:
102
140
            name, path = spec.split('@')
103
141
        except ValueError:
104
 
            raise errors.BzrCommandError(
105
 
                '"%s" is not a valid <plugin_name>@<plugin_path> description '
 
142
            raise errors.BzrCommandError(gettext(
 
143
                '"%s" is not a valid <plugin_name>@<plugin_path> description ')
106
144
                % spec)
107
145
        specs.append((name, path))
108
146
    return specs
236
274
    """Load bzrlib plugins.
237
275
 
238
276
    The environment variable BZR_PLUGIN_PATH is considered a delimited
239
 
    set of paths to look through. Each entry is searched for *.py
 
277
    set of paths to look through. Each entry is searched for `*.py`
240
278
    files (and whatever other extensions are used in the platform,
241
 
    such as *.pyd).
 
279
    such as `*.pyd`).
242
280
 
243
281
    load_from_path() provides the underlying mechanism and is called with
244
282
    the default directory list to provide the normal behaviour.
327
365
    return None, None, (None, None, None)
328
366
 
329
367
 
 
368
def record_plugin_warning(plugin_name, warning_message):
 
369
    trace.mutter(warning_message)
 
370
    plugin_warnings.setdefault(plugin_name, []).append(warning_message)
 
371
 
 
372
 
330
373
def _load_plugin_module(name, dir):
331
374
    """Load plugin name from dir.
332
375
 
340
383
    except KeyboardInterrupt:
341
384
        raise
342
385
    except errors.IncompatibleAPI, e:
343
 
        trace.warning("Unable to load plugin %r. It requested API version "
 
386
        warning_message = (
 
387
            "Unable to load plugin %r. It requested API version "
344
388
            "%s of module %s but the minimum exported version is %s, and "
345
389
            "the maximum is %s" %
346
390
            (name, e.wanted, e.api, e.minimum, e.current))
 
391
        record_plugin_warning(name, warning_message)
347
392
    except Exception, e:
348
393
        trace.warning("%s" % e)
349
394
        if re.search('\.|-| ', name):
354
399
                    "file path isn't a valid module name; try renaming "
355
400
                    "it to %r." % (name, dir, sanitised_name))
356
401
        else:
357
 
            trace.warning('Unable to load plugin %r from %r' % (name, dir))
 
402
            record_plugin_warning(
 
403
                name,
 
404
                'Unable to load plugin %r from %r' % (name, dir))
358
405
        trace.log_exception_quietly()
359
406
        if 'error' in debug.debug_flags:
360
407
            trace.print_exception(sys.exc_info(), sys.stderr)
400
447
    return result
401
448
 
402
449
 
 
450
def format_concise_plugin_list():
 
451
    """Return a string holding a concise list of plugins and their version.
 
452
    """
 
453
    items = []
 
454
    for name, a_plugin in sorted(plugins().items()):
 
455
        items.append("%s[%s]" %
 
456
            (name, a_plugin.__version__))
 
457
    return ', '.join(items)
 
458
 
 
459
 
 
460
 
403
461
class PluginsHelpIndex(object):
404
462
    """A help index that returns help topics for plugins."""
405
463
 
450
508
            result = self.module.__doc__
451
509
        if result[-1] != '\n':
452
510
            result += '\n'
453
 
        # there is code duplicated here and in bzrlib/help_topic.py's
454
 
        # matching Topic code. This should probably be factored in
455
 
        # to a helper function and a common base class.
456
 
        if additional_see_also is not None:
457
 
            see_also = sorted(set(additional_see_also))
458
 
        else:
459
 
            see_also = None
460
 
        if see_also:
461
 
            result += 'See also: '
462
 
            result += ', '.join(see_also)
463
 
            result += '\n'
 
511
        from bzrlib import help_topics
 
512
        result += help_topics._format_see_also(additional_see_also)
464
513
        return result
465
514
 
466
515
    def get_help_topic(self):
467
 
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
 
516
        """Return the module help topic: its basename."""
468
517
        return self.module.__name__[len('bzrlib.plugins.'):]
469
518
 
470
519
 
583
632
        return None
584
633
 
585
634
    def load_module(self, fullname):
586
 
        """Load a plugin from a specific directory."""
 
635
        """Load a plugin from a specific directory (or file)."""
587
636
        # We are called only for specific paths
588
637
        plugin_path = self.specific_paths[fullname]
589
638
        loading_path = None