/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

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
36
36
from bzrlib import osutils
37
37
 
38
38
from bzrlib.lazy_import import lazy_import
 
39
 
39
40
lazy_import(globals(), """
40
41
import imp
41
42
import re
51
52
from bzrlib import plugins as _mod_plugins
52
53
""")
53
54
 
 
55
from bzrlib.symbol_versioning import (
 
56
    deprecated_function,
 
57
    deprecated_in,
 
58
    )
 
59
 
54
60
 
55
61
DEFAULT_PLUGIN_PATH = None
56
62
_loaded = False
57
63
_plugins_disabled = False
58
64
 
59
65
 
60
 
plugin_warnings = {}
61
 
# Map from plugin name, to list of string warnings about eg plugin
62
 
# dependencies.
63
 
 
64
 
 
65
66
def are_plugins_disabled():
66
67
    return _plugins_disabled
67
68
 
76
77
    load_plugins([])
77
78
 
78
79
 
79
 
def describe_plugins(show_paths=False):
80
 
    """Generate text description of plugins.
81
 
 
82
 
    Includes both those that have loaded, and those that failed to 
83
 
    load.
84
 
 
85
 
    :param show_paths: If true,
86
 
    :returns: Iterator of text lines (including newlines.)
87
 
    """
88
 
    from inspect import getdoc
89
 
    loaded_plugins = plugins()
90
 
    all_names = sorted(list(set(
91
 
        loaded_plugins.keys() + plugin_warnings.keys())))
92
 
    for name in all_names:
93
 
        if name in loaded_plugins:
94
 
            plugin = loaded_plugins[name]
95
 
            version = plugin.__version__
96
 
            if version == 'unknown':
97
 
                version = ''
98
 
            yield '%s %s\n' % (name, version)
99
 
            d = getdoc(plugin.module)
100
 
            if d:
101
 
                doc = d.split('\n')[0]
102
 
            else:
103
 
                doc = '(no description)'
104
 
            yield ("  %s\n" % doc)
105
 
            if show_paths:
106
 
                yield ("   %s\n" % plugin.path())
107
 
            del plugin
108
 
        else:
109
 
            yield "%s (failed to load)\n" % name
110
 
        if name in plugin_warnings:
111
 
            for line in plugin_warnings[name]:
112
 
                yield "  ** " + line + '\n'
113
 
        yield '\n'
114
 
 
115
 
 
116
80
def _strip_trailing_sep(path):
117
81
    return path.rstrip("\\/")
118
82
 
119
83
 
120
 
def _get_specific_plugin_paths(paths):
121
 
    """Returns the plugin paths from a string describing the associations.
122
 
 
123
 
    :param paths: A string describing the paths associated with the plugins.
124
 
 
125
 
    :returns: A list of (plugin name, path) tuples.
126
 
 
127
 
    For example, if paths is my_plugin@/test/my-test:her_plugin@/production/her,
128
 
    [('my_plugin', '/test/my-test'), ('her_plugin', '/production/her')] 
129
 
    will be returned.
130
 
 
131
 
    Note that ':' in the example above depends on the os.
132
 
    """
133
 
    if not paths:
134
 
        return []
135
 
    specs = []
136
 
    for spec in paths.split(os.pathsep):
137
 
        try:
138
 
            name, path = spec.split('@')
139
 
        except ValueError:
140
 
            raise errors.BzrCommandError(
141
 
                '"%s" is not a valid <plugin_name>@<plugin_path> description '
142
 
                % spec)
143
 
        specs.append((name, path))
144
 
    return specs
145
 
 
146
 
 
147
84
def set_plugins_path(path=None):
148
85
    """Set the path for plugins to be loaded from.
149
86
 
161
98
        for name in disabled_plugins.split(os.pathsep):
162
99
            PluginImporter.blacklist.add('bzrlib.plugins.' + name)
163
100
    # Set up a the specific paths for plugins
164
 
    for plugin_name, plugin_path in _get_specific_plugin_paths(os.environ.get(
165
 
            'BZR_PLUGINS_AT', None)):
 
101
    specific_plugins = os.environ.get('BZR_PLUGINS_AT', None)
 
102
    if specific_plugins is not None:
 
103
        for spec in specific_plugins.split(os.pathsep):
 
104
            plugin_name, plugin_path = spec.split('@')
166
105
            PluginImporter.specific_paths[
167
106
                'bzrlib.plugins.%s' % plugin_name] = plugin_path
168
107
    return path
272
211
    """Load bzrlib plugins.
273
212
 
274
213
    The environment variable BZR_PLUGIN_PATH is considered a delimited
275
 
    set of paths to look through. Each entry is searched for `*.py`
 
214
    set of paths to look through. Each entry is searched for *.py
276
215
    files (and whatever other extensions are used in the platform,
277
 
    such as `*.pyd`).
 
216
    such as *.pyd).
278
217
 
279
218
    load_from_path() provides the underlying mechanism and is called with
280
219
    the default directory list to provide the normal behaviour.
363
302
    return None, None, (None, None, None)
364
303
 
365
304
 
366
 
def record_plugin_warning(plugin_name, warning_message):
367
 
    trace.mutter(warning_message)
368
 
    plugin_warnings.setdefault(plugin_name, []).append(warning_message)
369
 
 
370
 
 
371
305
def _load_plugin_module(name, dir):
372
306
    """Load plugin name from dir.
373
307
 
381
315
    except KeyboardInterrupt:
382
316
        raise
383
317
    except errors.IncompatibleAPI, e:
384
 
        warning_message = (
385
 
            "Unable to load plugin %r. It requested API version "
 
318
        trace.warning("Unable to load plugin %r. It requested API version "
386
319
            "%s of module %s but the minimum exported version is %s, and "
387
320
            "the maximum is %s" %
388
321
            (name, e.wanted, e.api, e.minimum, e.current))
389
 
        record_plugin_warning(name, warning_message)
390
322
    except Exception, e:
391
323
        trace.warning("%s" % e)
392
324
        if re.search('\.|-| ', name):
397
329
                    "file path isn't a valid module name; try renaming "
398
330
                    "it to %r." % (name, dir, sanitised_name))
399
331
        else:
400
 
            record_plugin_warning(
401
 
                name,
402
 
                'Unable to load plugin %r from %r' % (name, dir))
 
332
            trace.warning('Unable to load plugin %r from %r' % (name, dir))
403
333
        trace.log_exception_quietly()
404
334
        if 'error' in debug.debug_flags:
405
335
            trace.print_exception(sys.exc_info(), sys.stderr)
445
375
    return result
446
376
 
447
377
 
448
 
def format_concise_plugin_list():
449
 
    """Return a string holding a concise list of plugins and their version.
450
 
    """
451
 
    items = []
452
 
    for name, a_plugin in sorted(plugins().items()):
453
 
        items.append("%s[%s]" %
454
 
            (name, a_plugin.__version__))
455
 
    return ', '.join(items)
456
 
 
457
 
 
458
 
 
459
378
class PluginsHelpIndex(object):
460
379
    """A help index that returns help topics for plugins."""
461
380
 
506
425
            result = self.module.__doc__
507
426
        if result[-1] != '\n':
508
427
            result += '\n'
509
 
        from bzrlib import help_topics
510
 
        result += help_topics._format_see_also(additional_see_also)
 
428
        # there is code duplicated here and in bzrlib/help_topic.py's
 
429
        # matching Topic code. This should probably be factored in
 
430
        # to a helper function and a common base class.
 
431
        if additional_see_also is not None:
 
432
            see_also = sorted(set(additional_see_also))
 
433
        else:
 
434
            see_also = None
 
435
        if see_also:
 
436
            result += 'See also: '
 
437
            result += ', '.join(see_also)
 
438
            result += '\n'
511
439
        return result
512
440
 
513
441
    def get_help_topic(self):
514
 
        """Return the module help topic: its basename."""
 
442
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
515
443
        return self.module.__name__[len('bzrlib.plugins.'):]
516
444
 
517
445
 
634
562
        # We are called only for specific paths
635
563
        plugin_path = self.specific_paths[fullname]
636
564
        loading_path = None
 
565
        package = False
637
566
        if os.path.isdir(plugin_path):
638
567
            for suffix, mode, kind in imp.get_suffixes():
639
568
                if kind not in (imp.PY_SOURCE, imp.PY_COMPILED):
641
570
                    continue
642
571
                init_path = osutils.pathjoin(plugin_path, '__init__' + suffix)
643
572
                if os.path.isfile(init_path):
644
 
                    # We've got a module here and load_module needs specific
645
 
                    # parameters.
646
 
                    loading_path = plugin_path
647
 
                    suffix = ''
648
 
                    mode = ''
649
 
                    kind = imp.PKG_DIRECTORY
 
573
                    loading_path = init_path
 
574
                    package = True
650
575
                    break
651
576
        else:
652
577
            for suffix, mode, kind in imp.get_suffixes():
656
581
        if loading_path is None:
657
582
            raise ImportError('%s cannot be loaded from %s'
658
583
                              % (fullname, plugin_path))
659
 
        if kind is imp.PKG_DIRECTORY:
660
 
            f = None
661
 
        else:
662
 
            f = open(loading_path, mode)
 
584
        f = open(loading_path, mode)
663
585
        try:
664
586
            mod = imp.load_module(fullname, f, loading_path,
665
587
                                  (suffix, mode, kind))
 
588
            if package:
 
589
                # The plugin can contain modules, so be ready
 
590
                mod.__path__ = [plugin_path]
666
591
            mod.__package__ = fullname
667
592
            return mod
668
593
        finally:
669
 
            if f is not None:
670
 
                f.close()
 
594
            f.close()
671
595
 
672
596
 
673
597
# Install a dedicated importer for plugins requiring special handling