bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 
737
by Martin Pool
 - add plugin patch, still being integrated  | 
1  | 
*** added file 'bzrlib/plugin.py'
 | 
2  | 
--- /dev/null
 | 
|
3  | 
+++ bzrlib/plugin.py
 | 
|
4  | 
@@ -0,0 +1,92 @@
 | 
|
5  | 
+# Copyright (C) 2004, 2005 by Canonical Ltd
 | 
|
6  | 
+
 | 
|
7  | 
+# This program is free software; you can redistribute it and/or modify
 | 
|
8  | 
+# it under the terms of the GNU General Public License as published by
 | 
|
9  | 
+# the Free Software Foundation; either version 2 of the License, or
 | 
|
10  | 
+# (at your option) any later version.
 | 
|
11  | 
+
 | 
|
12  | 
+# This program is distributed in the hope that it will be useful,
 | 
|
13  | 
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|
14  | 
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|
15  | 
+# GNU General Public License for more details.
 | 
|
16  | 
+
 | 
|
17  | 
+# You should have received a copy of the GNU General Public License
 | 
|
18  | 
+# along with this program; if not, write to the Free Software
 | 
|
19  | 
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
|
20  | 
+
 | 
|
21  | 
+
 | 
|
22  | 
+# This module implements plug-in support.
 | 
|
23  | 
+# Any python module in $BZR_PLUGIN_PATH will be imported upon initialization
 | 
|
24  | 
+# of bzrlib (and then forgotten about).  In the plugin's main body, it should
 | 
|
25  | 
+# update any bzrlib registries it wants to extend; for example, to add new
 | 
|
26  | 
+# commands, import bzrlib.commands and add your new command to the
 | 
|
27  | 
+# plugin_cmds variable.
 | 
|
28  | 
+
 | 
|
29  | 
+import sys, os, imp
 | 
|
30  | 
+try:
 | 
|
31  | 
+    set
 | 
|
32  | 
+except NameError:
 | 
|
33  | 
+    from sets import Set as set
 | 
|
34  | 
+from bzrlib.trace import log_error
 | 
|
35  | 
+
 | 
|
36  | 
+
 | 
|
37  | 
+def load_plugins():
 | 
|
38  | 
+    """Find all python files which are plugins, and load them
 | 
|
39  | 
+
 | 
|
40  | 
+    The environment variable BZR_PLUGIN_PATH is considered a delimited set of
 | 
|
41  | 
+    paths to look through. Each entry is searched for *.py files (and whatever
 | 
|
42  | 
+    other extensions are used in the platform, such as *.pyd).
 | 
|
43  | 
+    """
 | 
|
44  | 
+    bzrpath = os.environ.get('BZR_PLUGIN_PATH', os.path.expanduser('~/.bzr/plugins'))
 | 
|
45  | 
+
 | 
|
46  | 
+    # The problem with imp.get_suffixes() is that it doesn't include
 | 
|
47  | 
+    # .pyo which is technically valid
 | 
|
48  | 
+    # It also means that "testmodule.so" will show up as both test and testmodule
 | 
|
49  | 
+    # though it is only valid as 'test'
 | 
|
50  | 
+    # but you should be careful, because "testmodule.py" loads as testmodule.
 | 
|
51  | 
+    suffixes = imp.get_suffixes()
 | 
|
52  | 
+    suffixes.append(('.pyo', 'rb', imp.PY_COMPILED))
 | 
|
53  | 
+    package_entries = ['__init__.py', '__init__.pyc', '__init__.pyo']
 | 
|
54  | 
+    for d in bzrpath.split(os.pathsep):
 | 
|
55  | 
+        # going trough them one by one allows different plugins with the same
 | 
|
56  | 
+        # filename in different directories in the path
 | 
|
57  | 
+        if not d:
 | 
|
58  | 
+            continue
 | 
|
59  | 
+        plugin_names = set()
 | 
|
60  | 
+        if not os.path.isdir(d):
 | 
|
61  | 
+            continue
 | 
|
62  | 
+        for f in os.listdir(d):
 | 
|
63  | 
+            path = os.path.join(d, f)
 | 
|
64  | 
+            if os.path.isdir(path):
 | 
|
65  | 
+                for entry in package_entries:
 | 
|
66  | 
+                    # This directory should be a package, and thus added to
 | 
|
67  | 
+                    # the list
 | 
|
68  | 
+                    if os.path.isfile(os.path.join(path, entry)):
 | 
|
69  | 
+                        break
 | 
|
70  | 
+                else: # This directory is not a package
 | 
|
71  | 
+                    continue
 | 
|
72  | 
+            else:
 | 
|
73  | 
+                for suffix_info in suffixes:
 | 
|
74  | 
+                    if f.endswith(suffix_info[0]):
 | 
|
75  | 
+                        f = f[:-len(suffix_info[0])]
 | 
|
76  | 
+                        if suffix_info[2] == imp.C_EXTENSION and f.endswith('module'):
 | 
|
77  | 
+                            f = f[:-len('module')]
 | 
|
78  | 
+                        break
 | 
|
79  | 
+                else:
 | 
|
80  | 
+                    continue
 | 
|
81  | 
+            plugin_names.add(f)
 | 
|
82  | 
+
 | 
|
83  | 
+        plugin_names = list(plugin_names)
 | 
|
84  | 
+        plugin_names.sort()
 | 
|
85  | 
+        for name in plugin_names:
 | 
|
86  | 
+            try:
 | 
|
87  | 
+                plugin_info = imp.find_module(name, [d])
 | 
|
88  | 
+                try:
 | 
|
89  | 
+                    plugin = imp.load_module('bzrlib.plugin.' + name,
 | 
|
90  | 
+                                             *plugin_info)
 | 
|
91  | 
+                finally:
 | 
|
92  | 
+                    if plugin_info[0] is not None:
 | 
|
93  | 
+                        plugin_info[0].close()
 | 
|
94  | 
+            except Exception, e:
 | 
|
95  | 
+                log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
 | 
|
96  | 
+
 | 
|
97  | 
||
98  | 
*** modified file 'bzrlib/__init__.py'
 | 
|
99  | 
--- bzrlib/__init__.py
 | 
|
100  | 
+++ bzrlib/__init__.py
 | 
|
101  | 
@@ -23,6 +23,7@@
 | 
|
102  | 
 from diff import compare_trees
 | 
|
103  | 
 from trace import mutter, warning, open_tracefile
 | 
|
104  | 
 from log import show_log
 | 
|
105  | 
+from plugin import load_plugins
 | 
|
106  | 
 import add
 | 
|
107  | 
||
108  | 
 BZRDIR = ".bzr"
 | 
|
109  | 
@@ -62,4 +63,4 @@
 | 
|
110  | 
             return None
 | 
|
111  | 
     except BzrError:
 | 
|
112  | 
         return None
 | 
|
113  | 
-
 | 
|
114  | 
+
 | 
|
115  | 
||
116  | 
*** modified file 'bzrlib/commands.py'
 | 
|
117  | 
--- bzrlib/commands.py
 | 
|
118  | 
+++ bzrlib/commands.py
 | 
|
119  | 
@@ -24,6 +24,24 @@
 | 
|
120  | 
 from bzrlib.osutils import quotefn
 | 
|
121  | 
 from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
 | 
|
122  | 
      format_date
 | 
|
123  | 
+
 | 
|
124  | 
+
 | 
|
125  | 
+plugin_cmds = {}
 | 
|
126  | 
+
 | 
|
127  | 
+
 | 
|
128  | 
+def register_plugin_command(cmd):
 | 
|
129  | 
+    "Utility function to help register a command"
 | 
|
130  | 
+    global plugin_cmds
 | 
|
131  | 
+    k = cmd.__name__
 | 
|
132  | 
+    if k.startswith("cmd_"):
 | 
|
133  | 
+        k_unsquished = _unsquish_command_name(k)
 | 
|
134  | 
+    else:
 | 
|
135  | 
+        k_unsquished = k
 | 
|
136  | 
+    if not plugin_cmds.has_key(k_unsquished):
 | 
|
137  | 
+        plugin_cmds[k_unsquished] = cmd
 | 
|
138  | 
+    else:
 | 
|
139  | 
+        log_error('Two plugins defined the same command: %r' % k)
 | 
|
140  | 
+        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 | 
|
141  | 
||
142  | 
||
143  | 
 def _squish_command_name(cmd):
 | 
|
144  | 
@@ -68,100 +86,34 @@
 | 
|
145  | 
         revs = int(revstr)
 | 
|
146  | 
     return revs
 | 
|
147  | 
||
148  | 
-def _find_plugins():
 | 
|
149  | 
-    """Find all python files which are plugins, and load their commands
 | 
|
150  | 
-    to add to the list of "all commands"
 | 
|
151  | 
-
 | 
|
152  | 
-    The environment variable BZRPATH is considered a delimited set of
 | 
|
153  | 
-    paths to look through. Each entry is searched for *.py files.
 | 
|
154  | 
-    If a directory is found, it is also searched, but they are
 | 
|
155  | 
-    not searched recursively. This allows you to revctl the plugins.
 | 
|
156  | 
-
 | 
|
157  | 
-    Inside the plugin should be a series of cmd_* function, which inherit from
 | 
|
158  | 
-    the bzrlib.commands.Command class.
 | 
|
159  | 
-    """
 | 
|
160  | 
-    bzrpath = os.environ.get('BZRPLUGINPATH', '')
 | 
|
161  | 
-
 | 
|
162  | 
-    plugin_cmds = {}
 | 
|
163  | 
-    if not bzrpath:
 | 
|
164  | 
-        return plugin_cmds
 | 
|
165  | 
-    _platform_extensions = {
 | 
|
166  | 
-        'win32':'.pyd',
 | 
|
167  | 
-        'cygwin':'.dll',
 | 
|
168  | 
-        'darwin':'.dylib',
 | 
|
169  | 
-        'linux2':'.so'
 | 
|
170  | 
-        }
 | 
|
171  | 
-    if _platform_extensions.has_key(sys.platform):
 | 
|
172  | 
-        platform_extension = _platform_extensions[sys.platform]
 | 
|
173  | 
-    else:
 | 
|
174  | 
-        platform_extension = None
 | 
|
175  | 
-    for d in bzrpath.split(os.pathsep):
 | 
|
176  | 
-        plugin_names = {} # This should really be a set rather than a dict
 | 
|
177  | 
-        for f in os.listdir(d):
 | 
|
178  | 
-            if f.endswith('.py'):
 | 
|
179  | 
-                f = f[:-3]
 | 
|
180  | 
-            elif f.endswith('.pyc') or f.endswith('.pyo'):
 | 
|
181  | 
-                f = f[:-4]
 | 
|
182  | 
-            elif platform_extension and f.endswith(platform_extension):
 | 
|
183  | 
-                f = f[:-len(platform_extension)]
 | 
|
184  | 
-                if f.endswidth('module'):
 | 
|
185  | 
-                    f = f[:-len('module')]
 | 
|
186  | 
-            else:
 | 
|
187  | 
-                continue
 | 
|
188  | 
-            if not plugin_names.has_key(f):
 | 
|
189  | 
-                plugin_names[f] = True
 | 
|
190  | 
-
 | 
|
191  | 
-        plugin_names = plugin_names.keys()
 | 
|
192  | 
-        plugin_names.sort()
 | 
|
193  | 
-        try:
 | 
|
194  | 
-            sys.path.insert(0, d)
 | 
|
195  | 
-            for name in plugin_names:
 | 
|
196  | 
-                try:
 | 
|
197  | 
-                    old_module = None
 | 
|
198  | 
-                    try:
 | 
|
199  | 
-                        if sys.modules.has_key(name):
 | 
|
200  | 
-                            old_module = sys.modules[name]
 | 
|
201  | 
-                            del sys.modules[name]
 | 
|
202  | 
-                        plugin = __import__(name, locals())
 | 
|
203  | 
-                        for k in dir(plugin):
 | 
|
204  | 
-                            if k.startswith('cmd_'):
 | 
|
205  | 
-                                k_unsquished = _unsquish_command_name(k)
 | 
|
206  | 
-                                if not plugin_cmds.has_key(k_unsquished):
 | 
|
207  | 
-                                    plugin_cmds[k_unsquished] = getattr(plugin, k)
 | 
|
208  | 
-                                else:
 | 
|
209  | 
-                                    log_error('Two plugins defined the same command: %r' % k)
 | 
|
210  | 
-                                    log_error('Not loading the one in %r in dir %r' % (name, d))
 | 
|
211  | 
-                    finally:
 | 
|
212  | 
-                        if old_module:
 | 
|
213  | 
-                            sys.modules[name] = old_module
 | 
|
214  | 
-                except ImportError, e:
 | 
|
215  | 
-                    log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
 | 
|
216  | 
-        finally:
 | 
|
217  | 
-            sys.path.pop(0)
 | 
|
218  | 
-    return plugin_cmds
 | 
|
219  | 
-
 | 
|
220  | 
-def _get_cmd_dict(include_plugins=True):
 | 
|
221  | 
+def _get_cmd_dict(plugins_override=True):
 | 
|
222  | 
     d = {}
 | 
|
223  | 
     for k, v in globals().iteritems():
 | 
|
224  | 
         if k.startswith("cmd_"):
 | 
|
225  | 
             d[_unsquish_command_name(k)] = v
 | 
|
226  | 
-    if include_plugins:
 | 
|
227  | 
-        d.update(_find_plugins())
 | 
|
228  | 
+    # If we didn't load plugins, the plugin_cmds dict will be empty
 | 
|
229  | 
+    if plugins_override:
 | 
|
230  | 
+        d.update(plugin_cmds)
 | 
|
231  | 
+    else:
 | 
|
232  | 
+        d2 = {}
 | 
|
233  | 
+        d2.update(plugin_cmds)
 | 
|
234  | 
+        d2.update(d)
 | 
|
235  | 
+        d = d2
 | 
|
236  | 
     return d
 | 
|
237  | 
||
238  | 
-def get_all_cmds(include_plugins=True):
 | 
|
239  | 
+def get_all_cmds(plugins_override=True):
 | 
|
240  | 
     """Return canonical name and class for all registered commands."""
 | 
|
241  | 
-    for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
 | 
|
242  | 
+    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 | 
|
243  | 
         yield k,v
 | 
|
244  | 
||
245  | 
||
246  | 
-def get_cmd_class(cmd,include_plugins=True):
 | 
|
247  | 
+def get_cmd_class(cmd, plugins_override=True):
 | 
|
248  | 
     """Return the canonical name and command class for a command.
 | 
|
249  | 
     """
 | 
|
250  | 
     cmd = str(cmd)                      # not unicode
 | 
|
251  | 
||
252  | 
     # first look up this command under the specified name
 | 
|
253  | 
-    cmds = _get_cmd_dict(include_plugins=include_plugins)
 | 
|
254  | 
+    cmds = _get_cmd_dict(plugins_override=plugins_override)
 | 
|
255  | 
     try:
 | 
|
256  | 
         return cmd, cmds[cmd]
 | 
|
257  | 
     except KeyError:
 | 
|
258  | 
@@ -1461,6 +1413,75 @@
 | 
|
259  | 
     return argdict
 | 
|
260  | 
||
261  | 
||
262  | 
+def _parse_master_args(argv):
 | 
|
263  | 
+    """Parse the arguments that always go with the original command.
 | 
|
264  | 
+    These are things like bzr --no-plugins, etc.
 | 
|
265  | 
+
 | 
|
266  | 
+    There are now 2 types of option flags. Ones that come *before* the command,
 | 
|
267  | 
+    and ones that come *after* the command.
 | 
|
268  | 
+    Ones coming *before* the command are applied against all possible commands.
 | 
|
269  | 
+    And are generally applied before plugins are loaded.
 | 
|
270  | 
+
 | 
|
271  | 
+    The current list are:
 | 
|
272  | 
+        --builtin   Allow plugins to load, but don't let them override builtin commands,
 | 
|
273  | 
+                    they will still be allowed if they do not override a builtin.
 | 
|
274  | 
+        --no-plugins    Don't load any plugins. This lets you get back to official source
 | 
|
275  | 
+                        behavior.
 | 
|
276  | 
+        --profile   Enable the hotspot profile before running the command.
 | 
|
277  | 
+                    For backwards compatibility, this is also a non-master option.
 | 
|
278  | 
+        --version   Spit out the version of bzr that is running and exit.
 | 
|
279  | 
+                    This is also a non-master option.
 | 
|
280  | 
+        --help      Run help and exit, also a non-master option (I think that should stay, though)
 | 
|
281  | 
+
 | 
|
282  | 
+    >>> argv, opts = _parse_master_args(['bzr', '--test'])
 | 
|
283  | 
+    Traceback (most recent call last):
 | 
|
284  | 
+    ...
 | 
|
285  | 
+    BzrCommandError: Invalid master option: 'test'
 | 
|
286  | 
+    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
 | 
|
287  | 
+    >>> print argv
 | 
|
288  | 
+    ['command']
 | 
|
289  | 
+    >>> print opts['version']
 | 
|
290  | 
+    True
 | 
|
291  | 
+    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
 | 
|
292  | 
+    >>> print argv
 | 
|
293  | 
+    ['command', '--more-options']
 | 
|
294  | 
+    >>> print opts['profile']
 | 
|
295  | 
+    True
 | 
|
296  | 
+    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
 | 
|
297  | 
+    >>> print argv
 | 
|
298  | 
+    ['command']
 | 
|
299  | 
+    >>> print opts['no-plugins']
 | 
|
300  | 
+    True
 | 
|
301  | 
+    >>> print opts['profile']
 | 
|
302  | 
+    False
 | 
|
303  | 
+    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
 | 
|
304  | 
+    >>> print argv
 | 
|
305  | 
+    ['command', '--profile']
 | 
|
306  | 
+    >>> print opts['profile']
 | 
|
307  | 
+    False
 | 
|
308  | 
+    """
 | 
|
309  | 
+    master_opts = {'builtin':False,
 | 
|
310  | 
+        'no-plugins':False,
 | 
|
311  | 
+        'version':False,
 | 
|
312  | 
+        'profile':False,
 | 
|
313  | 
+        'help':False
 | 
|
314  | 
+    }
 | 
|
315  | 
+
 | 
|
316  | 
+    # This is the point where we could hook into argv[0] to determine
 | 
|
317  | 
+    # what front-end is supposed to be run
 | 
|
318  | 
+    # For now, we are just ignoring it.
 | 
|
319  | 
+    cmd_name = argv.pop(0)
 | 
|
320  | 
+    for arg in argv[:]:
 | 
|
321  | 
+        if arg[:2] != '--': # at the first non-option, we return the rest
 | 
|
322  | 
+            break
 | 
|
323  | 
+        arg = arg[2:] # Remove '--'
 | 
|
324  | 
+        if arg not in master_opts:
 | 
|
325  | 
+            # We could say that this is not an error, that we should
 | 
|
326  | 
+            # just let it be handled by the main section instead
 | 
|
327  | 
+            raise BzrCommandError('Invalid master option: %r' % arg)
 | 
|
328  | 
+        argv.pop(0) # We are consuming this entry
 | 
|
329  | 
+        master_opts[arg] = True
 | 
|
330  | 
+    return argv, master_opts
 | 
|
331  | 
||
332  | 
 def run_bzr(argv):
 | 
|
333  | 
     """Execute a command.
 | 
|
334  | 
@@ -1470,22 +1491,21 @@
 | 
|
335  | 
     """
 | 
|
336  | 
     argv = [a.decode(bzrlib.user_encoding) for a in argv]
 | 
|
337  | 
||
338  | 
-    include_plugins=True
 | 
|
339  | 
     try:
 | 
|
340  | 
-        args, opts = parse_args(argv[1:])
 | 
|
341  | 
-        if 'help' in opts:
 | 
|
342  | 
+        argv, master_opts = _parse_master_args(argv)
 | 
|
343  | 
+        if not master_opts['no-plugins']:
 | 
|
344  | 
+            bzrlib.load_plugins()
 | 
|
345  | 
+        args, opts = parse_args(argv)
 | 
|
346  | 
+        if 'help' in opts or master_opts['help']:
 | 
|
347  | 
             import help
 | 
|
348  | 
             if args:
 | 
|
349  | 
                 help.help(args[0])
 | 
|
350  | 
             else:
 | 
|
351  | 
                 help.help()
 | 
|
352  | 
             return 0
 | 
|
353  | 
-        elif 'version' in opts:
 | 
|
354  | 
+        elif 'version' in opts or master_opts['version']:
 | 
|
355  | 
             show_version()
 | 
|
356  | 
             return 0
 | 
|
357  | 
-        elif args and args[0] == 'builtin':
 | 
|
358  | 
-            include_plugins=False
 | 
|
359  | 
-            args = args[1:]
 | 
|
360  | 
         cmd = str(args.pop(0))
 | 
|
361  | 
     except IndexError:
 | 
|
362  | 
         import help
 | 
|
363  | 
@@ -1493,14 +1513,15 @@
 | 
|
364  | 
         return 1
 | 
|
365  | 
||
366  | 
||
367  | 
-    canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
 | 
|
368  | 
-
 | 
|
369  | 
-    # global option
 | 
|
370  | 
+    plugins_override = not (master_opts['builtin'])
 | 
|
371  | 
+    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
 | 
|
372  | 
+
 | 
|
373  | 
+    profile = master_opts['profile']
 | 
|
374  | 
+    # For backwards compatibility, I would rather stick with --profile being a
 | 
|
375  | 
+    # master/global option
 | 
|
376  | 
     if 'profile' in opts:
 | 
|
377  | 
         profile = True
 | 
|
378  | 
         del opts['profile']
 | 
|
379  | 
-    else:
 | 
|
380  | 
-        profile = False
 | 
|
381  | 
||
382  | 
     # check options are reasonable
 | 
|
383  | 
     allowed = cmd_class.takes_options
 | 
|
384  | 
||
385  | 
*** modified file 'testbzr'
 | 
|
386  | 
--- testbzr
 | 
|
387  | 
+++ testbzr
 | 
|
388  | 
@@ -149,6 +149,7 @@
 | 
|
389  | 
     """Run a test involving creating a plugin to load,
 | 
|
390  | 
     and making sure it is seen properly.
 | 
|
391  | 
     """
 | 
|
392  | 
+    orig_help = backtick('bzr help commands') # No plugins yet
 | 
|
393  | 
     mkdir('plugin_test')
 | 
|
394  | 
     f = open(os.path.join('plugin_test', 'myplug.py'), 'wb')
 | 
|
395  | 
     f.write("""import bzrlib, bzrlib.commands
 | 
|
396  | 
@@ -157,24 +158,36 @@
 | 
|
397  | 
     aliases = ['mplg']
 | 
|
398  | 
     def run(self):
 | 
|
399  | 
         print 'Hello from my plugin'
 | 
|
400  | 
+class cmd_myplug_with_opt(bzrlib.commands.Command):
 | 
|
401  | 
+    '''A simple plugin that requires a special option'''
 | 
|
402  | 
+    takes_options = ['aspecialoptionthatdoesntexist']
 | 
|
403  | 
+    def run(self, aspecialoptionthatdoesntexist=None):
 | 
|
404  | 
+        print 'Found: %s' % aspecialoptionthatdoesntexist
 | 
|
405  | 
+
 | 
|
406  | 
+bzrlib.commands.register_plugin_command(cmd_myplug)
 | 
|
407  | 
+bzrlib.commands.register_plugin_command(cmd_myplug_with_opt)
 | 
|
408  | 
+bzrlib.commands.OPTIONS['aspecialoptionthatdoesntexist'] = str
 | 
|
409  | 
 """)
 | 
|
410  | 
     f.close()
 | 
|
411  | 
||
412  | 
-    os.environ['BZRPLUGINPATH'] = os.path.abspath('plugin_test')
 | 
|
413  | 
-    help = backtick('bzr help commands')
 | 
|
414  | 
+    os.environ['BZR_PLUGIN_PATH'] = os.path.abspath('plugin_test')
 | 
|
415  | 
+    help = backtick('bzr help commands') #Help with user-visible plugins
 | 
|
416  | 
     assert help.find('myplug') != -1
 | 
|
417  | 
     assert help.find('Just a simple test plugin.') != -1
 | 
|
418  | 
||
419  | 
||
420  | 
     assert backtick('bzr myplug') == 'Hello from my plugin\n'
 | 
|
421  | 
     assert backtick('bzr mplg') == 'Hello from my plugin\n'
 | 
|
422  | 
+    assert backtick('bzr myplug-with-opt') == 'Found: None\n'
 | 
|
423  | 
+    assert backtick('bzr myplug-with-opt --aspecialoptionthatdoesntexist=2') == 'Found: 2\n'
 | 
|
424  | 
||
425  | 
     f = open(os.path.join('plugin_test', 'override.py'), 'wb')
 | 
|
426  | 
     f.write("""import bzrlib, bzrlib.commands
 | 
|
427  | 
-class cmd_commit(bzrlib.commands.cmd_commit):
 | 
|
428  | 
-    '''Commit changes into a new revision.'''
 | 
|
429  | 
+class cmd_revno(bzrlib.commands.cmd_revno):
 | 
|
430  | 
+    '''Show current revision number.'''
 | 
|
431  | 
     def run(self, *args, **kwargs):
 | 
|
432  | 
         print "I'm sorry dave, you can't do that"
 | 
|
433  | 
+        return 1
 | 
|
434  | 
||
435  | 
 class cmd_help(bzrlib.commands.cmd_help):
 | 
|
436  | 
     '''Show help on a command or other topic.'''
 | 
|
437  | 
@@ -182,16 +195,67 @@
 | 
|
438  | 
         print "You have been overridden"
 | 
|
439  | 
         bzrlib.commands.cmd_help.run(self, *args, **kwargs)
 | 
|
440  | 
||
441  | 
+bzrlib.commands.register_plugin_command(cmd_revno)
 | 
|
442  | 
+bzrlib.commands.register_plugin_command(cmd_help)
 | 
|
443  | 
 """)
 | 
|
444  | 
     f.close()
 | 
|
445  | 
||
446  | 
-    newhelp = backtick('bzr help commands')
 | 
|
447  | 
+    newhelp = backtick('bzr help commands') # Help with no new commands,
 | 
|
448  | 
     assert newhelp.startswith('You have been overridden\n')
 | 
|
449  | 
     # We added a line, but the rest should work
 | 
|
450  | 
     assert newhelp[25:] == help
 | 
|
451  | 
-
 | 
|
452  | 
-    assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
 | 
|
453  | 
-
 | 
|
454  | 
+    # Make sure we can get back to the original command
 | 
|
455  | 
+    # Not overridden, and no extra commands present
 | 
|
456  | 
+    assert backtick('bzr --builtin help commands') == help
 | 
|
457  | 
+    assert backtick('bzr --no-plugins help commands') == orig_help
 | 
|
458  | 
+
 | 
|
459  | 
+    assert backtick('bzr revno', retcode=1) == "I'm sorry dave, you can't do that\n"
 | 
|
460  | 
+
 | 
|
461  | 
+    print_txt = '** Loading noop plugin'
 | 
|
462  | 
+    f = open(os.path.join('plugin_test', 'loading.py'), 'wb')
 | 
|
463  | 
+    f.write("""import bzrlib, bzrlib.commands
 | 
|
464  | 
+class cmd_noop(bzrlib.commands.Command):
 | 
|
465  | 
+    def run(self, *args, **kwargs):
 | 
|
466  | 
+        pass
 | 
|
467  | 
+
 | 
|
468  | 
+print %r
 | 
|
469  | 
+bzrlib.commands.register_plugin_command(cmd_noop)
 | 
|
470  | 
+""" % print_txt)
 | 
|
471  | 
+    f.close()
 | 
|
472  | 
+    print_txt += '\n'
 | 
|
473  | 
+
 | 
|
474  | 
+    # Check that --builtin still loads the plugin, and enables it as
 | 
|
475  | 
+    # an extra command, but not as an override
 | 
|
476  | 
+    # and that --no-plugins doesn't load the command at all
 | 
|
477  | 
+    assert backtick('bzr noop') == print_txt
 | 
|
478  | 
+    assert backtick('bzr --builtin help')[:len(print_txt)] == print_txt
 | 
|
479  | 
+    assert backtick('bzr --no-plugins help')[:len(print_txt)] != print_txt
 | 
|
480  | 
+    runcmd('bzr revno', retcode=1)
 | 
|
481  | 
+    runcmd('bzr --builtin revno', retcode=0)
 | 
|
482  | 
+    runcmd('bzr --no-plugins revno', retcode=0)
 | 
|
483  | 
+    runcmd('bzr --builtin noop', retcode=0)
 | 
|
484  | 
+    runcmd('bzr --no-plugins noop', retcode=1)
 | 
|
485  | 
+
 | 
|
486  | 
+    # Check that packages can also be loaded
 | 
|
487  | 
+    test_str = 'packages work'
 | 
|
488  | 
+    os.mkdir(os.path.join('plugin_test', 'testpkg'))
 | 
|
489  | 
+    f = open(os.path.join('plugin_test', 'testpkg', '__init__.py'), 'wb')
 | 
|
490  | 
+    f.write("""import bzrlib, bzrlib.commands
 | 
|
491  | 
+class testpkgcmd(bzrlib.commands.Command):
 | 
|
492  | 
+    def run(self, *args, **kwargs):
 | 
|
493  | 
+        print %r
 | 
|
494  | 
+
 | 
|
495  | 
+bzrlib.commands.register_plugin_command(testpkgcmd)
 | 
|
496  | 
+""" % test_str)
 | 
|
497  | 
+    f.close()
 | 
|
498  | 
+    test_str += '\n'
 | 
|
499  | 
+    assert backtick('bzr testpkgcmd') == print_txt + test_str
 | 
|
500  | 
+    runcmd('bzr --no-plugins testpkgcmd', retcode=1)
 | 
|
501  | 
+
 | 
|
502  | 
+    # Make sure that setting BZR_PLUGIN_PATH to empty is the same as using --no-plugins
 | 
|
503  | 
+    os.environ['BZR_PLUGIN_PATH'] = ''
 | 
|
504  | 
+    assert backtick('bzr help commands') == orig_help
 | 
|
505  | 
+
 | 
|
506  | 
     shutil.rmtree('plugin_test')
 | 
|
507  | 
||
508  | 
 try:
 | 
|
509  | 
@@ -221,6 +285,9 @@
 | 
|
510  | 
||
511  | 
     runcmd(['mkdir', TESTDIR])
 | 
|
512  | 
     cd(TESTDIR)
 | 
|
513  | 
+    # This means that any command that is naively run in this directory
 | 
|
514  | 
+    # Won't affect the parent directory.
 | 
|
515  | 
+    runcmd('bzr init')
 | 
|
516  | 
     test_root = os.getcwd()
 | 
|
517  | 
||
518  | 
     progress("introductory commands")
 | 
|
519  |