/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 patches/plugins-no-plugins.patch

  • Committer: Martin Pool
  • Date: 2005-08-29 04:05:28 UTC
  • Revision ID: mbp@sourcefrog.net-20050829040528-3fb016af15c4e4c0
- smart_add routine now doesn't print filenames directly, but rather
  returns them to the caller for appropriate display.  the add command
  displays them in the same way as before.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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