/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: Robert Collins
  • Date: 2005-10-02 21:51:29 UTC
  • mfrom: (1396)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20051002215128-5686c7d24bf9bdb9
merge from martins newformat branch - brings in transport abstraction

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