/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: Lalo Martins
  • Date: 2005-09-14 06:11:53 UTC
  • mto: (1185.1.22)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: lalo@exoweb.net-20050914061153-509cea89f773f329
fixing a few tests that came on the merge, for the new constructors

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