~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 08:15:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101008081514-dviqzrdfwyzsqbz2
Split NEWS into per-release doc/en/release-notes/bzr-*.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
 
17
18
"""bzr python plugin support.
18
19
 
19
20
When load_plugins() is invoked, any python module in any directory in
29
30
called.
30
31
"""
31
32
 
32
 
from __future__ import absolute_import
33
 
 
34
33
import os
35
34
import sys
36
35
 
37
36
from bzrlib import osutils
38
37
 
39
38
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
53
52
from bzrlib import plugins as _mod_plugins
54
53
""")
55
54
 
 
55
from bzrlib.symbol_versioning import (
 
56
    deprecated_function,
 
57
    deprecated_in,
 
58
    )
 
59
 
56
60
 
57
61
DEFAULT_PLUGIN_PATH = None
58
62
_loaded = False
59
63
_plugins_disabled = False
60
64
 
61
65
 
62
 
plugin_warnings = {}
63
 
# Map from plugin name, to list of string warnings about eg plugin
64
 
# dependencies.
65
 
 
66
 
 
67
66
def are_plugins_disabled():
68
67
    return _plugins_disabled
69
68
 
78
77
    load_plugins([])
79
78
 
80
79
 
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
 
 
118
80
def _strip_trailing_sep(path):
119
81
    return path.rstrip("\\/")
120
82
 
139
101
        try:
140
102
            name, path = spec.split('@')
141
103
        except ValueError:
142
 
            raise errors.BzrCommandError(gettext(
143
 
                '"%s" is not a valid <plugin_name>@<plugin_path> description ')
 
104
            raise errors.BzrCommandError(
 
105
                '"%s" is not a valid <plugin_name>@<plugin_path> description '
144
106
                % spec)
145
107
        specs.append((name, path))
146
108
    return specs
274
236
    """Load bzrlib plugins.
275
237
 
276
238
    The environment variable BZR_PLUGIN_PATH is considered a delimited
277
 
    set of paths to look through. Each entry is searched for `*.py`
 
239
    set of paths to look through. Each entry is searched for *.py
278
240
    files (and whatever other extensions are used in the platform,
279
 
    such as `*.pyd`).
 
241
    such as *.pyd).
280
242
 
281
243
    load_from_path() provides the underlying mechanism and is called with
282
244
    the default directory list to provide the normal behaviour.
365
327
    return None, None, (None, None, None)
366
328
 
367
329
 
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
 
 
373
330
def _load_plugin_module(name, dir):
374
331
    """Load plugin name from dir.
375
332
 
383
340
    except KeyboardInterrupt:
384
341
        raise
385
342
    except errors.IncompatibleAPI, e:
386
 
        warning_message = (
387
 
            "Unable to load plugin %r. It requested API version "
 
343
        trace.warning("Unable to load plugin %r. It requested API version "
388
344
            "%s of module %s but the minimum exported version is %s, and "
389
345
            "the maximum is %s" %
390
346
            (name, e.wanted, e.api, e.minimum, e.current))
391
 
        record_plugin_warning(name, warning_message)
392
347
    except Exception, e:
393
348
        trace.warning("%s" % e)
394
349
        if re.search('\.|-| ', name):
399
354
                    "file path isn't a valid module name; try renaming "
400
355
                    "it to %r." % (name, dir, sanitised_name))
401
356
        else:
402
 
            record_plugin_warning(
403
 
                name,
404
 
                'Unable to load plugin %r from %r' % (name, dir))
 
357
            trace.warning('Unable to load plugin %r from %r' % (name, dir))
405
358
        trace.log_exception_quietly()
406
359
        if 'error' in debug.debug_flags:
407
360
            trace.print_exception(sys.exc_info(), sys.stderr)
447
400
    return result
448
401
 
449
402
 
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
 
 
461
403
class PluginsHelpIndex(object):
462
404
    """A help index that returns help topics for plugins."""
463
405
 
508
450
            result = self.module.__doc__
509
451
        if result[-1] != '\n':
510
452
            result += '\n'
511
 
        from bzrlib import help_topics
512
 
        result += help_topics._format_see_also(additional_see_also)
 
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'
513
464
        return result
514
465
 
515
466
    def get_help_topic(self):
516
 
        """Return the module help topic: its basename."""
 
467
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
517
468
        return self.module.__name__[len('bzrlib.plugins.'):]
518
469
 
519
470
 
632
583
        return None
633
584
 
634
585
    def load_module(self, fullname):
635
 
        """Load a plugin from a specific directory (or file)."""
 
586
        """Load a plugin from a specific directory."""
636
587
        # We are called only for specific paths
637
588
        plugin_path = self.specific_paths[fullname]
638
589
        loading_path = None