~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: John Ferlito
  • Date: 2009-05-25 10:59:42 UTC
  • mto: (4665.4.1 ppa-doc)
  • mto: This revision was merged to the branch mainline in revision 4693.
  • Revision ID: johnf@inodes.org-20090525105942-5xkcbe37m1u5lp5z
Update packaging scripts to make deployment a bit easier
Update documentation for deploying to PPA

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006, 2008 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
40
40
 
41
41
import bzrlib
42
42
from bzrlib import (
43
 
    cleanup,
44
43
    debug,
45
44
    errors,
46
45
    option,
47
46
    osutils,
48
47
    trace,
49
 
    ui,
50
48
    win32utils,
51
49
    )
52
50
""")
53
51
 
 
52
from bzrlib import registry
 
53
# Compatibility
54
54
from bzrlib.hooks import HookPoint, Hooks
55
 
# Compatibility - Option used to be in commands.
56
55
from bzrlib.option import Option
57
 
from bzrlib import registry
58
 
from bzrlib.symbol_versioning import (
59
 
    deprecated_function,
60
 
    deprecated_in,
61
 
    deprecated_method,
62
 
    suppress_deprecation_warnings,
63
 
    )
64
56
 
65
57
 
66
58
class CommandInfo(object):
104
96
            registry.Registry.register(self, k_unsquished, cmd,
105
97
                                       override_existing=decorate, info=info)
106
98
        except KeyError:
107
 
            trace.warning('Two plugins defined the same command: %r' % k)
108
 
            trace.warning('Not loading the one in %r' %
109
 
                sys.modules[cmd.__module__])
110
 
            trace.warning('Previously this command was registered from %r' %
111
 
                sys.modules[previous.__module__])
 
99
            trace.log_error('Two plugins defined the same command: %r' % k)
 
100
            trace.log_error('Not loading the one in %r' %
 
101
                            sys.modules[cmd.__module__])
 
102
            trace.log_error('Previously this command was registered from %r' %
 
103
                            sys.modules[previous.__module__])
112
104
        return previous
113
105
 
114
106
    def register_lazy(self, command_name, aliases, module_name):
141
133
 
142
134
def _builtin_commands():
143
135
    import bzrlib.builtins
144
 
    return _scan_module_for_commands(bzrlib.builtins)
145
 
 
146
 
 
147
 
def _scan_module_for_commands(module):
148
136
    r = {}
149
 
    for name, obj in module.__dict__.iteritems():
 
137
    builtins = bzrlib.builtins.__dict__
 
138
    for name in builtins:
150
139
        if name.startswith("cmd_"):
151
140
            real_name = _unsquish_command_name(name)
152
 
            r[real_name] = obj
 
141
            r[real_name] = builtins[name]
153
142
    return r
154
143
 
155
144
 
156
 
def _list_bzr_commands(names):
157
 
    """Find commands from bzr's core and plugins."""
158
 
    # to eliminate duplicates
159
 
    names.update(builtin_command_names())
160
 
    names.update(plugin_command_names())
161
 
    return names
162
 
 
163
 
 
164
 
def all_command_names():
165
 
    """Return a set of all command names."""
166
 
    names = set()
167
 
    for hook in Command.hooks['list_commands']:
168
 
        names = hook(names)
169
 
        if names is None:
170
 
            raise AssertionError(
171
 
                'hook %s returned None' % Command.hooks.get_hook_name(hook))
172
 
    return names
173
 
 
174
 
 
175
145
def builtin_command_names():
176
 
    """Return list of builtin command names.
177
 
    
178
 
    Use of all_command_names() is encouraged rather than builtin_command_names
179
 
    and/or plugin_command_names.
180
 
    """
 
146
    """Return list of builtin command names."""
181
147
    return _builtin_commands().keys()
182
148
 
183
149
 
184
150
def plugin_command_names():
185
 
    """Returns command names from commands registered by plugins."""
186
151
    return plugin_cmds.keys()
187
152
 
188
153
 
189
 
@deprecated_function(deprecated_in((1, 17, 0)))
190
 
def get_all_cmds(plugins_override=False):
191
 
    """Return canonical name and class for most commands.
192
 
    
193
 
    NB: This does not return all commands since the introduction of
194
 
    command hooks, and returning the class is not sufficient to 
195
 
    get correctly setup commands, which is why it is deprecated.
196
 
 
197
 
    Use 'all_command_names' + 'get_cmd_object' instead.
198
 
    """
 
154
def _get_cmd_dict(plugins_override=True):
 
155
    """Return name->class mapping for all commands."""
199
156
    d = _builtin_commands()
200
157
    if plugins_override:
201
158
        d.update(plugin_cmds.iteritems())
202
 
    for k, v in d.iteritems():
 
159
    return d
 
160
 
 
161
 
 
162
def get_all_cmds(plugins_override=True):
 
163
    """Return canonical name and class for all registered commands."""
 
164
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
203
165
        yield k,v
204
166
 
205
167
 
206
168
def get_cmd_object(cmd_name, plugins_override=True):
207
 
    """Return the command object for a command.
 
169
    """Return the canonical name and command class for a command.
208
170
 
209
171
    plugins_override
210
172
        If true, plugin commands can override builtins.
211
173
    """
212
174
    try:
213
 
        return _get_cmd_object(cmd_name, plugins_override)
 
175
        cmd = _get_cmd_object(cmd_name, plugins_override)
 
176
        # Allow plugins to extend commands
 
177
        for hook in Command.hooks['extend_command']:
 
178
            hook(cmd)
 
179
        return cmd
214
180
    except KeyError:
215
181
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
216
182
 
217
183
 
218
184
def _get_cmd_object(cmd_name, plugins_override=True):
219
 
    """Get a command object.
 
185
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
 
186
    from bzrlib.externalcommand import ExternalCommand
220
187
 
221
 
    :param cmd_name: The name of the command.
222
 
    :param plugins_override: Allow plugins to override builtins.
223
 
    :return: A Command object instance
224
 
    :raises KeyError: If no command is found.
225
 
    """
226
188
    # We want only 'ascii' command names, but the user may have typed
227
189
    # in a Unicode name. In that case, they should just get a
228
190
    # 'command not found' error later.
229
191
    # In the future, we may actually support Unicode command names.
230
 
    cmd = None
231
 
    # Get a command
232
 
    for hook in Command.hooks['get_command']:
233
 
        cmd = hook(cmd, cmd_name)
234
 
        if cmd is not None and not plugins_override and not cmd.plugin_name():
235
 
            # We've found a non-plugin command, don't permit it to be
236
 
            # overridden.
237
 
            break
238
 
    if cmd is None:
239
 
        for hook in Command.hooks['get_missing_command']:
240
 
            cmd = hook(cmd_name)
241
 
            if cmd is not None:
242
 
                break
243
 
    if cmd is None:
244
 
        # No command found.
245
 
        raise KeyError
246
 
    # Allow plugins to extend commands
247
 
    for hook in Command.hooks['extend_command']:
248
 
        hook(cmd)
249
 
    return cmd
250
 
 
251
 
 
252
 
def _try_plugin_provider(cmd_name):
253
 
    """Probe for a plugin provider having cmd_name."""
254
 
    try:
255
 
        plugin_metadata, provider = probe_for_provider(cmd_name)
256
 
        raise errors.CommandAvailableInPlugin(cmd_name,
257
 
            plugin_metadata, provider)
258
 
    except errors.NoPluginAvailable:
259
 
        pass
260
 
 
261
 
 
262
 
def probe_for_provider(cmd_name):
263
 
    """Look for a provider for cmd_name.
264
 
 
265
 
    :param cmd_name: The command name.
266
 
    :return: plugin_metadata, provider for getting cmd_name.
267
 
    :raises NoPluginAvailable: When no provider can supply the plugin.
268
 
    """
269
 
    # look for providers that provide this command but aren't installed
270
 
    for provider in command_providers_registry:
 
192
 
 
193
    # first look up this command under the specified name
 
194
    if plugins_override:
271
195
        try:
272
 
            return provider.plugin_for_command(cmd_name), provider
273
 
        except errors.NoPluginAvailable:
 
196
            return plugin_cmds.get(cmd_name)()
 
197
        except KeyError:
274
198
            pass
275
 
    raise errors.NoPluginAvailable(cmd_name)
276
 
 
277
 
 
278
 
def _get_bzr_command(cmd_or_None, cmd_name):
279
 
    """Get a command from bzr's core."""
280
 
    cmds = _builtin_commands()
 
199
    cmds = _get_cmd_dict(plugins_override=False)
281
200
    try:
282
201
        return cmds[cmd_name]()
283
202
    except KeyError:
284
203
        pass
 
204
    if plugins_override:
 
205
        for key in plugin_cmds.keys():
 
206
            info = plugin_cmds.get_info(key)
 
207
            if cmd_name in info.aliases:
 
208
                return plugin_cmds.get(key)()
285
209
    # look for any command which claims this as an alias
286
210
    for real_cmd_name, cmd_class in cmds.iteritems():
287
211
        if cmd_name in cmd_class.aliases:
288
212
            return cmd_class()
289
 
    return cmd_or_None
290
 
 
291
 
 
292
 
def _get_external_command(cmd_or_None, cmd_name):
293
 
    """Lookup a command that is a shell script."""
294
 
    # Only do external command lookups when no command is found so far.
295
 
    if cmd_or_None is not None:
296
 
        return cmd_or_None
297
 
    from bzrlib.externalcommand import ExternalCommand
 
213
 
298
214
    cmd_obj = ExternalCommand.find_command(cmd_name)
299
215
    if cmd_obj:
300
216
        return cmd_obj
301
217
 
302
 
 
303
 
def _get_plugin_command(cmd_or_None, cmd_name):
304
 
    """Get a command from bzr's plugins."""
305
 
    try:
306
 
        return plugin_cmds.get(cmd_name)()
307
 
    except KeyError:
308
 
        pass
309
 
    for key in plugin_cmds.keys():
310
 
        info = plugin_cmds.get_info(key)
311
 
        if cmd_name in info.aliases:
312
 
            return plugin_cmds.get(key)()
313
 
    return cmd_or_None
 
218
    # look for plugins that provide this command but aren't installed
 
219
    for provider in command_providers_registry:
 
220
        try:
 
221
            plugin_metadata = provider.plugin_for_command(cmd_name)
 
222
        except errors.NoPluginAvailable:
 
223
            pass
 
224
        else:
 
225
            raise errors.CommandAvailableInPlugin(cmd_name,
 
226
                                                  plugin_metadata, provider)
 
227
    raise KeyError
314
228
 
315
229
 
316
230
class Command(object):
385
299
            warn("No help message set for %r" % self)
386
300
        # List of standard options directly supported
387
301
        self.supported_std_options = []
388
 
        self._operation = cleanup.OperationWithCleanups(self.run)
389
 
    
390
 
    def add_cleanup(self, cleanup_func, *args, **kwargs):
391
 
        """Register a function to call after self.run returns or raises.
392
 
 
393
 
        Functions will be called in LIFO order.
394
 
        """
395
 
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
396
 
 
397
 
    def cleanup_now(self):
398
 
        """Execute and empty pending cleanup functions immediately.
399
 
 
400
 
        After cleanup_now all registered cleanups are forgotten.  add_cleanup
401
 
        may be called again after cleanup_now; these cleanups will be called
402
 
        after self.run returns or raises (or when cleanup_now is next called).
403
 
 
404
 
        This is useful for releasing expensive or contentious resources (such
405
 
        as write locks) before doing further work that does not require those
406
 
        resources (such as writing results to self.outf).
407
 
        """
408
 
        self._operation.cleanup_now()
409
 
        
410
 
    @deprecated_method(deprecated_in((2, 1, 0)))
 
302
 
411
303
    def _maybe_expand_globs(self, file_list):
412
304
        """Glob expand file_list if the platform does not do that itself.
413
305
 
414
 
        Not used anymore, now that the bzr command-line parser globs on
415
 
        Windows.
416
 
 
417
306
        :return: A possibly empty list of unicode paths.
418
307
 
419
308
        Introduced in bzrlib 0.18.
420
309
        """
421
 
        return file_list
 
310
        if not file_list:
 
311
            file_list = []
 
312
        if sys.platform == 'win32':
 
313
            file_list = win32utils.glob_expand(file_list)
 
314
        return list(file_list)
422
315
 
423
316
    def _usage(self):
424
317
        """Return single-line grammar for this command.
476
369
        result += '\n'
477
370
 
478
371
        # Add the options
479
 
        #
480
 
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
481
 
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
482
 
        # 20090319
483
372
        options = option.get_optparser(self.options()).format_option_help()
484
 
        # XXX: According to the spec, ReST option lists actually don't support 
485
 
        # options like --1.9 so that causes syntax errors (in Sphinx at least).
486
 
        # As that pattern always appears in the commands that break, we trap
487
 
        # on that and then format that block of 'format' options as a literal
488
 
        # block.
489
 
        if not plain and options.find('  --1.9  ') != -1:
490
 
            options = options.replace(' format:\n', ' format::\n\n', 1)
491
373
        if options.startswith('Options:'):
492
374
            result += ':' + options
493
375
        elif options.startswith('options:'):
533
415
                        # so don't create a real link
534
416
                        see_also_links.append(item)
535
417
                    else:
536
 
                        # Use a Sphinx link for this entry
537
 
                        link_text = ":doc:`%s <%s-help>`" % (item, item)
538
 
                        see_also_links.append(link_text)
 
418
                        # Use a reST link for this entry
 
419
                        see_also_links.append("`%s`_" % (item,))
539
420
                see_also = see_also_links
540
421
            result += ':See also: '
541
422
            result += ', '.join(see_also) + '\n'
619
500
 
620
501
    def _setup_outf(self):
621
502
        """Return a file linked to stdout, which has proper encoding."""
622
 
        self.outf = ui.ui_factory.make_output_stream(
623
 
            encoding_type=self.encoding_type)
 
503
        # Originally I was using self.stdout, but that looks
 
504
        # *way* too much like sys.stdout
 
505
        if self.encoding_type == 'exact':
 
506
            # force sys.stdout to be binary stream on win32
 
507
            if sys.platform == 'win32':
 
508
                fileno = getattr(sys.stdout, 'fileno', None)
 
509
                if fileno:
 
510
                    import msvcrt
 
511
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
512
            self.outf = sys.stdout
 
513
            return
 
514
 
 
515
        output_encoding = osutils.get_terminal_encoding()
 
516
 
 
517
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
518
                        errors=self.encoding_type)
 
519
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
520
        # it just returns the encoding of the wrapped file, which is completely
 
521
        # bogus. So set the attribute, so we can find the correct encoding later.
 
522
        self.outf.encoding = output_encoding
624
523
 
625
524
    def run_argv_aliases(self, argv, alias_argv=None):
626
525
        """Parse the command line and run with extra aliases in alias_argv."""
658
557
 
659
558
        self._setup_outf()
660
559
 
661
 
        return self.run_direct(**all_cmd_args)
662
 
 
663
 
    def run_direct(self, *args, **kwargs):
664
 
        """Call run directly with objects (without parsing an argv list)."""
665
 
        return self._operation.run_simple(*args, **kwargs)
 
560
        return self.run(**all_cmd_args)
666
561
 
667
562
    def run(self):
668
563
        """Actually run the command.
713
608
            "Called after creating a command object to allow modifications "
714
609
            "such as adding or removing options, docs etc. Called with the "
715
610
            "new bzrlib.commands.Command object.", (1, 13), None))
716
 
        self.create_hook(HookPoint('get_command',
717
 
            "Called when creating a single command. Called with "
718
 
            "(cmd_or_None, command_name). get_command should either return "
719
 
            "the cmd_or_None parameter, or a replacement Command object that "
720
 
            "should be used for the command. Note that the Command.hooks "
721
 
            "hooks are core infrastructure. Many users will prefer to use "
722
 
            "bzrlib.commands.register_command or plugin_cmds.register_lazy.",
723
 
            (1, 17), None))
724
 
        self.create_hook(HookPoint('get_missing_command',
725
 
            "Called when creating a single command if no command could be "
726
 
            "found. Called with (command_name). get_missing_command should "
727
 
            "either return None, or a Command object to be used for the "
728
 
            "command.", (1, 17), None))
729
 
        self.create_hook(HookPoint('list_commands',
730
 
            "Called when enumerating commands. Called with a set of "
731
 
            "cmd_name strings for all the commands found so far. This set "
732
 
            " is safe to mutate - e.g. to remove a command. "
733
 
            "list_commands should return the updated set of command names.",
734
 
            (1, 17), None))
735
611
 
736
612
Command.hooks = CommandHooks()
737
613
 
950
826
 
951
827
    --coverage
952
828
        Generate line coverage report in the specified directory.
953
 
 
954
 
    --concurrency
955
 
        Specify the number of processes that can be run concurrently (selftest).
956
829
    """
957
 
    trace.mutter("bazaar version: " + bzrlib.__version__)
958
830
    argv = list(argv)
959
831
    trace.mutter("bzr arguments: %r", argv)
960
832
 
984
856
            opt_no_aliases = True
985
857
        elif a == '--builtin':
986
858
            opt_builtin = True
987
 
        elif a == '--concurrency':
988
 
            os.environ['BZR_CONCURRENCY'] = argv[i + 1]
989
 
            i += 1
990
859
        elif a == '--coverage':
991
860
            opt_coverage_dir = argv[i + 1]
992
861
            i += 1
1053
922
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1054
923
        else:
1055
924
            ret = run(*run_argv)
 
925
        if 'memory' in debug.debug_flags:
 
926
            trace.debug_memory('Process status after command:', short=False)
1056
927
        return ret or 0
1057
928
    finally:
1058
929
        # reset, in case we may do other commands later within the same
1059
930
        # process. Commands that want to execute sub-commands must propagate
1060
931
        # --verbose in their own way.
1061
 
        if 'memory' in debug.debug_flags:
1062
 
            trace.debug_memory('Process status after command:', short=False)
1063
932
        option._verbosity_level = saved_verbosity_level
1064
933
 
1065
934
 
1083
952
    return ignore_pipe
1084
953
 
1085
954
 
1086
 
def install_bzr_command_hooks():
1087
 
    """Install the hooks to supply bzr's own commands."""
1088
 
    if _list_bzr_commands in Command.hooks["list_commands"]:
1089
 
        return
1090
 
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1091
 
        "bzr commands")
1092
 
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1093
 
        "bzr commands")
1094
 
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1095
 
        "bzr plugin commands")
1096
 
    Command.hooks.install_named_hook("get_command", _get_external_command,
1097
 
        "bzr external command lookup")
1098
 
    Command.hooks.install_named_hook("get_missing_command", _try_plugin_provider,
1099
 
        "bzr plugin-provider-db check")
1100
 
 
1101
 
 
1102
955
def main(argv=None):
1103
956
    """Main entry point of command-line interface.
1104
957
 
1115
968
 
1116
969
    # Is this a final release version? If so, we should suppress warnings
1117
970
    if bzrlib.version_info[3] == 'final':
1118
 
        suppress_deprecation_warnings(override=True)
 
971
        from bzrlib import symbol_versioning
 
972
        symbol_versioning.suppress_deprecation_warnings(override=False)
1119
973
    if argv is None:
1120
974
        argv = osutils.get_unicode_argv()
1121
975
    else:
1131
985
            raise errors.BzrError("argv should be list of unicode strings.")
1132
986
        argv = new_argv
1133
987
    ret = run_bzr_catch_errors(argv)
1134
 
    bzrlib.ui.ui_factory.log_transport_activity(
1135
 
        display=('bytes' in debug.debug_flags))
1136
988
    trace.mutter("return code %d", ret)
1137
 
    osutils.report_extension_load_failures()
1138
989
    return ret
1139
990
 
1140
991
 
1144
995
    This function assumed that that UI layer is setup, that symbol deprecations
1145
996
    are already applied, and that unicode decoding has already been performed on argv.
1146
997
    """
1147
 
    install_bzr_command_hooks()
1148
998
    return exception_to_return_code(run_bzr, argv)
1149
999
 
1150
1000
 
1154
1004
    This is used for the test suite, and might be useful for other programs
1155
1005
    that want to wrap the commandline interface.
1156
1006
    """
1157
 
    install_bzr_command_hooks()
1158
1007
    try:
1159
1008
        return run_bzr(argv)
1160
1009
    except Exception, e: