~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2006-11-02 10:20:19 UTC
  • mfrom: (2114 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2119.
  • Revision ID: mbp@sourcefrog.net-20061102102019-9a5a02f485dff6f6
merge bzr.dev and reconcile several changes, also some test fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2006 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
20
20
 
21
 
# TODO: Help messages for options.
22
 
 
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
 
23
# would help with validation and shell completion.  They could also provide
 
24
# help/explanation for that argument in a structured way.
 
25
 
 
26
# TODO: Specific "examples" property on commands for consistent formatting.
26
27
 
27
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
28
29
# the profile output behind so it can be interactively examined?
29
30
 
 
31
import os
30
32
import sys
31
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
32
38
from warnings import warn
33
 
from inspect import getdoc
34
39
 
35
40
import bzrlib
36
 
import bzrlib.trace
37
 
from bzrlib.trace import mutter, note, log_error, warning
38
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
39
 
from bzrlib.revisionspec import RevisionSpec
40
 
from bzrlib import BZRDIR
 
41
from bzrlib import (
 
42
    errors,
 
43
    option,
 
44
    osutils,
 
45
    trace,
 
46
    )
 
47
""")
 
48
 
 
49
from bzrlib.symbol_versioning import (
 
50
    deprecated_function,
 
51
    deprecated_method,
 
52
    zero_eight,
 
53
    zero_eleven,
 
54
    )
 
55
# Compatibility
41
56
from bzrlib.option import Option
42
57
 
 
58
 
43
59
plugin_cmds = {}
44
60
 
45
61
 
46
 
def register_command(cmd):
47
 
    "Utility function to help register a command"
 
62
def register_command(cmd, decorate=False):
 
63
    """Utility function to help register a command
 
64
 
 
65
    :param cmd: Command subclass to register
 
66
    :param decorate: If true, allow overriding an existing command
 
67
        of the same name; the old command is returned by this function.
 
68
        Otherwise it is an error to try to override an existing command.
 
69
    """
48
70
    global plugin_cmds
49
71
    k = cmd.__name__
50
72
    if k.startswith("cmd_"):
51
73
        k_unsquished = _unsquish_command_name(k)
52
74
    else:
53
75
        k_unsquished = k
54
 
    if not plugin_cmds.has_key(k_unsquished):
55
 
        plugin_cmds[k_unsquished] = cmd
56
 
        mutter('registered plugin command %s', k_unsquished)      
 
76
    if k_unsquished not in plugin_cmds:
 
77
        plugin_cmds[k_unsquished] = cmd
 
78
        trace.mutter('registered plugin command %s', k_unsquished)
 
79
        if decorate and k_unsquished in builtin_command_names():
 
80
            return _builtin_commands()[k_unsquished]
 
81
    elif decorate:
 
82
        result = plugin_cmds[k_unsquished]
 
83
        plugin_cmds[k_unsquished] = cmd
 
84
        return result
57
85
    else:
58
 
        log_error('Two plugins defined the same command: %r' % k)
59
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
86
        trace.log_error('Two plugins defined the same command: %r' % k)
 
87
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
60
88
 
61
89
 
62
90
def _squish_command_name(cmd):
74
102
    builtins = bzrlib.builtins.__dict__
75
103
    for name in builtins:
76
104
        if name.startswith("cmd_"):
77
 
            real_name = _unsquish_command_name(name)        
 
105
            real_name = _unsquish_command_name(name)
78
106
            r[real_name] = builtins[name]
79
107
    return r
80
 
 
81
108
            
82
109
 
83
110
def builtin_command_names():
111
138
    """
112
139
    from bzrlib.externalcommand import ExternalCommand
113
140
 
114
 
    cmd_name = str(cmd_name)            # not unicode
 
141
    # We want only 'ascii' command names, but the user may have typed
 
142
    # in a Unicode name. In that case, they should just get a
 
143
    # 'command not found' error later.
 
144
    # In the future, we may actually support Unicode command names.
115
145
 
116
146
    # first look up this command under the specified name
117
147
    cmds = _get_cmd_dict(plugins_override=plugins_override)
129
159
    if cmd_obj:
130
160
        return cmd_obj
131
161
 
132
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
162
    raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
133
163
 
134
164
 
135
165
class Command(object):
157
187
        List of argument forms, marked with whether they are optional,
158
188
        repeated, etc.
159
189
 
 
190
                Examples:
 
191
 
 
192
                ['to_location', 'from_branch?', 'file*']
 
193
 
 
194
                'to_location' is required
 
195
                'from_branch' is optional
 
196
                'file' can be specified 0 or more times
 
197
 
160
198
    takes_options
161
199
        List of options that may be given for this command.  These can
162
200
        be either strings, referring to globally-defined options,
165
203
    hidden
166
204
        If true, this command isn't advertised.  This is typically
167
205
        for commands intended for expert users.
 
206
 
 
207
    encoding_type
 
208
        Command objects will get a 'outf' attribute, which has been
 
209
        setup to properly handle encoding of unicode strings.
 
210
        encoding_type determines what will happen when characters cannot
 
211
        be encoded
 
212
            strict - abort if we cannot decode
 
213
            replace - put in a bogus character (typically '?')
 
214
            exact - do not encode sys.stdout
 
215
 
168
216
    """
169
217
    aliases = []
170
218
    takes_args = []
171
219
    takes_options = []
 
220
    encoding_type = 'strict'
172
221
 
173
222
    hidden = False
174
223
    
182
231
 
183
232
        Maps from long option name to option object."""
184
233
        r = dict()
185
 
        r['help'] = Option.OPTIONS['help']
 
234
        r['help'] = option.Option.OPTIONS['help']
186
235
        for o in self.takes_options:
187
 
            if not isinstance(o, Option):
188
 
                o = Option.OPTIONS[o]
 
236
            if isinstance(o, basestring):
 
237
                o = option.Option.OPTIONS[o]
189
238
            r[o.name] = o
190
239
        return r
191
240
 
 
241
    def _setup_outf(self):
 
242
        """Return a file linked to stdout, which has proper encoding."""
 
243
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
244
 
 
245
        # Originally I was using self.stdout, but that looks
 
246
        # *way* too much like sys.stdout
 
247
        if self.encoding_type == 'exact':
 
248
            self.outf = sys.stdout
 
249
            return
 
250
 
 
251
        output_encoding = osutils.get_terminal_encoding()
 
252
 
 
253
        # use 'replace' so that we don't abort if trying to write out
 
254
        # in e.g. the default C locale.
 
255
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
256
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
257
        # it just returns the encoding of the wrapped file, which is completely
 
258
        # bogus. So set the attribute, so we can find the correct encoding later.
 
259
        self.outf.encoding = output_encoding
 
260
 
 
261
    @deprecated_method(zero_eight)
192
262
    def run_argv(self, argv):
193
 
        """Parse command line and run."""
194
 
        args, opts = parse_args(self, argv)
 
263
        """Parse command line and run.
 
264
        
 
265
        See run_argv_aliases for the 0.8 and beyond api.
 
266
        """
 
267
        return self.run_argv_aliases(argv)
 
268
 
 
269
    def run_argv_aliases(self, argv, alias_argv=None):
 
270
        """Parse the command line and run with extra aliases in alias_argv."""
 
271
        if argv is None:
 
272
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
 
273
                 DeprecationWarning, stacklevel=2)
 
274
            argv = []
 
275
        args, opts = parse_args(self, argv, alias_argv)
195
276
        if 'help' in opts:  # e.g. bzr add --help
196
277
            from bzrlib.help import help_on_command
197
278
            help_on_command(self.name())
198
279
            return 0
199
 
        # XXX: This should be handled by the parser
200
 
        allowed_names = self.options().keys()
201
 
        for oname in opts:
202
 
            if oname not in allowed_names:
203
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
204
 
                                      % (oname, self.name()))
205
280
        # mix arguments and options into one dictionary
206
281
        cmdargs = _match_argform(self.name(), self.takes_args, args)
207
282
        cmdopts = {}
211
286
        all_cmd_args = cmdargs.copy()
212
287
        all_cmd_args.update(cmdopts)
213
288
 
 
289
        self._setup_outf()
 
290
 
214
291
        return self.run(**all_cmd_args)
215
 
 
216
292
    
217
293
    def run(self):
218
294
        """Actually run the command.
224
300
        shell error code if not.  It's OK for this method to allow
225
301
        an exception to raise up.
226
302
        """
227
 
        raise NotImplementedError()
228
 
 
 
303
        raise NotImplementedError('no implementation of command %r' 
 
304
                                  % self.name())
229
305
 
230
306
    def help(self):
231
307
        """Return help message for this class."""
 
308
        from inspect import getdoc
232
309
        if self.__doc__ is Command.__doc__:
233
310
            return None
234
311
        return getdoc(self)
236
313
    def name(self):
237
314
        return _unsquish_command_name(self.__class__.__name__)
238
315
 
239
 
 
 
316
    def plugin_name(self):
 
317
        """Get the name of the plugin that provides this command.
 
318
 
 
319
        :return: The name of the plugin or None if the command is builtin.
 
320
        """
 
321
        mod_parts = self.__module__.split('.')
 
322
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
323
            return mod_parts[2]
 
324
        else:
 
325
            return None
 
326
 
 
327
 
 
328
# Technically, this function hasn't been use in a *really* long time
 
329
# but we are only deprecating it now.
 
330
@deprecated_function(zero_eleven)
240
331
def parse_spec(spec):
241
332
    """
242
333
    >>> parse_spec(None)
268
359
        parsed = [spec, None]
269
360
    return parsed
270
361
 
271
 
def parse_args(command, argv):
 
362
def parse_args(command, argv, alias_argv=None):
272
363
    """Parse command line.
273
364
    
274
365
    Arguments and options are parsed at this level before being passed
276
367
    lookup table, something about the available options, what optargs
277
368
    they take, and which commands will accept them.
278
369
    """
279
 
    # TODO: chop up this beast; make it a method of the Command
280
 
    args = []
281
 
    opts = {}
282
 
 
283
 
    cmd_options = command.options()
284
 
    argsover = False
285
 
    while argv:
286
 
        a = argv.pop(0)
287
 
        if argsover:
288
 
            args.append(a)
289
 
            continue
290
 
        elif a == '--':
291
 
            # We've received a standalone -- No more flags
292
 
            argsover = True
293
 
            continue
294
 
        if a[0] == '-':
295
 
            # option names must not be unicode
296
 
            a = str(a)
297
 
            optarg = None
298
 
            if a[1] == '-':
299
 
                mutter("  got option %r" % a)
300
 
                if '=' in a:
301
 
                    optname, optarg = a[2:].split('=', 1)
302
 
                else:
303
 
                    optname = a[2:]
304
 
                if optname not in cmd_options:
305
 
                    raise BzrCommandError('unknown long option %r for command %s' 
306
 
                            % (a, command.name))
307
 
            else:
308
 
                shortopt = a[1:]
309
 
                if shortopt in Option.SHORT_OPTIONS:
310
 
                    # Multi-character options must have a space to delimit
311
 
                    # their value
312
 
                    # ^^^ what does this mean? mbp 20051014
313
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
314
 
                else:
315
 
                    # Single character short options, can be chained,
316
 
                    # and have their value appended to their name
317
 
                    shortopt = a[1:2]
318
 
                    if shortopt not in Option.SHORT_OPTIONS:
319
 
                        # We didn't find the multi-character name, and we
320
 
                        # didn't find the single char name
321
 
                        raise BzrError('unknown short option %r' % a)
322
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
323
 
 
324
 
                    if a[2:]:
325
 
                        # There are extra things on this option
326
 
                        # see if it is the value, or if it is another
327
 
                        # short option
328
 
                        optargfn = Option.OPTIONS[optname].type
329
 
                        if optargfn is None:
330
 
                            # This option does not take an argument, so the
331
 
                            # next entry is another short option, pack it back
332
 
                            # into the list
333
 
                            argv.insert(0, '-' + a[2:])
334
 
                        else:
335
 
                            # This option takes an argument, so pack it
336
 
                            # into the array
337
 
                            optarg = a[2:]
338
 
            
339
 
            if optname in opts:
340
 
                # XXX: Do we ever want to support this, e.g. for -r?
341
 
                raise BzrError('repeated option %r' % a)
342
 
                
343
 
            option_obj = cmd_options[optname]
344
 
            optargfn = option_obj.type
345
 
            if optargfn:
346
 
                if optarg == None:
347
 
                    if not argv:
348
 
                        raise BzrError('option %r needs an argument' % a)
349
 
                    else:
350
 
                        optarg = argv.pop(0)
351
 
                opts[optname] = optargfn(optarg)
352
 
            else:
353
 
                if optarg != None:
354
 
                    raise BzrError('option %r takes no argument' % optname)
355
 
                opts[optname] = True
356
 
        else:
357
 
            args.append(a)
 
370
    # TODO: make it a method of the Command?
 
371
    parser = option.get_optparser(command.options())
 
372
    if alias_argv is not None:
 
373
        args = alias_argv + argv
 
374
    else:
 
375
        args = argv
 
376
 
 
377
    options, args = parser.parse_args(args)
 
378
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if 
 
379
                 v is not option.OptionParser.DEFAULT_VALUE])
358
380
    return args, opts
359
381
 
360
382
 
375
397
                argdict[argname + '_list'] = None
376
398
        elif ap[-1] == '+':
377
399
            if not args:
378
 
                raise BzrCommandError("command %r needs one or more %s"
379
 
                        % (cmd, argname.upper()))
 
400
                raise errors.BzrCommandError("command %r needs one or more %s"
 
401
                                             % (cmd, argname.upper()))
380
402
            else:
381
403
                argdict[argname + '_list'] = args[:]
382
404
                args = []
383
405
        elif ap[-1] == '$': # all but one
384
406
            if len(args) < 2:
385
 
                raise BzrCommandError("command %r needs one or more %s"
386
 
                        % (cmd, argname.upper()))
 
407
                raise errors.BzrCommandError("command %r needs one or more %s"
 
408
                                             % (cmd, argname.upper()))
387
409
            argdict[argname + '_list'] = args[:-1]
388
 
            args[:-1] = []                
 
410
            args[:-1] = []
389
411
        else:
390
412
            # just a plain arg
391
413
            argname = ap
392
414
            if not args:
393
 
                raise BzrCommandError("command %r requires argument %s"
394
 
                        % (cmd, argname.upper()))
 
415
                raise errors.BzrCommandError("command %r requires argument %s"
 
416
                               % (cmd, argname.upper()))
395
417
            else:
396
418
                argdict[argname] = args.pop(0)
397
419
            
398
420
    if args:
399
 
        raise BzrCommandError("extra argument to command %s: %s"
400
 
                              % (cmd, args[0]))
 
421
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
422
                                     % (cmd, args[0]))
401
423
 
402
424
    return argdict
403
425
 
426
448
        os.remove(pfname)
427
449
 
428
450
 
 
451
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
452
    from bzrlib.lsprof import profile
 
453
    import cPickle
 
454
    ret, stats = profile(the_callable, *args, **kwargs)
 
455
    stats.sort()
 
456
    if filename is None:
 
457
        stats.pprint()
 
458
    else:
 
459
        stats.freeze()
 
460
        cPickle.dump(stats, open(filename, 'w'), 2)
 
461
        print 'Profile data written to %r.' % filename
 
462
    return ret
 
463
 
 
464
 
 
465
def get_alias(cmd):
 
466
    """Return an expanded alias, or None if no alias exists"""
 
467
    import bzrlib.config
 
468
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
469
    if (alias):
 
470
        return alias.split(' ')
 
471
    return None
 
472
 
 
473
 
429
474
def run_bzr(argv):
430
475
    """Execute a command.
431
476
 
434
479
    
435
480
    argv
436
481
       The command-line arguments, without the program name from argv[0]
 
482
       These should already be decoded. All library/test code calling
 
483
       run_bzr should be passing valid strings (don't need decoding).
437
484
    
438
485
    Returns a command status or raises an exception.
439
486
 
443
490
    --no-plugins
444
491
        Do not load plugin modules at all
445
492
 
 
493
    --no-aliases
 
494
        Do not allow aliases
 
495
 
446
496
    --builtin
447
497
        Only use builtin commands.  (Plugins are still allowed to change
448
498
        other behaviour.)
449
499
 
450
500
    --profile
451
 
        Run under the Python profiler.
 
501
        Run under the Python hotshot profiler.
 
502
 
 
503
    --lsprof
 
504
        Run under the Python lsprof profiler.
452
505
    """
453
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
506
    argv = list(argv)
454
507
 
455
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
508
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
509
                opt_no_aliases = False
 
510
    opt_lsprof_file = None
456
511
 
457
512
    # --no-plugins is handled specially at a very early stage. We need
458
513
    # to load plugins before doing other command parsing so that they
459
514
    # can override commands, but this needs to happen first.
460
515
 
461
 
    for a in argv:
 
516
    argv_copy = []
 
517
    i = 0
 
518
    while i < len(argv):
 
519
        a = argv[i]
462
520
        if a == '--profile':
463
521
            opt_profile = True
 
522
        elif a == '--lsprof':
 
523
            opt_lsprof = True
 
524
        elif a == '--lsprof-file':
 
525
            opt_lsprof = True
 
526
            opt_lsprof_file = argv[i + 1]
 
527
            i += 1
464
528
        elif a == '--no-plugins':
465
529
            opt_no_plugins = True
 
530
        elif a == '--no-aliases':
 
531
            opt_no_aliases = True
466
532
        elif a == '--builtin':
467
533
            opt_builtin = True
 
534
        elif a in ('--quiet', '-q'):
 
535
            trace.be_quiet()
468
536
        else:
469
 
            break
470
 
        argv.remove(a)
 
537
            argv_copy.append(a)
 
538
        i += 1
471
539
 
472
 
    if (not argv) or (argv[0] == '--help'):
473
 
        from bzrlib.help import help
474
 
        if len(argv) > 1:
475
 
            help(argv[1])
476
 
        else:
477
 
            help()
 
540
    argv = argv_copy
 
541
    if (not argv):
 
542
        from bzrlib.builtins import cmd_help
 
543
        cmd_help().run_argv_aliases([])
478
544
        return 0
479
545
 
480
546
    if argv[0] == '--version':
481
 
        from bzrlib.builtins import show_version
 
547
        from bzrlib.version import show_version
482
548
        show_version()
483
549
        return 0
484
550
        
485
551
    if not opt_no_plugins:
486
552
        from bzrlib.plugin import load_plugins
487
553
        load_plugins()
488
 
 
489
 
    cmd = str(argv.pop(0))
 
554
    else:
 
555
        from bzrlib.plugin import disable_plugins
 
556
        disable_plugins()
 
557
 
 
558
    alias_argv = None
 
559
 
 
560
    if not opt_no_aliases:
 
561
        alias_argv = get_alias(argv[0])
 
562
        if alias_argv:
 
563
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
564
            argv[0] = alias_argv.pop(0)
 
565
 
 
566
    cmd = argv.pop(0)
 
567
    # We want only 'ascii' command names, but the user may have typed
 
568
    # in a Unicode name. In that case, they should just get a
 
569
    # 'command not found' error later.
490
570
 
491
571
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
492
 
 
493
 
    if opt_profile:
494
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
 
572
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
573
        run = cmd_obj.run_argv
 
574
        run_argv = [argv]
495
575
    else:
496
 
        ret = cmd_obj.run_argv(argv)
497
 
    return ret or 0
 
576
        run = cmd_obj.run_argv_aliases
 
577
        run_argv = [argv, alias_argv]
 
578
 
 
579
    try:
 
580
        if opt_lsprof:
 
581
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
582
        elif opt_profile:
 
583
            ret = apply_profiled(run, *run_argv)
 
584
        else:
 
585
            ret = run(*run_argv)
 
586
        return ret or 0
 
587
    finally:
 
588
        # reset, in case we may do other commands later within the same process
 
589
        trace.be_quiet(False)
 
590
 
 
591
def display_command(func):
 
592
    """Decorator that suppresses pipe/interrupt errors."""
 
593
    def ignore_pipe(*args, **kwargs):
 
594
        try:
 
595
            result = func(*args, **kwargs)
 
596
            sys.stdout.flush()
 
597
            return result
 
598
        except IOError, e:
 
599
            if getattr(e, 'errno', None) is None:
 
600
                raise
 
601
            if e.errno != errno.EPIPE:
 
602
                # Win32 raises IOError with errno=0 on a broken pipe
 
603
                if sys.platform != 'win32' or e.errno != 0:
 
604
                    raise
 
605
            pass
 
606
        except KeyboardInterrupt:
 
607
            pass
 
608
    return ignore_pipe
498
609
 
499
610
 
500
611
def main(argv):
501
612
    import bzrlib.ui
502
 
    bzrlib.trace.log_startup(argv)
503
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
504
 
 
505
 
    return run_bzr_catch_errors(argv[1:])
 
613
    from bzrlib.ui.text import TextUIFactory
 
614
    bzrlib.ui.ui_factory = TextUIFactory()
 
615
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
616
    ret = run_bzr_catch_errors(argv)
 
617
    trace.mutter("return code %d", ret)
 
618
    return ret
506
619
 
507
620
 
508
621
def run_bzr_catch_errors(argv):
509
622
    try:
510
 
        try:
511
 
            return run_bzr(argv)
512
 
        finally:
513
 
            # do this here inside the exception wrappers to catch EPIPE
514
 
            sys.stdout.flush()
515
 
    except BzrCommandError, e:
516
 
        # command line syntax error, etc
517
 
        log_error(str(e))
518
 
        return 1
519
 
    except BzrError, e:
520
 
        bzrlib.trace.log_exception()
521
 
        return 1
522
 
    except AssertionError, e:
523
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
 
623
        return run_bzr(argv)
 
624
        # do this here inside the exception wrappers to catch EPIPE
 
625
        sys.stdout.flush()
 
626
    except (KeyboardInterrupt, Exception), e:
 
627
        # used to handle AssertionError and KeyboardInterrupt
 
628
        # specially here, but hopefully they're handled ok by the logger now
 
629
        trace.report_exception(sys.exc_info(), sys.stderr)
 
630
        if os.environ.get('BZR_PDB'):
 
631
            print '**** entering debugger'
 
632
            import pdb
 
633
            pdb.post_mortem(sys.exc_traceback)
524
634
        return 3
525
 
    except KeyboardInterrupt, e:
526
 
        bzrlib.trace.log_exception('interrupted')
527
 
        return 2
528
 
    except Exception, e:
529
 
        import errno
530
 
        if (isinstance(e, IOError) 
531
 
            and hasattr(e, 'errno')
532
 
            and e.errno == errno.EPIPE):
533
 
            bzrlib.trace.note('broken pipe')
534
 
            return 2
535
 
        else:
536
 
            ## import pdb
537
 
            ## pdb.pm()
538
 
            bzrlib.trace.log_exception()
539
 
            return 2
540
635
 
541
636
if __name__ == '__main__':
542
637
    sys.exit(main(sys.argv))