/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
1
# Copyright (C) 2004, 2005, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
18
"""bzr python plugin support.
19
20
When load_plugins() is invoked, any python module in any directory in
21
$BZR_PLUGIN_PATH will be imported.  The module will be imported as
22
'bzrlib.plugins.$BASENAME(PLUGIN)'.  In the plugin's main body, it should
23
update any bzrlib registries it wants to extend; for example, to add new
24
commands, import bzrlib.commands and add your new command to the plugin_cmds
25
variable.
26
27
BZR_PLUGIN_PATH is also honoured for any plugins imported via
28
'import bzrlib.plugins.PLUGINNAME', as long as set_plugins_path has been 
29
called.
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
30
"""
31
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
32
import os
1185.16.82 by mbp at sourcefrog
- give a quieter warning if a plugin can't be loaded
33
import sys
1996.3.17 by John Arbash Meinel
lazy_import plugin and transport/local
34
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
37
import imp
2256.2.3 by Robert Collins
Review feedback.
38
import re
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
39
import types
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
40
import zipfile
1185.16.82 by mbp at sourcefrog
- give a quieter warning if a plugin can't be loaded
41
1996.3.17 by John Arbash Meinel
lazy_import plugin and transport/local
42
from bzrlib import (
43
    config,
44
    osutils,
45
    plugins,
46
    )
47
""")
48
49
from bzrlib.trace import mutter, warning, log_exception_quietly
50
51
52
DEFAULT_PLUGIN_PATH = None
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
53
_loaded = False
1996.3.17 by John Arbash Meinel
lazy_import plugin and transport/local
54
55
def get_default_plugin_path():
56
    """Get the DEFAULT_PLUGIN_PATH"""
57
    global DEFAULT_PLUGIN_PATH
58
    if DEFAULT_PLUGIN_PATH is None:
59
        DEFAULT_PLUGIN_PATH = osutils.pathjoin(config.config_dir(), 'plugins')
60
    return DEFAULT_PLUGIN_PATH
61
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
62
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
63
def all_plugins():
64
    """Return a dictionary of the plugins."""
65
    result = {}
1996.3.17 by John Arbash Meinel
lazy_import plugin and transport/local
66
    for name, plugin in plugins.__dict__.items():
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
67
        if isinstance(plugin, types.ModuleType):
68
            result[name] = plugin
69
    return result
70
71
1551.3.11 by Aaron Bentley
Merge from Robert
72
def disable_plugins():
73
    """Disable loading plugins.
74
75
    Future calls to load_plugins() will be ignored.
76
    """
77
    # TODO: jam 20060131 This should probably also disable
78
    #       load_from_dirs()
79
    global _loaded
80
    _loaded = True
81
2753.1.1 by Ian Clatworthy
(Blake Winton) BZR_PLUGIN_PATH should ignore trailiing slashes
82
def _strip_trailing_sep(path):
2652.2.6 by Blake Winton
Incorporate suggestions from Alexander Belchenko
83
    return path.rstrip("\\/")
1551.3.11 by Aaron Bentley
Merge from Robert
84
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
85
def set_plugins_path():
86
    """Set the path for plugins to be loaded from."""
87
    path = os.environ.get('BZR_PLUGIN_PATH',
2652.2.5 by Blake Winton
Get rid of CRs.
88
                          get_default_plugin_path()).split(os.pathsep)
89
    # Get rid of trailing slashes, since Python can't handle them when
2652.2.4 by Blake Winton
Add a note explaining why I strip the slashes twice.
90
    # it tries to import modules.
2753.1.1 by Ian Clatworthy
(Blake Winton) BZR_PLUGIN_PATH should ignore trailiing slashes
91
    path = map(_strip_trailing_sep, path)
2298.2.1 by jml at canonical
Make set_plugins_path search user-specified directories (BZR_PLUGIN_PATH
92
    # search the plugin path before the bzrlib installed dir
93
    path.append(os.path.dirname(plugins.__file__))
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
94
    plugins.__path__ = path
95
    return path
96
97
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
98
def load_plugins():
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
99
    """Load bzrlib plugins.
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
100
101
    The environment variable BZR_PLUGIN_PATH is considered a delimited
102
    set of paths to look through. Each entry is searched for *.py
103
    files (and whatever other extensions are used in the platform,
104
    such as *.pyd).
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
105
106
    load_from_dirs() provides the underlying mechanism and is called with
107
    the default directory list to provide the normal behaviour.
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
108
    """
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
109
    global _loaded
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
110
    if _loaded:
111
        # People can make sure plugins are loaded, they just won't be twice
112
        return
113
    _loaded = True
114
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
115
    # scan for all plugins in the path.
116
    load_from_path(set_plugins_path())
117
118
119
def load_from_path(dirs):
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
120
    """Load bzrlib plugins found in each dir in dirs.
121
122
    Loading a plugin means importing it into the python interpreter.
123
    The plugin is expected to make calls to register commands when
124
    it's loaded (or perhaps access other hooks in future.)
125
126
    Plugins are loaded into bzrlib.plugins.NAME, and can be found there
127
    for future reference.
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
128
129
    The python module path for bzrlib.plugins will be modified to be 'dirs'.
2652.2.5 by Blake Winton
Get rid of CRs.
130
    """
131
    # We need to strip the trailing separators here as well as in the
132
    # set_plugins_path function because calling code can pass anything in to
133
    # this function, and since it sets plugins.__path__, it should set it to
134
    # something that will be valid for Python to use (in case people try to
2652.2.4 by Blake Winton
Add a note explaining why I strip the slashes twice.
135
    # run "import bzrlib.plugins.PLUGINNAME" after calling this function).
2753.1.1 by Ian Clatworthy
(Blake Winton) BZR_PLUGIN_PATH should ignore trailiing slashes
136
    plugins.__path__ = map(_strip_trailing_sep, dirs)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
137
    for d in dirs:
138
        if not d:
139
            continue
140
        mutter('looking for plugins in %s', d)
141
        if os.path.isdir(d):
142
            load_from_dir(d)
143
        else:
144
            # it might be a zip: try loading from the zip.
145
            load_from_zip(d)
146
            continue
147
148
149
# backwards compatability: load_from_dirs was the old name
150
# This was changed in 0.15
151
load_from_dirs = load_from_path
152
153
154
def load_from_dir(d):
155
    """Load the plugins in directory d."""
1864.6.1 by John Arbash Meinel
Use the correct suffixes for loading plugins (bug #51810)
156
    # Get the list of valid python suffixes for __init__.py?
157
    # this includes .py, .pyc, and .pyo (depending on if we are running -O)
158
    # but it doesn't include compiled modules (.so, .dll, etc)
159
    valid_suffixes = [suffix for suffix, mod_type, flags in imp.get_suffixes()
160
                              if flags in (imp.PY_SOURCE, imp.PY_COMPILED)]
161
    package_entries = ['__init__'+suffix for suffix in valid_suffixes]
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
162
    plugin_names = set()
163
    for f in os.listdir(d):
164
        path = osutils.pathjoin(d, f)
165
        if os.path.isdir(path):
166
            for entry in package_entries:
167
                # This directory should be a package, and thus added to
168
                # the list
169
                if os.path.isfile(osutils.pathjoin(path, entry)):
170
                    break
171
            else: # This directory is not a package
172
                continue
173
        else:
174
            for suffix_info in imp.get_suffixes():
175
                if f.endswith(suffix_info[0]):
176
                    f = f[:-len(suffix_info[0])]
177
                    if suffix_info[2] == imp.C_EXTENSION and f.endswith('module'):
178
                        f = f[:-len('module')]
179
                    break
180
            else:
181
                continue
182
        if getattr(plugins, f, None):
183
            mutter('Plugin name %s already loaded', f)
184
        else:
185
            # mutter('add plugin name %s', f)
186
            plugin_names.add(f)
187
    
188
    for name in plugin_names:
189
        try:
2256.2.3 by Robert Collins
Review feedback.
190
            exec "import bzrlib.plugins.%s" % name in {}
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
191
        except KeyboardInterrupt:
192
            raise
193
        except Exception, e:
194
            ## import pdb; pdb.set_trace()
2256.2.3 by Robert Collins
Review feedback.
195
            if re.search('\.|-| ', name):
196
                warning('Unable to load plugin %r from %r: '
197
                    'It is not a valid python module name.' % (name, d))
198
            else:
199
                warning('Unable to load plugin %r from %r' % (name, d))
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
200
            log_exception_quietly()
201
202
203
def load_from_zip(zip_name):
204
    """Load all the plugins in a zip."""
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
205
    valid_suffixes = ('.py', '.pyc', '.pyo')    # only python modules/packages
206
                                                # is allowed
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
207
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
208
    try:
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
209
        index = zip_name.rindex('.zip')
210
    except ValueError:
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
211
        return
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
212
    archive = zip_name[:index+4]
213
    prefix = zip_name[index+5:]
214
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
215
    mutter('Looking for plugins in %r', zip_name)
216
217
    # use zipfile to get list of files/dirs inside zip
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
218
    try:
219
        z = zipfile.ZipFile(archive)
220
        namelist = z.namelist()
221
        z.close()
222
    except zipfile.error:
223
        # not a valid zip
224
        return
225
226
    if prefix:
227
        prefix = prefix.replace('\\','/')
228
        if prefix[-1] != '/':
229
            prefix += '/'
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
230
        ix = len(prefix)
231
        namelist = [name[ix:]
232
                    for name in namelist
233
                    if name.startswith(prefix)]
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
234
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
235
    mutter('Names in archive: %r', namelist)
236
    
237
    for name in namelist:
238
        if not name or name.endswith('/'):
239
            continue
240
    
241
        # '/' is used to separate pathname components inside zip archives
242
        ix = name.rfind('/')
243
        if ix == -1:
244
            head, tail = '', name
245
        else:
246
            head, tail = name.rsplit('/',1)
247
        if '/' in head:
248
            # we don't need looking in subdirectories
249
            continue
250
    
251
        base, suffix = osutils.splitext(tail)
252
        if suffix not in valid_suffixes:
253
            continue
254
    
255
        if base == '__init__':
256
            # package
257
            plugin_name = head
258
        elif head == '':
259
            # module
260
            plugin_name = base
261
        else:
262
            continue
263
    
264
        if not plugin_name:
265
            continue
266
        if getattr(plugins, plugin_name, None):
267
            mutter('Plugin name %s already loaded', plugin_name)
268
            continue
269
    
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
270
        try:
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
271
            exec "import bzrlib.plugins.%s" % plugin_name in {}
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
272
            mutter('Load plugin %s from zip %r', plugin_name, zip_name)
273
        except KeyboardInterrupt:
274
            raise
275
        except Exception, e:
276
            ## import pdb; pdb.set_trace()
277
            warning('Unable to load plugin %r from %r'
278
                    % (name, zip_name))
279
            log_exception_quietly()
2432.1.24 by Robert Collins
Add plugins as a help index.
280
281
282
class PluginsHelpIndex(object):
283
    """A help index that returns help topics for plugins."""
284
285
    def __init__(self):
286
        self.prefix = 'plugins/'
287
288
    def get_topics(self, topic):
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
289
        """Search for topic in the loaded plugins.
290
291
        This will not trigger loading of new plugins.
292
293
        :param topic: A topic to search for.
294
        :return: A list which is either empty or contains a single
295
            RegisteredTopic entry.
296
        """
297
        if not topic:
298
            return []
299
        if topic.startswith(self.prefix):
300
            topic = topic[len(self.prefix):]
301
        plugin_module_name = 'bzrlib.plugins.%s' % topic
302
        try:
303
            module = sys.modules[plugin_module_name]
304
        except KeyError:
305
            return []
306
        else:
307
            return [ModuleHelpTopic(module)]
308
309
310
class ModuleHelpTopic(object):
311
    """A help topic which returns the docstring for a module."""
312
313
    def __init__(self, module):
314
        """Constructor.
315
316
        :param module: The module for which help should be generated.
317
        """
318
        self.module = module
319
320
    def get_help_text(self, additional_see_also=None):
321
        """Return a string with the help for this topic.
322
323
        :param additional_see_also: Additional help topics to be
324
            cross-referenced.
325
        """
326
        if not self.module.__doc__:
327
            result = "Plugin '%s' has no docstring.\n" % self.module.__name__
328
        else:
329
            result = self.module.__doc__
330
        if result[-1] != '\n':
331
            result += '\n'
332
        # there is code duplicated here and in bzrlib/help_topic.py's 
333
        # matching Topic code. This should probably be factored in
334
        # to a helper function and a common base class.
335
        if additional_see_also is not None:
336
            see_also = sorted(set(additional_see_also))
337
        else:
338
            see_also = None
339
        if see_also:
340
            result += 'See also: '
341
            result += ', '.join(see_also)
342
            result += '\n'
343
        return result
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
344
345
    def get_help_topic(self):
2432.1.30 by Robert Collins
Fix the ModuleHelpTopic get_help_topic to be tested with closer to real world data and strip the bzrlib.plugins. prefix from the name.
346
        """Return the modules help topic - its __name__ after bzrlib.plugins.."""
347
        return self.module.__name__[len('bzrlib.plugins.'):]