~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugin.py

  • Committer: John Arbash Meinel
  • Date: 2011-05-11 11:35:28 UTC
  • mto: This revision was merged to the branch mainline in revision 5851.
  • Revision ID: john@arbash-meinel.com-20110511113528-qepibuwxicjrbb2h
Break compatibility with python <2.6.

This includes auditing the code for places where we were doing
explicit 'sys.version' checks and removing them as appropriate.

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
36
36
from bzrlib import osutils
37
37
 
38
38
from bzrlib.lazy_import import lazy_import
39
 
 
40
39
lazy_import(globals(), """
41
40
import imp
42
41
import re
52
51
from bzrlib import plugins as _mod_plugins
53
52
""")
54
53
 
55
 
from bzrlib.symbol_versioning import (
56
 
    deprecated_function,
57
 
    deprecated_in,
58
 
    )
59
 
 
60
54
 
61
55
DEFAULT_PLUGIN_PATH = None
62
56
_loaded = False
63
57
_plugins_disabled = False
64
58
 
65
59
 
 
60
plugin_warnings = {}
 
61
# Map from plugin name, to list of string warnings about eg plugin
 
62
# dependencies.
 
63
 
 
64
 
66
65
def are_plugins_disabled():
67
66
    return _plugins_disabled
68
67
 
77
76
    load_plugins([])
78
77
 
79
78
 
 
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
 
80
116
def _strip_trailing_sep(path):
81
117
    return path.rstrip("\\/")
82
118
 
83
119
 
 
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
 
84
147
def set_plugins_path(path=None):
85
148
    """Set the path for plugins to be loaded from.
86
149
 
98
161
        for name in disabled_plugins.split(os.pathsep):
99
162
            PluginImporter.blacklist.add('bzrlib.plugins.' + name)
100
163
    # Set up a the specific paths for plugins
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('@')
 
164
    for plugin_name, plugin_path in _get_specific_plugin_paths(os.environ.get(
 
165
            'BZR_PLUGINS_AT', None)):
105
166
            PluginImporter.specific_paths[
106
167
                'bzrlib.plugins.%s' % plugin_name] = plugin_path
107
168
    return path
302
363
    return None, None, (None, None, None)
303
364
 
304
365
 
 
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
 
305
371
def _load_plugin_module(name, dir):
306
372
    """Load plugin name from dir.
307
373
 
315
381
    except KeyboardInterrupt:
316
382
        raise
317
383
    except errors.IncompatibleAPI, e:
318
 
        trace.warning("Unable to load plugin %r. It requested API version "
 
384
        warning_message = (
 
385
            "Unable to load plugin %r. It requested API version "
319
386
            "%s of module %s but the minimum exported version is %s, and "
320
387
            "the maximum is %s" %
321
388
            (name, e.wanted, e.api, e.minimum, e.current))
 
389
        record_plugin_warning(name, warning_message)
322
390
    except Exception, e:
323
391
        trace.warning("%s" % e)
324
392
        if re.search('\.|-| ', name):
329
397
                    "file path isn't a valid module name; try renaming "
330
398
                    "it to %r." % (name, dir, sanitised_name))
331
399
        else:
332
 
            trace.warning('Unable to load plugin %r from %r' % (name, dir))
 
400
            record_plugin_warning(
 
401
                name,
 
402
                'Unable to load plugin %r from %r' % (name, dir))
333
403
        trace.log_exception_quietly()
334
404
        if 'error' in debug.debug_flags:
335
405
            trace.print_exception(sys.exc_info(), sys.stderr)
375
445
    return result
376
446
 
377
447
 
 
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
 
378
459
class PluginsHelpIndex(object):
379
460
    """A help index that returns help topics for plugins."""
380
461
 
562
643
        # We are called only for specific paths
563
644
        plugin_path = self.specific_paths[fullname]
564
645
        loading_path = None
565
 
        package = False
566
646
        if os.path.isdir(plugin_path):
567
647
            for suffix, mode, kind in imp.get_suffixes():
568
648
                if kind not in (imp.PY_SOURCE, imp.PY_COMPILED):
570
650
                    continue
571
651
                init_path = osutils.pathjoin(plugin_path, '__init__' + suffix)
572
652
                if os.path.isfile(init_path):
573
 
                    loading_path = init_path
574
 
                    package = True
 
653
                    # We've got a module here and load_module needs specific
 
654
                    # parameters.
 
655
                    loading_path = plugin_path
 
656
                    suffix = ''
 
657
                    mode = ''
 
658
                    kind = imp.PKG_DIRECTORY
575
659
                    break
576
660
        else:
577
661
            for suffix, mode, kind in imp.get_suffixes():
581
665
        if loading_path is None:
582
666
            raise ImportError('%s cannot be loaded from %s'
583
667
                              % (fullname, plugin_path))
584
 
        f = open(loading_path, mode)
 
668
        if kind is imp.PKG_DIRECTORY:
 
669
            f = None
 
670
        else:
 
671
            f = open(loading_path, mode)
585
672
        try:
586
673
            mod = imp.load_module(fullname, f, loading_path,
587
674
                                  (suffix, mode, kind))
588
 
            if package:
589
 
                # The plugin can contain modules, so be ready
590
 
                mod.__path__ = [plugin_path]
591
675
            mod.__package__ = fullname
592
676
            return mod
593
677
        finally:
594
 
            f.close()
 
678
            if f is not None:
 
679
                f.close()
595
680
 
596
681
 
597
682
# Install a dedicated importer for plugins requiring special handling