~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Andrew Bennetts
  • Date: 2010-01-15 03:58:20 UTC
  • mfrom: (4963 +trunk)
  • mto: (4973.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4975.
  • Revision ID: andrew.bennetts@canonical.com-20100115035820-ilb3t36swgzq6v1l
MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2006, 2008, 2009 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
# TODO: probably should say which arguments are candidates for glob
35
35
lazy_import(globals(), """
36
36
import codecs
37
37
import errno
 
38
import threading
38
39
from warnings import warn
39
40
 
40
41
import bzrlib
41
42
from bzrlib import (
 
43
    cleanup,
42
44
    debug,
43
45
    errors,
44
46
    option,
45
47
    osutils,
46
48
    trace,
 
49
    ui,
47
50
    win32utils,
48
51
    )
49
52
""")
50
53
 
 
54
from bzrlib.hooks import HookPoint, Hooks
 
55
# Compatibility - Option used to be in commands.
 
56
from bzrlib.option import Option
51
57
from bzrlib import registry
52
 
# Compatibility
53
 
from bzrlib.hooks import Hooks
54
 
from bzrlib.option import Option
 
58
from bzrlib.symbol_versioning import (
 
59
    deprecated_function,
 
60
    deprecated_in,
 
61
    deprecated_method,
 
62
    suppress_deprecation_warnings,
 
63
    )
55
64
 
56
65
 
57
66
class CommandInfo(object):
95
104
            registry.Registry.register(self, k_unsquished, cmd,
96
105
                                       override_existing=decorate, info=info)
97
106
        except KeyError:
98
 
            trace.log_error('Two plugins defined the same command: %r' % k)
99
 
            trace.log_error('Not loading the one in %r' %
100
 
                            sys.modules[cmd.__module__])
101
 
            trace.log_error('Previously this command was registered from %r' %
102
 
                            sys.modules[previous.__module__])
 
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__])
103
112
        return previous
104
113
 
105
114
    def register_lazy(self, command_name, aliases, module_name):
132
141
 
133
142
def _builtin_commands():
134
143
    import bzrlib.builtins
 
144
    return _scan_module_for_commands(bzrlib.builtins)
 
145
 
 
146
 
 
147
def _scan_module_for_commands(module):
135
148
    r = {}
136
 
    builtins = bzrlib.builtins.__dict__
137
 
    for name in builtins:
 
149
    for name, obj in module.__dict__.iteritems():
138
150
        if name.startswith("cmd_"):
139
151
            real_name = _unsquish_command_name(name)
140
 
            r[real_name] = builtins[name]
 
152
            r[real_name] = obj
141
153
    return r
142
 
            
 
154
 
 
155
 
 
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
 
143
174
 
144
175
def builtin_command_names():
145
 
    """Return list of 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
181
    return _builtin_commands().keys()
147
 
    
 
182
 
148
183
 
149
184
def plugin_command_names():
 
185
    """Returns command names from commands registered by plugins."""
150
186
    return plugin_cmds.keys()
151
187
 
152
188
 
153
 
def _get_cmd_dict(plugins_override=True):
154
 
    """Return name->class mapping for all commands."""
 
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
    """
155
199
    d = _builtin_commands()
156
200
    if plugins_override:
157
201
        d.update(plugin_cmds.iteritems())
158
 
    return d
159
 
 
160
 
    
161
 
def get_all_cmds(plugins_override=True):
162
 
    """Return canonical name and class for all registered commands."""
163
 
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
202
    for k, v in d.iteritems():
164
203
        yield k,v
165
204
 
166
205
 
167
206
def get_cmd_object(cmd_name, plugins_override=True):
168
 
    """Return the canonical name and command class for a command.
 
207
    """Return the command object for a command.
169
208
 
170
209
    plugins_override
171
210
        If true, plugin commands can override builtins.
172
211
    """
173
212
    try:
174
 
        cmd = _get_cmd_object(cmd_name, plugins_override)
175
 
        # Allow plugins to extend commands
176
 
        for hook in Command.hooks['extend_command']:
177
 
            hook(cmd)
178
 
        return cmd
 
213
        return _get_cmd_object(cmd_name, plugins_override)
179
214
    except KeyError:
180
215
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
181
216
 
182
217
 
183
218
def _get_cmd_object(cmd_name, plugins_override=True):
184
 
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
185
 
    from bzrlib.externalcommand import ExternalCommand
 
219
    """Get a command object.
186
220
 
 
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
    """
187
226
    # We want only 'ascii' command names, but the user may have typed
188
227
    # in a Unicode name. In that case, they should just get a
189
228
    # 'command not found' error later.
190
229
    # In the future, we may actually support Unicode command names.
191
 
 
192
 
    # first look up this command under the specified name
193
 
    if plugins_override:
 
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:
194
271
        try:
195
 
            return plugin_cmds.get(cmd_name)()
196
 
        except KeyError:
 
272
            return provider.plugin_for_command(cmd_name), provider
 
273
        except errors.NoPluginAvailable:
197
274
            pass
198
 
    cmds = _get_cmd_dict(plugins_override=False)
 
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
281
    try:
200
282
        return cmds[cmd_name]()
201
283
    except KeyError:
202
284
        pass
203
 
    if plugins_override:
204
 
        for key in plugin_cmds.keys():
205
 
            info = plugin_cmds.get_info(key)
206
 
            if cmd_name in info.aliases:
207
 
                return plugin_cmds.get(key)()
208
285
    # look for any command which claims this as an alias
209
286
    for real_cmd_name, cmd_class in cmds.iteritems():
210
287
        if cmd_name in cmd_class.aliases:
211
288
            return cmd_class()
212
 
 
 
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
    cmd_obj = ExternalCommand.find_command(cmd_name)
214
299
    if cmd_obj:
215
300
        return cmd_obj
216
301
 
217
 
    # look for plugins that provide this command but aren't installed
218
 
    for provider in command_providers_registry:
219
 
        try:
220
 
            plugin_metadata = provider.plugin_for_command(cmd_name)
221
 
        except errors.NoPluginAvailable:
222
 
            pass
223
 
        else:
224
 
            raise errors.CommandAvailableInPlugin(cmd_name,
225
 
                                                  plugin_metadata, provider)
226
 
    raise KeyError
 
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
227
314
 
228
315
 
229
316
class Command(object):
291
378
    encoding_type = 'strict'
292
379
 
293
380
    hidden = False
294
 
    
 
381
 
295
382
    def __init__(self):
296
383
        """Construct an instance of this command."""
297
384
        if self.__doc__ == Command.__doc__:
298
385
            warn("No help message set for %r" % self)
299
386
        # List of standard options directly supported
300
387
        self.supported_std_options = []
301
 
 
 
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
    def _maybe_expand_globs(self, file_list):
303
412
        """Glob expand file_list if the platform does not do that itself.
304
 
        
 
413
 
 
414
        Not used anymore, now that the bzr command-line parser globs on
 
415
        Windows.
 
416
 
305
417
        :return: A possibly empty list of unicode paths.
306
418
 
307
419
        Introduced in bzrlib 0.18.
308
420
        """
309
 
        if not file_list:
310
 
            file_list = []
311
 
        if sys.platform == 'win32':
312
 
            file_list = win32utils.glob_expand(file_list)
313
 
        return list(file_list)
 
421
        return file_list
314
422
 
315
423
    def _usage(self):
316
424
        """Return single-line grammar for this command.
331
439
        return s
332
440
 
333
441
    def get_help_text(self, additional_see_also=None, plain=True,
334
 
                      see_also_as_links=False):
 
442
                      see_also_as_links=False, verbose=True):
335
443
        """Return a text string with help for this command.
336
 
        
 
444
 
337
445
        :param additional_see_also: Additional help topics to be
338
446
            cross-referenced.
339
447
        :param plain: if False, raw help (reStructuredText) is
340
448
            returned instead of plain text.
341
449
        :param see_also_as_links: if True, convert items in 'See also'
342
450
            list to internal links (used by bzr_man rstx generator)
 
451
        :param verbose: if True, display the full help, otherwise
 
452
            leave out the descriptive sections and just display
 
453
            usage help (e.g. Purpose, Usage, Options) with a
 
454
            message explaining how to obtain full help.
343
455
        """
344
456
        doc = self.help()
345
457
        if doc is None:
364
476
        result += '\n'
365
477
 
366
478
        # 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
367
483
        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)
368
491
        if options.startswith('Options:'):
369
492
            result += ':' + options
370
493
        elif options.startswith('options:'):
374
497
            result += options
375
498
        result += '\n'
376
499
 
377
 
        # Add the description, indenting it 2 spaces
378
 
        # to match the indentation of the options
379
 
        if sections.has_key(None):
380
 
            text = sections.pop(None)
381
 
            text = '\n  '.join(text.splitlines())
382
 
            result += ':%s:\n  %s\n\n' % ('Description',text)
 
500
        if verbose:
 
501
            # Add the description, indenting it 2 spaces
 
502
            # to match the indentation of the options
 
503
            if sections.has_key(None):
 
504
                text = sections.pop(None)
 
505
                text = '\n  '.join(text.splitlines())
 
506
                result += ':%s:\n  %s\n\n' % ('Description',text)
383
507
 
384
 
        # Add the custom sections (e.g. Examples). Note that there's no need
385
 
        # to indent these as they must be indented already in the source.
386
 
        if sections:
387
 
            for label in order:
388
 
                if sections.has_key(label):
389
 
                    result += ':%s:\n%s\n\n' % (label,sections[label])
 
508
            # Add the custom sections (e.g. Examples). Note that there's no need
 
509
            # to indent these as they must be indented already in the source.
 
510
            if sections:
 
511
                for label in order:
 
512
                    if sections.has_key(label):
 
513
                        result += ':%s:\n%s\n' % (label,sections[label])
 
514
                result += '\n'
 
515
        else:
 
516
            result += ("See bzr help %s for more details and examples.\n\n"
 
517
                % self.name())
390
518
 
391
519
        # Add the aliases, source (plug-in) and see also links, if any
392
520
        if self.aliases:
405
533
                        # so don't create a real link
406
534
                        see_also_links.append(item)
407
535
                    else:
408
 
                        # Use a reST link for this entry
409
 
                        see_also_links.append("`%s`_" % (item,))
 
536
                        # Use a Sphinx link for this entry
 
537
                        link_text = ":doc:`%s <%s-help>`" % (item, item)
 
538
                        see_also_links.append(link_text)
410
539
                see_also = see_also_links
411
540
            result += ':See also: '
412
541
            result += ', '.join(see_also) + '\n'
463
592
 
464
593
    def get_see_also(self, additional_terms=None):
465
594
        """Return a list of help topics that are related to this command.
466
 
        
 
595
 
467
596
        The list is derived from the content of the _see_also attribute. Any
468
597
        duplicates are removed and the result is in lexical order.
469
598
        :param additional_terms: Additional help topics to cross-reference.
490
619
 
491
620
    def _setup_outf(self):
492
621
        """Return a file linked to stdout, which has proper encoding."""
493
 
        # Originally I was using self.stdout, but that looks
494
 
        # *way* too much like sys.stdout
495
 
        if self.encoding_type == 'exact':
496
 
            # force sys.stdout to be binary stream on win32
497
 
            if sys.platform == 'win32':
498
 
                fileno = getattr(sys.stdout, 'fileno', None)
499
 
                if fileno:
500
 
                    import msvcrt
501
 
                    msvcrt.setmode(fileno(), os.O_BINARY)
502
 
            self.outf = sys.stdout
503
 
            return
504
 
 
505
 
        output_encoding = osutils.get_terminal_encoding()
506
 
 
507
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
508
 
                        errors=self.encoding_type)
509
 
        # For whatever reason codecs.getwriter() does not advertise its encoding
510
 
        # it just returns the encoding of the wrapped file, which is completely
511
 
        # bogus. So set the attribute, so we can find the correct encoding later.
512
 
        self.outf.encoding = output_encoding
 
622
        self.outf = ui.ui_factory.make_output_stream(
 
623
            encoding_type=self.encoding_type)
513
624
 
514
625
    def run_argv_aliases(self, argv, alias_argv=None):
515
626
        """Parse the command line and run with extra aliases in alias_argv."""
523
634
        if 'help' in opts:  # e.g. bzr add --help
524
635
            sys.stdout.write(self.get_help_text())
525
636
            return 0
 
637
        if 'usage' in opts:  # e.g. bzr add --usage
 
638
            sys.stdout.write(self.get_help_text(verbose=False))
 
639
            return 0
526
640
        trace.set_verbosity_level(option._verbosity_level)
527
641
        if 'verbose' in self.supported_std_options:
528
642
            opts['verbose'] = trace.is_verbose()
544
658
 
545
659
        self._setup_outf()
546
660
 
547
 
        return self.run(**all_cmd_args)
 
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)
548
666
 
549
667
    def run(self):
550
668
        """Actually run the command.
591
709
        notified.
592
710
        """
593
711
        Hooks.__init__(self)
594
 
        # Introduced in 1.13:
595
 
        # invoked after creating a command object to allow modifications such
596
 
        # as adding or removing options, docs etc. Invoked with the command
597
 
        # object.
598
 
        self['extend_command'] = []
 
712
        self.create_hook(HookPoint('extend_command',
 
713
            "Called after creating a command object to allow modifications "
 
714
            "such as adding or removing options, docs etc. Called with the "
 
715
            "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))
599
735
 
600
736
Command.hooks = CommandHooks()
601
737
 
602
738
 
603
739
def parse_args(command, argv, alias_argv=None):
604
740
    """Parse command line.
605
 
    
 
741
 
606
742
    Arguments and options are parsed at this level before being passed
607
743
    down to specific command handlers.  This routine knows, from a
608
744
    lookup table, something about the available options, what optargs
657
793
                               % (cmd, argname.upper()))
658
794
            else:
659
795
                argdict[argname] = args.pop(0)
660
 
            
 
796
 
661
797
    if args:
662
798
        raise errors.BzrCommandError("extra argument to command %s: %s"
663
799
                                     % (cmd, args[0]))
671
807
 
672
808
    tracer = trace.Trace(count=1, trace=0)
673
809
    sys.settrace(tracer.globaltrace)
674
 
 
675
 
    ret = the_callable(*args, **kwargs)
676
 
 
677
 
    sys.settrace(None)
678
 
    results = tracer.results()
679
 
    results.write_results(show_missing=1, summary=False,
680
 
                          coverdir=dirname)
 
810
    threading.settrace(tracer.globaltrace)
 
811
 
 
812
    try:
 
813
        return exception_to_return_code(the_callable, *args, **kwargs)
 
814
    finally:
 
815
        sys.settrace(None)
 
816
        results = tracer.results()
 
817
        results.write_results(show_missing=1, summary=False,
 
818
                              coverdir=dirname)
681
819
 
682
820
 
683
821
def apply_profiled(the_callable, *args, **kwargs):
688
826
    try:
689
827
        prof = hotshot.Profile(pfname)
690
828
        try:
691
 
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
829
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
 
830
                **kwargs) or 0
692
831
        finally:
693
832
            prof.close()
694
833
        stats = hotshot.stats.load(pfname)
703
842
        os.remove(pfname)
704
843
 
705
844
 
 
845
def exception_to_return_code(the_callable, *args, **kwargs):
 
846
    """UI level helper for profiling and coverage.
 
847
 
 
848
    This transforms exceptions into a return value of 3. As such its only
 
849
    relevant to the UI layer, and should never be called where catching
 
850
    exceptions may be desirable.
 
851
    """
 
852
    try:
 
853
        return the_callable(*args, **kwargs)
 
854
    except (KeyboardInterrupt, Exception), e:
 
855
        # used to handle AssertionError and KeyboardInterrupt
 
856
        # specially here, but hopefully they're handled ok by the logger now
 
857
        exc_info = sys.exc_info()
 
858
        exitcode = trace.report_exception(exc_info, sys.stderr)
 
859
        if os.environ.get('BZR_PDB'):
 
860
            print '**** entering debugger'
 
861
            tb = exc_info[2]
 
862
            import pdb
 
863
            if sys.version_info[:2] < (2, 6):
 
864
                # XXX: we want to do
 
865
                #    pdb.post_mortem(tb)
 
866
                # but because pdb.post_mortem gives bad results for tracebacks
 
867
                # from inside generators, we do it manually.
 
868
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
 
869
 
 
870
                # Setup pdb on the traceback
 
871
                p = pdb.Pdb()
 
872
                p.reset()
 
873
                p.setup(tb.tb_frame, tb)
 
874
                # Point the debugger at the deepest frame of the stack
 
875
                p.curindex = len(p.stack) - 1
 
876
                p.curframe = p.stack[p.curindex][0]
 
877
                # Start the pdb prompt.
 
878
                p.print_stack_entry(p.stack[p.curindex])
 
879
                p.execRcLines()
 
880
                p.cmdloop()
 
881
            else:
 
882
                pdb.post_mortem(tb)
 
883
        return exitcode
 
884
 
 
885
 
706
886
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
707
887
    from bzrlib.lsprof import profile
708
 
    ret, stats = profile(the_callable, *args, **kwargs)
 
888
    ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
709
889
    stats.sort()
710
890
    if filename is None:
711
891
        stats.pprint()
746
926
       The command-line arguments, without the program name from argv[0]
747
927
       These should already be decoded. All library/test code calling
748
928
       run_bzr should be passing valid strings (don't need decoding).
749
 
    
 
929
 
750
930
    Returns a command status or raises an exception.
751
931
 
752
932
    Special master options: these must come before the command because
770
950
 
771
951
    --coverage
772
952
        Generate line coverage report in the specified directory.
 
953
 
 
954
    --concurrency
 
955
        Specify the number of processes that can be run concurrently (selftest).
773
956
    """
 
957
    trace.mutter("bazaar version: " + bzrlib.__version__)
774
958
    argv = list(argv)
775
959
    trace.mutter("bzr arguments: %r", argv)
776
960
 
800
984
            opt_no_aliases = True
801
985
        elif a == '--builtin':
802
986
            opt_builtin = True
 
987
        elif a == '--concurrency':
 
988
            os.environ['BZR_CONCURRENCY'] = argv[i + 1]
 
989
            i += 1
803
990
        elif a == '--coverage':
804
991
            opt_coverage_dir = argv[i + 1]
805
992
            i += 1
809
996
            argv_copy.append(a)
810
997
        i += 1
811
998
 
 
999
    debug.set_debug_flags_from_config()
 
1000
 
812
1001
    argv = argv_copy
813
1002
    if (not argv):
814
1003
        from bzrlib.builtins import cmd_help
864
1053
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
865
1054
        else:
866
1055
            ret = run(*run_argv)
867
 
        if 'memory' in debug.debug_flags:
868
 
            trace.debug_memory('Process status after command:', short=False)
869
1056
        return ret or 0
870
1057
    finally:
871
1058
        # reset, in case we may do other commands later within the same
872
1059
        # process. Commands that want to execute sub-commands must propagate
873
1060
        # --verbose in their own way.
 
1061
        if 'memory' in debug.debug_flags:
 
1062
            trace.debug_memory('Process status after command:', short=False)
874
1063
        option._verbosity_level = saved_verbosity_level
875
1064
 
876
1065
 
894
1083
    return ignore_pipe
895
1084
 
896
1085
 
897
 
def main(argv):
 
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
def main(argv=None):
 
1103
    """Main entry point of command-line interface.
 
1104
 
 
1105
    :param argv: list of unicode command-line arguments similar to sys.argv.
 
1106
        argv[0] is script name usually, it will be ignored.
 
1107
        Don't pass here sys.argv because this list contains plain strings
 
1108
        and not unicode; pass None instead.
 
1109
 
 
1110
    :return: exit code of bzr command.
 
1111
    """
898
1112
    import bzrlib.ui
899
1113
    bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
900
1114
        sys.stdin, sys.stdout, sys.stderr)
901
1115
 
902
1116
    # Is this a final release version? If so, we should suppress warnings
903
1117
    if bzrlib.version_info[3] == 'final':
904
 
        from bzrlib import symbol_versioning
905
 
        symbol_versioning.suppress_deprecation_warnings(override=False)
906
 
    try:
907
 
        user_encoding = osutils.get_user_encoding()
908
 
        argv = [a.decode(user_encoding) for a in argv[1:]]
909
 
    except UnicodeDecodeError:
910
 
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
911
 
                                                            "encoding." % a))
 
1118
        suppress_deprecation_warnings(override=True)
 
1119
    if argv is None:
 
1120
        argv = osutils.get_unicode_argv()
 
1121
    else:
 
1122
        new_argv = []
 
1123
        try:
 
1124
            # ensure all arguments are unicode strings
 
1125
            for a in argv[1:]:
 
1126
                if isinstance(a, unicode):
 
1127
                    new_argv.append(a)
 
1128
                else:
 
1129
                    new_argv.append(a.decode('ascii'))
 
1130
        except UnicodeDecodeError:
 
1131
            raise errors.BzrError("argv should be list of unicode strings.")
 
1132
        argv = new_argv
912
1133
    ret = run_bzr_catch_errors(argv)
 
1134
    bzrlib.ui.ui_factory.log_transport_activity(
 
1135
        display=('bytes' in debug.debug_flags))
913
1136
    trace.mutter("return code %d", ret)
 
1137
    osutils.report_extension_load_failures()
914
1138
    return ret
915
1139
 
916
1140
 
917
1141
def run_bzr_catch_errors(argv):
918
 
    # Note: The except clause logic below should be kept in sync with the
919
 
    # profile() routine in lsprof.py.
920
 
    try:
921
 
        return run_bzr(argv)
922
 
    except (KeyboardInterrupt, Exception), e:
923
 
        # used to handle AssertionError and KeyboardInterrupt
924
 
        # specially here, but hopefully they're handled ok by the logger now
925
 
        exc_info = sys.exc_info()
926
 
        exitcode = trace.report_exception(exc_info, sys.stderr)
927
 
        if os.environ.get('BZR_PDB'):
928
 
            print '**** entering debugger'
929
 
            tb = exc_info[2]
930
 
            import pdb
931
 
            if sys.version_info[:2] < (2, 6):
932
 
                # XXX: we want to do
933
 
                #    pdb.post_mortem(tb)
934
 
                # but because pdb.post_mortem gives bad results for tracebacks
935
 
                # from inside generators, we do it manually.
936
 
                # (http://bugs.python.org/issue4150, fixed in Python 2.6)
937
 
                
938
 
                # Setup pdb on the traceback
939
 
                p = pdb.Pdb()
940
 
                p.reset()
941
 
                p.setup(tb.tb_frame, tb)
942
 
                # Point the debugger at the deepest frame of the stack
943
 
                p.curindex = len(p.stack) - 1
944
 
                p.curframe = p.stack[p.curindex]
945
 
                # Start the pdb prompt.
946
 
                p.print_stack_entry(p.stack[p.curindex])
947
 
                p.execRcLines()
948
 
                p.cmdloop()
949
 
            else:
950
 
                pdb.post_mortem(tb)
951
 
        return exitcode
 
1142
    """Run a bzr command with parameters as described by argv.
 
1143
 
 
1144
    This function assumed that that UI layer is setup, that symbol deprecations
 
1145
    are already applied, and that unicode decoding has already been performed on argv.
 
1146
    """
 
1147
    install_bzr_command_hooks()
 
1148
    return exception_to_return_code(run_bzr, argv)
952
1149
 
953
1150
 
954
1151
def run_bzr_catch_user_errors(argv):
957
1154
    This is used for the test suite, and might be useful for other programs
958
1155
    that want to wrap the commandline interface.
959
1156
    """
 
1157
    install_bzr_command_hooks()
960
1158
    try:
961
1159
        return run_bzr(argv)
962
1160
    except Exception, e:
996
1194
 
997
1195
    def plugin_for_command(self, cmd_name):
998
1196
        '''Takes a command and returns the information for that plugin
999
 
        
1000
 
        :return: A dictionary with all the available information 
 
1197
 
 
1198
        :return: A dictionary with all the available information
1001
1199
        for the requested plugin
1002
1200
        '''
1003
1201
        raise NotImplementedError