~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2007-04-30 05:13:58 UTC
  • mfrom: (2470 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2471.
  • Revision ID: robertc@robertcollins.net-20070430051358-8cp7kvp1q0tqhxx0
Merge Johns fix for bug 110399.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
 
1
# Copyright (C) 2006 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
28
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
29
29
# the profile output behind so it can be interactively examined?
30
30
 
 
31
import os
31
32
import sys
32
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
33
38
from warnings import warn
34
 
import errno
35
39
 
36
40
import bzrlib
37
 
from bzrlib.errors import (BzrError,
38
 
                           BzrCheckError,
39
 
                           BzrCommandError,
40
 
                           BzrOptionError,
41
 
                           NotBranchError)
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    trace,
 
47
    )
 
48
""")
 
49
 
 
50
from bzrlib.symbol_versioning import (
 
51
    deprecated_function,
 
52
    deprecated_method,
 
53
    zero_eight,
 
54
    zero_eleven,
 
55
    )
 
56
# Compatibility
42
57
from bzrlib.option import Option
43
 
from bzrlib.revisionspec import RevisionSpec
44
 
from bzrlib.symbol_versioning import *
45
 
import bzrlib.trace
46
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
58
 
47
59
 
48
60
plugin_cmds = {}
49
61
 
62
74
        k_unsquished = _unsquish_command_name(k)
63
75
    else:
64
76
        k_unsquished = k
65
 
    if not plugin_cmds.has_key(k_unsquished):
 
77
    if k_unsquished not in plugin_cmds:
66
78
        plugin_cmds[k_unsquished] = cmd
67
 
        mutter('registered plugin command %s', k_unsquished)
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
68
80
        if decorate and k_unsquished in builtin_command_names():
69
81
            return _builtin_commands()[k_unsquished]
70
82
    elif decorate:
72
84
        plugin_cmds[k_unsquished] = cmd
73
85
        return result
74
86
    else:
75
 
        log_error('Two plugins defined the same command: %r' % k)
76
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
77
89
 
78
90
 
79
91
def _squish_command_name(cmd):
125
137
    plugins_override
126
138
        If true, plugin commands can override builtins.
127
139
    """
 
140
    try:
 
141
        return _get_cmd_object(cmd_name, plugins_override)
 
142
    except KeyError:
 
143
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
144
 
 
145
 
 
146
def _get_cmd_object(cmd_name, plugins_override=True):
 
147
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
128
148
    from bzrlib.externalcommand import ExternalCommand
129
149
 
130
 
    cmd_name = str(cmd_name)            # not unicode
 
150
    # We want only 'ascii' command names, but the user may have typed
 
151
    # in a Unicode name. In that case, they should just get a
 
152
    # 'command not found' error later.
 
153
    # In the future, we may actually support Unicode command names.
131
154
 
132
155
    # first look up this command under the specified name
133
156
    cmds = _get_cmd_dict(plugins_override=plugins_override)
144
167
    cmd_obj = ExternalCommand.find_command(cmd_name)
145
168
    if cmd_obj:
146
169
        return cmd_obj
147
 
 
148
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
170
    raise KeyError
149
171
 
150
172
 
151
173
class Command(object):
189
211
    hidden
190
212
        If true, this command isn't advertised.  This is typically
191
213
        for commands intended for expert users.
 
214
 
 
215
    encoding_type
 
216
        Command objects will get a 'outf' attribute, which has been
 
217
        setup to properly handle encoding of unicode strings.
 
218
        encoding_type determines what will happen when characters cannot
 
219
        be encoded
 
220
            strict - abort if we cannot decode
 
221
            replace - put in a bogus character (typically '?')
 
222
            exact - do not encode sys.stdout
 
223
 
 
224
            NOTE: by default on Windows, sys.stdout is opened as a text
 
225
            stream, therefore LF line-endings are converted to CRLF.
 
226
            When a command uses encoding_type = 'exact', then
 
227
            sys.stdout is forced to be a binary stream, and line-endings
 
228
            will not mangled.
 
229
 
192
230
    """
193
231
    aliases = []
194
232
    takes_args = []
195
233
    takes_options = []
 
234
    encoding_type = 'strict'
196
235
 
197
236
    hidden = False
198
237
    
201
240
        if self.__doc__ == Command.__doc__:
202
241
            warn("No help message set for %r" % self)
203
242
 
 
243
    def _usage(self):
 
244
        """Return single-line grammar for this command.
 
245
 
 
246
        Only describes arguments, not options.
 
247
        """
 
248
        s = 'bzr ' + self.name() + ' '
 
249
        for aname in self.takes_args:
 
250
            aname = aname.upper()
 
251
            if aname[-1] in ['$', '+']:
 
252
                aname = aname[:-1] + '...'
 
253
            elif aname[-1] == '?':
 
254
                aname = '[' + aname[:-1] + ']'
 
255
            elif aname[-1] == '*':
 
256
                aname = '[' + aname[:-1] + '...]'
 
257
            s += aname + ' '
 
258
                
 
259
        assert s[-1] == ' '
 
260
        s = s[:-1]
 
261
        return s
 
262
 
 
263
    def get_help_text(self, additional_see_also=None):
 
264
        """Return a text string with help for this command.
 
265
        
 
266
        :param additional_see_also: Additional help topics to be
 
267
            cross-referenced.
 
268
        """
 
269
        doc = self.help()
 
270
        if doc is None:
 
271
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
 
272
 
 
273
        result = ""
 
274
        result += 'usage: %s\n' % self._usage()
 
275
 
 
276
        if self.aliases:
 
277
            result += 'aliases:\n'
 
278
            result += ', '.join(self.aliases) + '\n'
 
279
 
 
280
        result += '\n'
 
281
 
 
282
        plugin_name = self.plugin_name()
 
283
        if plugin_name is not None:
 
284
            result += '(From plugin "%s")' % plugin_name
 
285
            result += '\n\n'
 
286
 
 
287
        result += doc
 
288
        if result[-1] != '\n':
 
289
            result += '\n'
 
290
        result += '\n'
 
291
        result += option.get_optparser(self.options()).format_option_help()
 
292
        see_also = self.get_see_also(additional_see_also)
 
293
        if see_also:
 
294
            result += '\nSee also: '
 
295
            result += ', '.join(see_also)
 
296
            result += '\n'
 
297
        return result
 
298
 
 
299
    def get_help_topic(self):
 
300
        """Return the commands help topic - its name."""
 
301
        return self.name()
 
302
 
 
303
    def get_see_also(self, additional_terms=None):
 
304
        """Return a list of help topics that are related to this ommand.
 
305
        
 
306
        The list is derived from the content of the _see_also attribute. Any
 
307
        duplicates are removed and the result is in lexical order.
 
308
        :param additional_terms: Additional help topics to cross-reference.
 
309
        :return: A list of help topics.
 
310
        """
 
311
        see_also = set(getattr(self, '_see_also', []))
 
312
        if additional_terms:
 
313
            see_also.update(additional_terms)
 
314
        return sorted(see_also)
 
315
 
204
316
    def options(self):
205
317
        """Return dict of valid options for this command.
206
318
 
207
319
        Maps from long option name to option object."""
208
320
        r = dict()
209
 
        r['help'] = Option.OPTIONS['help']
 
321
        r['help'] = option.Option.OPTIONS['help']
210
322
        for o in self.takes_options:
211
 
            if not isinstance(o, Option):
212
 
                o = Option.OPTIONS[o]
 
323
            if isinstance(o, basestring):
 
324
                o = option.Option.OPTIONS[o]
213
325
            r[o.name] = o
214
326
        return r
215
327
 
216
 
    @deprecated_method(zero_eight)
217
 
    def run_argv(self, argv):
218
 
        """Parse command line and run.
219
 
        
220
 
        See run_argv_aliases for the 0.8 and beyond api.
221
 
        """
222
 
        return self.run_argv_aliases(argv)
 
328
    def _setup_outf(self):
 
329
        """Return a file linked to stdout, which has proper encoding."""
 
330
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
331
 
 
332
        # Originally I was using self.stdout, but that looks
 
333
        # *way* too much like sys.stdout
 
334
        if self.encoding_type == 'exact':
 
335
            # force sys.stdout to be binary stream on win32
 
336
            if sys.platform == 'win32':
 
337
                fileno = getattr(sys.stdout, 'fileno', None)
 
338
                if fileno:
 
339
                    import msvcrt
 
340
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
341
            self.outf = sys.stdout
 
342
            return
 
343
 
 
344
        output_encoding = osutils.get_terminal_encoding()
 
345
 
 
346
        # use 'replace' so that we don't abort if trying to write out
 
347
        # in e.g. the default C locale.
 
348
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
349
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
350
        # it just returns the encoding of the wrapped file, which is completely
 
351
        # bogus. So set the attribute, so we can find the correct encoding later.
 
352
        self.outf.encoding = output_encoding
223
353
 
224
354
    def run_argv_aliases(self, argv, alias_argv=None):
225
355
        """Parse the command line and run with extra aliases in alias_argv."""
 
356
        if argv is None:
 
357
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
358
                 DeprecationWarning, stacklevel=2)
 
359
            argv = []
226
360
        args, opts = parse_args(self, argv, alias_argv)
227
361
        if 'help' in opts:  # e.g. bzr add --help
228
 
            from bzrlib.help import help_on_command
229
 
            help_on_command(self.name())
 
362
            sys.stdout.write(self.get_help_text())
230
363
            return 0
231
 
        # XXX: This should be handled by the parser
232
 
        allowed_names = self.options().keys()
233
 
        for oname in opts:
234
 
            if oname not in allowed_names:
235
 
                raise BzrCommandError("option '--%s' is not allowed for"
236
 
                                      " command %r" % (oname, self.name()))
237
364
        # mix arguments and options into one dictionary
238
365
        cmdargs = _match_argform(self.name(), self.takes_args, args)
239
366
        cmdopts = {}
243
370
        all_cmd_args = cmdargs.copy()
244
371
        all_cmd_args.update(cmdopts)
245
372
 
 
373
        self._setup_outf()
 
374
 
246
375
        return self.run(**all_cmd_args)
247
376
    
248
377
    def run(self):
255
384
        shell error code if not.  It's OK for this method to allow
256
385
        an exception to raise up.
257
386
        """
258
 
        raise NotImplementedError('no implementation of command %r' 
 
387
        raise NotImplementedError('no implementation of command %r'
259
388
                                  % self.name())
260
389
 
261
390
    def help(self):
268
397
    def name(self):
269
398
        return _unsquish_command_name(self.__class__.__name__)
270
399
 
271
 
 
 
400
    def plugin_name(self):
 
401
        """Get the name of the plugin that provides this command.
 
402
 
 
403
        :return: The name of the plugin or None if the command is builtin.
 
404
        """
 
405
        mod_parts = self.__module__.split('.')
 
406
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
407
            return mod_parts[2]
 
408
        else:
 
409
            return None
 
410
 
 
411
 
 
412
# Technically, this function hasn't been use in a *really* long time
 
413
# but we are only deprecating it now.
 
414
@deprecated_function(zero_eleven)
272
415
def parse_spec(spec):
273
416
    """
274
417
    >>> parse_spec(None)
308
451
    lookup table, something about the available options, what optargs
309
452
    they take, and which commands will accept them.
310
453
    """
311
 
    # TODO: chop up this beast; make it a method of the Command
312
 
    args = []
313
 
    opts = {}
314
 
    alias_opts = {}
315
 
 
316
 
    cmd_options = command.options()
317
 
    argsover = False
318
 
    proc_aliasarg = True # Are we processing alias_argv now?
319
 
    for proc_argv in alias_argv, argv:
320
 
        while proc_argv:
321
 
            a = proc_argv.pop(0)
322
 
            if argsover:
323
 
                args.append(a)
324
 
                continue
325
 
            elif a == '--':
326
 
                # We've received a standalone -- No more flags
327
 
                argsover = True
328
 
                continue
329
 
            if a[0] == '-':
330
 
                # option names must not be unicode
331
 
                a = str(a)
332
 
                optarg = None
333
 
                if a[1] == '-':
334
 
                    mutter("  got option %r", a)
335
 
                    if '=' in a:
336
 
                        optname, optarg = a[2:].split('=', 1)
337
 
                    else:
338
 
                        optname = a[2:]
339
 
                    if optname not in cmd_options:
340
 
                        raise BzrOptionError('unknown long option %r for'
341
 
                                             ' command %s' % 
342
 
                                             (a, command.name()))
343
 
                else:
344
 
                    shortopt = a[1:]
345
 
                    if shortopt in Option.SHORT_OPTIONS:
346
 
                        # Multi-character options must have a space to delimit
347
 
                        # their value
348
 
                        # ^^^ what does this mean? mbp 20051014
349
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
350
 
                    else:
351
 
                        # Single character short options, can be chained,
352
 
                        # and have their value appended to their name
353
 
                        shortopt = a[1:2]
354
 
                        if shortopt not in Option.SHORT_OPTIONS:
355
 
                            # We didn't find the multi-character name, and we
356
 
                            # didn't find the single char name
357
 
                            raise BzrError('unknown short option %r' % a)
358
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
359
 
 
360
 
                        if a[2:]:
361
 
                            # There are extra things on this option
362
 
                            # see if it is the value, or if it is another
363
 
                            # short option
364
 
                            optargfn = Option.OPTIONS[optname].type
365
 
                            if optargfn is None:
366
 
                                # This option does not take an argument, so the
367
 
                                # next entry is another short option, pack it
368
 
                                # back into the list
369
 
                                proc_argv.insert(0, '-' + a[2:])
370
 
                            else:
371
 
                                # This option takes an argument, so pack it
372
 
                                # into the array
373
 
                                optarg = a[2:]
374
 
                
375
 
                    if optname not in cmd_options:
376
 
                        raise BzrOptionError('unknown short option %r for'
377
 
                                             ' command %s' % 
378
 
                                             (shortopt, command.name()))
379
 
                if optname in opts:
380
 
                    # XXX: Do we ever want to support this, e.g. for -r?
381
 
                    if proc_aliasarg:
382
 
                        raise BzrError('repeated option %r' % a)
383
 
                    elif optname in alias_opts:
384
 
                        # Replace what's in the alias with what's in the real
385
 
                        # argument
386
 
                        del alias_opts[optname]
387
 
                        del opts[optname]
388
 
                        proc_argv.insert(0, a)
389
 
                        continue
390
 
                    else:
391
 
                        raise BzrError('repeated option %r' % a)
392
 
                    
393
 
                option_obj = cmd_options[optname]
394
 
                optargfn = option_obj.type
395
 
                if optargfn:
396
 
                    if optarg == None:
397
 
                        if not proc_argv:
398
 
                            raise BzrError('option %r needs an argument' % a)
399
 
                        else:
400
 
                            optarg = proc_argv.pop(0)
401
 
                    opts[optname] = optargfn(optarg)
402
 
                    if proc_aliasarg:
403
 
                        alias_opts[optname] = optargfn(optarg)
404
 
                else:
405
 
                    if optarg != None:
406
 
                        raise BzrError('option %r takes no argument' % optname)
407
 
                    opts[optname] = True
408
 
                    if proc_aliasarg:
409
 
                        alias_opts[optname] = True
410
 
            else:
411
 
                args.append(a)
412
 
        proc_aliasarg = False # Done with alias argv
 
454
    # TODO: make it a method of the Command?
 
455
    parser = option.get_optparser(command.options())
 
456
    if alias_argv is not None:
 
457
        args = alias_argv + argv
 
458
    else:
 
459
        args = argv
 
460
 
 
461
    options, args = parser.parse_args(args)
 
462
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
463
                 v is not option.OptionParser.DEFAULT_VALUE])
413
464
    return args, opts
414
465
 
415
466
 
430
481
                argdict[argname + '_list'] = None
431
482
        elif ap[-1] == '+':
432
483
            if not args:
433
 
                raise BzrCommandError("command %r needs one or more %s"
434
 
                        % (cmd, argname.upper()))
 
484
                raise errors.BzrCommandError("command %r needs one or more %s"
 
485
                                             % (cmd, argname.upper()))
435
486
            else:
436
487
                argdict[argname + '_list'] = args[:]
437
488
                args = []
438
489
        elif ap[-1] == '$': # all but one
439
490
            if len(args) < 2:
440
 
                raise BzrCommandError("command %r needs one or more %s"
441
 
                        % (cmd, argname.upper()))
 
491
                raise errors.BzrCommandError("command %r needs one or more %s"
 
492
                                             % (cmd, argname.upper()))
442
493
            argdict[argname + '_list'] = args[:-1]
443
 
            args[:-1] = []                
 
494
            args[:-1] = []
444
495
        else:
445
496
            # just a plain arg
446
497
            argname = ap
447
498
            if not args:
448
 
                raise BzrCommandError("command %r requires argument %s"
449
 
                        % (cmd, argname.upper()))
 
499
                raise errors.BzrCommandError("command %r requires argument %s"
 
500
                               % (cmd, argname.upper()))
450
501
            else:
451
502
                argdict[argname] = args.pop(0)
452
503
            
453
504
    if args:
454
 
        raise BzrCommandError("extra argument to command %s: %s"
455
 
                              % (cmd, args[0]))
 
505
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
506
                                     % (cmd, args[0]))
456
507
 
457
508
    return argdict
458
509
 
495
546
    return ret
496
547
 
497
548
 
498
 
def get_alias(cmd):
499
 
    """Return an expanded alias, or None if no alias exists"""
500
 
    import bzrlib.config
501
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
549
def get_alias(cmd, config=None):
 
550
    """Return an expanded alias, or None if no alias exists.
 
551
 
 
552
    cmd
 
553
        Command to be checked for an alias.
 
554
    config
 
555
        Used to specify an alternative config to use,
 
556
        which is especially useful for testing.
 
557
        If it is unspecified, the global config will be used.
 
558
    """
 
559
    if config is None:
 
560
        import bzrlib.config
 
561
        config = bzrlib.config.GlobalConfig()
 
562
    alias = config.get_alias(cmd)
502
563
    if (alias):
503
 
        return alias.split(' ')
 
564
        import shlex
 
565
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
504
566
    return None
505
567
 
506
568
 
512
574
    
513
575
    argv
514
576
       The command-line arguments, without the program name from argv[0]
 
577
       These should already be decoded. All library/test code calling
 
578
       run_bzr should be passing valid strings (don't need decoding).
515
579
    
516
580
    Returns a command status or raises an exception.
517
581
 
534
598
    --lsprof
535
599
        Run under the Python lsprof profiler.
536
600
    """
537
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
601
    argv = list(argv)
 
602
    trace.mutter("bzr arguments: %r", argv)
538
603
 
539
604
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
540
605
                opt_no_aliases = False
553
618
        elif a == '--lsprof':
554
619
            opt_lsprof = True
555
620
        elif a == '--lsprof-file':
 
621
            opt_lsprof = True
556
622
            opt_lsprof_file = argv[i + 1]
557
623
            i += 1
558
624
        elif a == '--no-plugins':
562
628
        elif a == '--builtin':
563
629
            opt_builtin = True
564
630
        elif a in ('--quiet', '-q'):
565
 
            be_quiet()
 
631
            trace.be_quiet()
 
632
        elif a.startswith('-D'):
 
633
            debug.debug_flags.add(a[2:])
566
634
        else:
567
635
            argv_copy.append(a)
568
636
        i += 1
574
642
        return 0
575
643
 
576
644
    if argv[0] == '--version':
577
 
        from bzrlib.builtins import show_version
 
645
        from bzrlib.version import show_version
578
646
        show_version()
579
647
        return 0
580
648
        
593
661
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
594
662
            argv[0] = alias_argv.pop(0)
595
663
 
596
 
    cmd = str(argv.pop(0))
 
664
    cmd = argv.pop(0)
 
665
    # We want only 'ascii' command names, but the user may have typed
 
666
    # in a Unicode name. In that case, they should just get a
 
667
    # 'command not found' error later.
597
668
 
598
669
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
599
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
600
 
        run = cmd_obj.run_argv
601
 
        run_argv = [argv]
602
 
    else:
603
 
        run = cmd_obj.run_argv_aliases
604
 
        run_argv = [argv, alias_argv]
 
670
    run = cmd_obj.run_argv_aliases
 
671
    run_argv = [argv, alias_argv]
605
672
 
606
673
    try:
607
674
        if opt_lsprof:
613
680
        return ret or 0
614
681
    finally:
615
682
        # reset, in case we may do other commands later within the same process
616
 
        be_quiet(False)
 
683
        trace.be_quiet(False)
617
684
 
618
685
def display_command(func):
619
686
    """Decorator that suppresses pipe/interrupt errors."""
623
690
            sys.stdout.flush()
624
691
            return result
625
692
        except IOError, e:
626
 
            if not hasattr(e, 'errno'):
 
693
            if getattr(e, 'errno', None) is None:
627
694
                raise
628
695
            if e.errno != errno.EPIPE:
629
 
                raise
 
696
                # Win32 raises IOError with errno=0 on a broken pipe
 
697
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
698
                    raise
630
699
            pass
631
700
        except KeyboardInterrupt:
632
701
            pass
636
705
def main(argv):
637
706
    import bzrlib.ui
638
707
    from bzrlib.ui.text import TextUIFactory
639
 
    ## bzrlib.trace.enable_default_logging()
640
 
    bzrlib.trace.log_startup(argv)
641
708
    bzrlib.ui.ui_factory = TextUIFactory()
642
 
    ret = run_bzr_catch_errors(argv[1:])
643
 
    mutter("return code %d", ret)
 
709
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
710
    ret = run_bzr_catch_errors(argv)
 
711
    trace.mutter("return code %d", ret)
644
712
    return ret
645
713
 
646
714
 
651
719
        finally:
652
720
            # do this here inside the exception wrappers to catch EPIPE
653
721
            sys.stdout.flush()
654
 
    except Exception, e:
 
722
    except (KeyboardInterrupt, Exception), e:
655
723
        # used to handle AssertionError and KeyboardInterrupt
656
724
        # specially here, but hopefully they're handled ok by the logger now
657
 
        import errno
658
 
        if (isinstance(e, IOError) 
659
 
            and hasattr(e, 'errno')
660
 
            and e.errno == errno.EPIPE):
661
 
            bzrlib.trace.note('broken pipe')
662
 
            return 3
 
725
        trace.report_exception(sys.exc_info(), sys.stderr)
 
726
        if os.environ.get('BZR_PDB'):
 
727
            print '**** entering debugger'
 
728
            import pdb
 
729
            pdb.post_mortem(sys.exc_traceback)
 
730
        return 3
 
731
 
 
732
 
 
733
class HelpCommandIndex(object):
 
734
    """A index for bzr help that returns commands."""
 
735
 
 
736
    def __init__(self):
 
737
        self.prefix = 'commands/'
 
738
 
 
739
    def get_topics(self, topic):
 
740
        """Search for topic amongst commands.
 
741
 
 
742
        :param topic: A topic to search for.
 
743
        :return: A list which is either empty or contains a single
 
744
            Command entry.
 
745
        """
 
746
        if topic and topic.startswith(self.prefix):
 
747
            topic = topic[len(self.prefix):]
 
748
        try:
 
749
            cmd = _get_cmd_object(topic)
 
750
        except KeyError:
 
751
            return []
663
752
        else:
664
 
            bzrlib.trace.log_exception()
665
 
            if os.environ.get('BZR_PDB'):
666
 
                print '**** entering debugger'
667
 
                import pdb
668
 
                pdb.post_mortem(sys.exc_traceback)
669
 
            return 3
 
753
            return [cmd]
 
754
 
670
755
 
671
756
if __name__ == '__main__':
672
757
    sys.exit(main(sys.argv))