/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-02-28 04:35:06 UTC
  • mfrom: (1551.2.15 Aaron's small fixes)
  • Revision ID: pqm@pqm.ubuntu.com-20060228043506-6627101e946abb2d
Fix diff support of checkouts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
 
 
18
# TODO: probably should say which arguments are candidates for glob
 
19
# expansion on windows and do that at the command level.
 
20
 
 
21
# TODO: Help messages for options.
 
22
 
 
23
# TODO: Define arguments by objects, rather than just using names.
 
24
# Those objects can specify the expected type of the argument, which
 
25
# would help with validation and shell completion.
 
26
 
 
27
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
 
28
# the profile output behind so it can be interactively examined?
 
29
 
 
30
import sys
 
31
import os
 
32
from warnings import warn
 
33
from inspect import getdoc
 
34
import errno
 
35
 
 
36
import bzrlib
 
37
import bzrlib.trace
 
38
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
39
from bzrlib.errors import (BzrError, 
 
40
                           BzrCheckError,
 
41
                           BzrCommandError,
 
42
                           BzrOptionError,
 
43
                           NotBranchError)
 
44
from bzrlib.revisionspec import RevisionSpec
 
45
from bzrlib.option import Option
 
46
 
 
47
plugin_cmds = {}
 
48
 
 
49
 
 
50
def register_command(cmd, decorate=False):
 
51
    "Utility function to help register a command"
 
52
    global plugin_cmds
 
53
    k = cmd.__name__
 
54
    if k.startswith("cmd_"):
 
55
        k_unsquished = _unsquish_command_name(k)
 
56
    else:
 
57
        k_unsquished = k
 
58
    if not plugin_cmds.has_key(k_unsquished):
 
59
        plugin_cmds[k_unsquished] = cmd
 
60
        mutter('registered plugin command %s', k_unsquished)      
 
61
        if decorate and k_unsquished in builtin_command_names():
 
62
            return _builtin_commands()[k_unsquished]
 
63
    elif decorate:
 
64
        result = plugin_cmds[k_unsquished]
 
65
        plugin_cmds[k_unsquished] = cmd
 
66
        return result
 
67
    else:
 
68
        log_error('Two plugins defined the same command: %r' % k)
 
69
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
70
 
 
71
 
 
72
def _squish_command_name(cmd):
 
73
    return 'cmd_' + cmd.replace('-', '_')
 
74
 
 
75
 
 
76
def _unsquish_command_name(cmd):
 
77
    assert cmd.startswith("cmd_")
 
78
    return cmd[4:].replace('_','-')
 
79
 
 
80
 
 
81
def _builtin_commands():
 
82
    import bzrlib.builtins
 
83
    r = {}
 
84
    builtins = bzrlib.builtins.__dict__
 
85
    for name in builtins:
 
86
        if name.startswith("cmd_"):
 
87
            real_name = _unsquish_command_name(name)        
 
88
            r[real_name] = builtins[name]
 
89
    return r
 
90
 
 
91
            
 
92
 
 
93
def builtin_command_names():
 
94
    """Return list of builtin command names."""
 
95
    return _builtin_commands().keys()
 
96
    
 
97
 
 
98
def plugin_command_names():
 
99
    return plugin_cmds.keys()
 
100
 
 
101
 
 
102
def _get_cmd_dict(plugins_override=True):
 
103
    """Return name->class mapping for all commands."""
 
104
    d = _builtin_commands()
 
105
    if plugins_override:
 
106
        d.update(plugin_cmds)
 
107
    return d
 
108
 
 
109
    
 
110
def get_all_cmds(plugins_override=True):
 
111
    """Return canonical name and class for all registered commands."""
 
112
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
113
        yield k,v
 
114
 
 
115
 
 
116
def get_cmd_object(cmd_name, plugins_override=True):
 
117
    """Return the canonical name and command class for a command.
 
118
 
 
119
    plugins_override
 
120
        If true, plugin commands can override builtins.
 
121
    """
 
122
    from bzrlib.externalcommand import ExternalCommand
 
123
 
 
124
    cmd_name = str(cmd_name)            # not unicode
 
125
 
 
126
    # first look up this command under the specified name
 
127
    cmds = _get_cmd_dict(plugins_override=plugins_override)
 
128
    try:
 
129
        return cmds[cmd_name]()
 
130
    except KeyError:
 
131
        pass
 
132
 
 
133
    # look for any command which claims this as an alias
 
134
    for real_cmd_name, cmd_class in cmds.iteritems():
 
135
        if cmd_name in cmd_class.aliases:
 
136
            return cmd_class()
 
137
 
 
138
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
139
    if cmd_obj:
 
140
        return cmd_obj
 
141
 
 
142
    raise BzrCommandError("unknown command %r" % cmd_name)
 
143
 
 
144
 
 
145
class Command(object):
 
146
    """Base class for commands.
 
147
 
 
148
    Commands are the heart of the command-line bzr interface.
 
149
 
 
150
    The command object mostly handles the mapping of command-line
 
151
    parameters into one or more bzrlib operations, and of the results
 
152
    into textual output.
 
153
 
 
154
    Commands normally don't have any state.  All their arguments are
 
155
    passed in to the run method.  (Subclasses may take a different
 
156
    policy if the behaviour of the instance needs to depend on e.g. a
 
157
    shell plugin and not just its Python class.)
 
158
 
 
159
    The docstring for an actual command should give a single-line
 
160
    summary, then a complete description of the command.  A grammar
 
161
    description will be inserted.
 
162
 
 
163
    aliases
 
164
        Other accepted names for this command.
 
165
 
 
166
    takes_args
 
167
        List of argument forms, marked with whether they are optional,
 
168
        repeated, etc.
 
169
 
 
170
                Examples:
 
171
 
 
172
                ['to_location', 'from_branch?', 'file*']
 
173
 
 
174
                'to_location' is required
 
175
                'from_branch' is optional
 
176
                'file' can be specified 0 or more times
 
177
 
 
178
    takes_options
 
179
        List of options that may be given for this command.  These can
 
180
        be either strings, referring to globally-defined options,
 
181
        or option objects.  Retrieve through options().
 
182
 
 
183
    hidden
 
184
        If true, this command isn't advertised.  This is typically
 
185
        for commands intended for expert users.
 
186
    """
 
187
    aliases = []
 
188
    takes_args = []
 
189
    takes_options = []
 
190
 
 
191
    hidden = False
 
192
    
 
193
    def __init__(self):
 
194
        """Construct an instance of this command."""
 
195
        if self.__doc__ == Command.__doc__:
 
196
            warn("No help message set for %r" % self)
 
197
 
 
198
    def options(self):
 
199
        """Return dict of valid options for this command.
 
200
 
 
201
        Maps from long option name to option object."""
 
202
        r = dict()
 
203
        r['help'] = Option.OPTIONS['help']
 
204
        for o in self.takes_options:
 
205
            if not isinstance(o, Option):
 
206
                o = Option.OPTIONS[o]
 
207
            r[o.name] = o
 
208
        return r
 
209
 
 
210
    def run_argv(self, argv, alias_argv=None):
 
211
        """Parse command line and run."""
 
212
        args, opts = parse_args(self, argv, alias_argv)
 
213
        if 'help' in opts:  # e.g. bzr add --help
 
214
            from bzrlib.help import help_on_command
 
215
            help_on_command(self.name())
 
216
            return 0
 
217
        # XXX: This should be handled by the parser
 
218
        allowed_names = self.options().keys()
 
219
        for oname in opts:
 
220
            if oname not in allowed_names:
 
221
                raise BzrCommandError("option '--%s' is not allowed for"
 
222
                                      " command %r" % (oname, self.name()))
 
223
        # mix arguments and options into one dictionary
 
224
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
225
        cmdopts = {}
 
226
        for k, v in opts.items():
 
227
            cmdopts[k.replace('-', '_')] = v
 
228
 
 
229
        all_cmd_args = cmdargs.copy()
 
230
        all_cmd_args.update(cmdopts)
 
231
 
 
232
        return self.run(**all_cmd_args)
 
233
    
 
234
    def run(self):
 
235
        """Actually run the command.
 
236
 
 
237
        This is invoked with the options and arguments bound to
 
238
        keyword parameters.
 
239
 
 
240
        Return 0 or None if the command was successful, or a non-zero
 
241
        shell error code if not.  It's OK for this method to allow
 
242
        an exception to raise up.
 
243
        """
 
244
        raise NotImplementedError()
 
245
 
 
246
 
 
247
    def help(self):
 
248
        """Return help message for this class."""
 
249
        if self.__doc__ is Command.__doc__:
 
250
            return None
 
251
        return getdoc(self)
 
252
 
 
253
    def name(self):
 
254
        return _unsquish_command_name(self.__class__.__name__)
 
255
 
 
256
 
 
257
def parse_spec(spec):
 
258
    """
 
259
    >>> parse_spec(None)
 
260
    [None, None]
 
261
    >>> parse_spec("./")
 
262
    ['./', None]
 
263
    >>> parse_spec("../@")
 
264
    ['..', -1]
 
265
    >>> parse_spec("../f/@35")
 
266
    ['../f', 35]
 
267
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
268
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
269
    """
 
270
    if spec is None:
 
271
        return [None, None]
 
272
    if '/@' in spec:
 
273
        parsed = spec.split('/@')
 
274
        assert len(parsed) == 2
 
275
        if parsed[1] == "":
 
276
            parsed[1] = -1
 
277
        else:
 
278
            try:
 
279
                parsed[1] = int(parsed[1])
 
280
            except ValueError:
 
281
                pass # We can allow stuff like ./@revid:blahblahblah
 
282
            else:
 
283
                assert parsed[1] >=0
 
284
    else:
 
285
        parsed = [spec, None]
 
286
    return parsed
 
287
 
 
288
def parse_args(command, argv, alias_argv=None):
 
289
    """Parse command line.
 
290
    
 
291
    Arguments and options are parsed at this level before being passed
 
292
    down to specific command handlers.  This routine knows, from a
 
293
    lookup table, something about the available options, what optargs
 
294
    they take, and which commands will accept them.
 
295
    """
 
296
    # TODO: chop up this beast; make it a method of the Command
 
297
    args = []
 
298
    opts = {}
 
299
    alias_opts = {}
 
300
 
 
301
    cmd_options = command.options()
 
302
    argsover = False
 
303
    proc_aliasarg = True # Are we processing alias_argv now?
 
304
    for proc_argv in alias_argv, argv:
 
305
        while proc_argv:
 
306
            a = proc_argv.pop(0)
 
307
            if argsover:
 
308
                args.append(a)
 
309
                continue
 
310
            elif a == '--':
 
311
                # We've received a standalone -- No more flags
 
312
                argsover = True
 
313
                continue
 
314
            if a[0] == '-':
 
315
                # option names must not be unicode
 
316
                a = str(a)
 
317
                optarg = None
 
318
                if a[1] == '-':
 
319
                    mutter("  got option %r", a)
 
320
                    if '=' in a:
 
321
                        optname, optarg = a[2:].split('=', 1)
 
322
                    else:
 
323
                        optname = a[2:]
 
324
                    if optname not in cmd_options:
 
325
                        raise BzrOptionError('unknown long option %r for'
 
326
                                             ' command %s' % 
 
327
                                             (a, command.name()))
 
328
                else:
 
329
                    shortopt = a[1:]
 
330
                    if shortopt in Option.SHORT_OPTIONS:
 
331
                        # Multi-character options must have a space to delimit
 
332
                        # their value
 
333
                        # ^^^ what does this mean? mbp 20051014
 
334
                        optname = Option.SHORT_OPTIONS[shortopt].name
 
335
                    else:
 
336
                        # Single character short options, can be chained,
 
337
                        # and have their value appended to their name
 
338
                        shortopt = a[1:2]
 
339
                        if shortopt not in Option.SHORT_OPTIONS:
 
340
                            # We didn't find the multi-character name, and we
 
341
                            # didn't find the single char name
 
342
                            raise BzrError('unknown short option %r' % a)
 
343
                        optname = Option.SHORT_OPTIONS[shortopt].name
 
344
 
 
345
                        if a[2:]:
 
346
                            # There are extra things on this option
 
347
                            # see if it is the value, or if it is another
 
348
                            # short option
 
349
                            optargfn = Option.OPTIONS[optname].type
 
350
                            if optargfn is None:
 
351
                                # This option does not take an argument, so the
 
352
                                # next entry is another short option, pack it
 
353
                                # back into the list
 
354
                                proc_argv.insert(0, '-' + a[2:])
 
355
                            else:
 
356
                                # This option takes an argument, so pack it
 
357
                                # into the array
 
358
                                optarg = a[2:]
 
359
                
 
360
                    if optname not in cmd_options:
 
361
                        raise BzrOptionError('unknown short option %r for'
 
362
                                             ' command %s' % 
 
363
                                             (shortopt, command.name()))
 
364
                if optname in opts:
 
365
                    # XXX: Do we ever want to support this, e.g. for -r?
 
366
                    if proc_aliasarg:
 
367
                        raise BzrError('repeated option %r' % a)
 
368
                    elif optname in alias_opts:
 
369
                        # Replace what's in the alias with what's in the real
 
370
                        # argument
 
371
                        del alias_opts[optname]
 
372
                        del opts[optname]
 
373
                        proc_argv.insert(0, a)
 
374
                        continue
 
375
                    else:
 
376
                        raise BzrError('repeated option %r' % a)
 
377
                    
 
378
                option_obj = cmd_options[optname]
 
379
                optargfn = option_obj.type
 
380
                if optargfn:
 
381
                    if optarg == None:
 
382
                        if not proc_argv:
 
383
                            raise BzrError('option %r needs an argument' % a)
 
384
                        else:
 
385
                            optarg = proc_argv.pop(0)
 
386
                    opts[optname] = optargfn(optarg)
 
387
                    if proc_aliasarg:
 
388
                        alias_opts[optname] = optargfn(optarg)
 
389
                else:
 
390
                    if optarg != None:
 
391
                        raise BzrError('option %r takes no argument' % optname)
 
392
                    opts[optname] = True
 
393
                    if proc_aliasarg:
 
394
                        alias_opts[optname] = True
 
395
            else:
 
396
                args.append(a)
 
397
        proc_aliasarg = False # Done with alias argv
 
398
    return args, opts
 
399
 
 
400
 
 
401
def _match_argform(cmd, takes_args, args):
 
402
    argdict = {}
 
403
 
 
404
    # step through args and takes_args, allowing appropriate 0-many matches
 
405
    for ap in takes_args:
 
406
        argname = ap[:-1]
 
407
        if ap[-1] == '?':
 
408
            if args:
 
409
                argdict[argname] = args.pop(0)
 
410
        elif ap[-1] == '*': # all remaining arguments
 
411
            if args:
 
412
                argdict[argname + '_list'] = args[:]
 
413
                args = []
 
414
            else:
 
415
                argdict[argname + '_list'] = None
 
416
        elif ap[-1] == '+':
 
417
            if not args:
 
418
                raise BzrCommandError("command %r needs one or more %s"
 
419
                        % (cmd, argname.upper()))
 
420
            else:
 
421
                argdict[argname + '_list'] = args[:]
 
422
                args = []
 
423
        elif ap[-1] == '$': # all but one
 
424
            if len(args) < 2:
 
425
                raise BzrCommandError("command %r needs one or more %s"
 
426
                        % (cmd, argname.upper()))
 
427
            argdict[argname + '_list'] = args[:-1]
 
428
            args[:-1] = []                
 
429
        else:
 
430
            # just a plain arg
 
431
            argname = ap
 
432
            if not args:
 
433
                raise BzrCommandError("command %r requires argument %s"
 
434
                        % (cmd, argname.upper()))
 
435
            else:
 
436
                argdict[argname] = args.pop(0)
 
437
            
 
438
    if args:
 
439
        raise BzrCommandError("extra argument to command %s: %s"
 
440
                              % (cmd, args[0]))
 
441
 
 
442
    return argdict
 
443
 
 
444
 
 
445
 
 
446
def apply_profiled(the_callable, *args, **kwargs):
 
447
    import hotshot
 
448
    import tempfile
 
449
    import hotshot.stats
 
450
    pffileno, pfname = tempfile.mkstemp()
 
451
    try:
 
452
        prof = hotshot.Profile(pfname)
 
453
        try:
 
454
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
455
        finally:
 
456
            prof.close()
 
457
        stats = hotshot.stats.load(pfname)
 
458
        stats.strip_dirs()
 
459
        stats.sort_stats('cum')   # 'time'
 
460
        ## XXX: Might like to write to stderr or the trace file instead but
 
461
        ## print_stats seems hardcoded to stdout
 
462
        stats.print_stats(20)
 
463
        return ret
 
464
    finally:
 
465
        os.close(pffileno)
 
466
        os.remove(pfname)
 
467
 
 
468
 
 
469
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
470
    from bzrlib.lsprof import profile
 
471
    import cPickle
 
472
    ret, stats = profile(the_callable, *args, **kwargs)
 
473
    stats.sort()
 
474
    if filename is None:
 
475
        stats.pprint()
 
476
    else:
 
477
        stats.freeze()
 
478
        cPickle.dump(stats, open(filename, 'w'), 2)
 
479
        print 'Profile data written to %r.' % filename
 
480
    return ret
 
481
 
 
482
 
 
483
def get_alias(cmd):
 
484
    """Return an expanded alias, or None if no alias exists"""
 
485
    import bzrlib.config
 
486
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
487
    if (alias):
 
488
        return alias.split(' ')
 
489
    return None
 
490
 
 
491
 
 
492
def run_bzr(argv):
 
493
    """Execute a command.
 
494
 
 
495
    This is similar to main(), but without all the trappings for
 
496
    logging and error handling.  
 
497
    
 
498
    argv
 
499
       The command-line arguments, without the program name from argv[0]
 
500
    
 
501
    Returns a command status or raises an exception.
 
502
 
 
503
    Special master options: these must come before the command because
 
504
    they control how the command is interpreted.
 
505
 
 
506
    --no-plugins
 
507
        Do not load plugin modules at all
 
508
 
 
509
    --no-aliases
 
510
        Do not allow aliases
 
511
 
 
512
    --builtin
 
513
        Only use builtin commands.  (Plugins are still allowed to change
 
514
        other behaviour.)
 
515
 
 
516
    --profile
 
517
        Run under the Python hotshot profiler.
 
518
 
 
519
    --lsprof
 
520
        Run under the Python lsprof profiler.
 
521
    """
 
522
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
523
 
 
524
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
525
                opt_no_aliases = False
 
526
    opt_lsprof_file = None
 
527
 
 
528
    # --no-plugins is handled specially at a very early stage. We need
 
529
    # to load plugins before doing other command parsing so that they
 
530
    # can override commands, but this needs to happen first.
 
531
 
 
532
    argv_copy = []
 
533
    i = 0
 
534
    while i < len(argv):
 
535
        a = argv[i]
 
536
        if a == '--profile':
 
537
            opt_profile = True
 
538
        elif a == '--lsprof':
 
539
            opt_lsprof = True
 
540
        elif a == '--lsprof-file':
 
541
            opt_lsprof_file = argv[i + 1]
 
542
            i += 1
 
543
        elif a == '--no-plugins':
 
544
            opt_no_plugins = True
 
545
        elif a == '--no-aliases':
 
546
            opt_no_aliases = True
 
547
        elif a == '--builtin':
 
548
            opt_builtin = True
 
549
        elif a in ('--quiet', '-q'):
 
550
            be_quiet()
 
551
        else:
 
552
            argv_copy.append(a)
 
553
        i += 1
 
554
 
 
555
    argv = argv_copy
 
556
    if (not argv) or (argv[0] == '--help'):
 
557
        from bzrlib.help import help
 
558
        if len(argv) > 1:
 
559
            help(argv[1])
 
560
        else:
 
561
            help()
 
562
        return 0
 
563
 
 
564
    if argv[0] == '--version':
 
565
        from bzrlib.builtins import show_version
 
566
        show_version()
 
567
        return 0
 
568
        
 
569
    if not opt_no_plugins:
 
570
        from bzrlib.plugin import load_plugins
 
571
        load_plugins()
 
572
    else:
 
573
        from bzrlib.plugin import disable_plugins
 
574
        disable_plugins()
 
575
 
 
576
    alias_argv = None
 
577
 
 
578
    if not opt_no_aliases:
 
579
        alias_argv = get_alias(argv[0])
 
580
        if alias_argv:
 
581
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
582
            argv[0] = alias_argv.pop(0)
 
583
 
 
584
    cmd = str(argv.pop(0))
 
585
 
 
586
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
587
 
 
588
    try:
 
589
        if opt_lsprof:
 
590
            ret = apply_lsprofiled(opt_lsprof_file, cmd_obj.run_argv, argv, 
 
591
                                   alias_argv)
 
592
        elif opt_profile:
 
593
            ret = apply_profiled(cmd_obj.run_argv, argv, alias_argv)
 
594
        else:
 
595
            ret = cmd_obj.run_argv(argv, alias_argv)
 
596
        return ret or 0
 
597
    finally:
 
598
        # reset, in case we may do other commands later within the same process
 
599
        be_quiet(False)
 
600
 
 
601
def display_command(func):
 
602
    """Decorator that suppresses pipe/interrupt errors."""
 
603
    def ignore_pipe(*args, **kwargs):
 
604
        try:
 
605
            result = func(*args, **kwargs)
 
606
            sys.stdout.flush()
 
607
            return result
 
608
        except IOError, e:
 
609
            if not hasattr(e, 'errno'):
 
610
                raise
 
611
            if e.errno != errno.EPIPE:
 
612
                raise
 
613
            pass
 
614
        except KeyboardInterrupt:
 
615
            pass
 
616
    return ignore_pipe
 
617
 
 
618
 
 
619
def main(argv):
 
620
    import bzrlib.ui
 
621
    from bzrlib.ui.text import TextUIFactory
 
622
    ## bzrlib.trace.enable_default_logging()
 
623
    bzrlib.trace.log_startup(argv)
 
624
    bzrlib.ui.ui_factory = TextUIFactory()
 
625
    ret = run_bzr_catch_errors(argv[1:])
 
626
    mutter("return code %d", ret)
 
627
    return ret
 
628
 
 
629
 
 
630
def run_bzr_catch_errors(argv):
 
631
    try:
 
632
        try:
 
633
            return run_bzr(argv)
 
634
        finally:
 
635
            # do this here inside the exception wrappers to catch EPIPE
 
636
            sys.stdout.flush()
 
637
    except Exception, e:
 
638
        # used to handle AssertionError and KeyboardInterrupt
 
639
        # specially here, but hopefully they're handled ok by the logger now
 
640
        import errno
 
641
        if (isinstance(e, IOError) 
 
642
            and hasattr(e, 'errno')
 
643
            and e.errno == errno.EPIPE):
 
644
            bzrlib.trace.note('broken pipe')
 
645
            return 3
 
646
        else:
 
647
            bzrlib.trace.log_exception()
 
648
            if os.environ.get('BZR_PDB'):
 
649
                print '**** entering debugger'
 
650
                import pdb
 
651
                pdb.post_mortem(sys.exc_traceback)
 
652
            return 3
 
653
 
 
654
if __name__ == '__main__':
 
655
    sys.exit(main(sys.argv))