~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-10-02 22:47:02 UTC
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20051002224701-8a8b20b90de559a6
support ghosts in commits

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
2
 
#
 
1
# Copyright (C) 2004, 2005 by 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: Define arguments by objects, rather than just using names.
22
 
# Those objects can specify the expected type of the argument, which
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.
27
 
 
28
 
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
29
 
# the profile output behind so it can be interactively examined?
30
 
 
31
 
import codecs
32
 
import errno
 
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
33
44
import os
34
45
from warnings import warn
35
 
import sys
 
46
from inspect import getdoc
36
47
 
37
48
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.errors import (BzrError,
40
 
                           BzrCommandError,
41
 
                           BzrCheckError,
42
 
                           NotBranchError)
43
 
from bzrlib import option
44
 
from bzrlib.option import Option
45
 
import bzrlib.osutils
 
49
import bzrlib.trace
 
50
from bzrlib.trace import mutter, note, log_error, warning
 
51
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
46
52
from bzrlib.revisionspec import RevisionSpec
47
 
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
48
 
import bzrlib.trace
49
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
53
from bzrlib import BZRDIR
50
54
 
51
55
plugin_cmds = {}
52
56
 
53
57
 
54
 
def register_command(cmd, decorate=False):
55
 
    """Utility function to help register a command
56
 
 
57
 
    :param cmd: Command subclass to register
58
 
    :param decorate: If true, allow overriding an existing command
59
 
        of the same name; the old command is returned by this function.
60
 
        Otherwise it is an error to try to override an existing command.
61
 
    """
 
58
def register_command(cmd):
 
59
    "Utility function to help register a command"
62
60
    global plugin_cmds
63
61
    k = cmd.__name__
64
62
    if k.startswith("cmd_"):
67
65
        k_unsquished = k
68
66
    if not plugin_cmds.has_key(k_unsquished):
69
67
        plugin_cmds[k_unsquished] = cmd
70
 
        mutter('registered plugin command %s', k_unsquished)
71
 
        if decorate and k_unsquished in builtin_command_names():
72
 
            return _builtin_commands()[k_unsquished]
73
 
    elif decorate:
74
 
        result = plugin_cmds[k_unsquished]
75
 
        plugin_cmds[k_unsquished] = cmd
76
 
        return result
 
68
        mutter('registered plugin command %s', k_unsquished)      
77
69
    else:
78
70
        log_error('Two plugins defined the same command: %r' % k)
79
71
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
88
80
    return cmd[4:].replace('_','-')
89
81
 
90
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
 
91
151
def _builtin_commands():
92
152
    import bzrlib.builtins
93
153
    r = {}
94
154
    builtins = bzrlib.builtins.__dict__
95
155
    for name in builtins:
96
156
        if name.startswith("cmd_"):
97
 
            real_name = _unsquish_command_name(name)
 
157
            real_name = _unsquish_command_name(name)        
98
158
            r[real_name] = builtins[name]
99
159
    return r
 
160
 
100
161
            
101
162
 
102
163
def builtin_command_names():
148
209
    if cmd_obj:
149
210
        return cmd_obj
150
211
 
151
 
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
212
    raise BzrCommandError("unknown command %r" % cmd_name)
152
213
 
153
214
 
154
215
class Command(object):
176
237
        List of argument forms, marked with whether they are optional,
177
238
        repeated, etc.
178
239
 
179
 
                Examples:
180
 
 
181
 
                ['to_location', 'from_branch?', 'file*']
182
 
 
183
 
                'to_location' is required
184
 
                'from_branch' is optional
185
 
                'file' can be specified 0 or more times
186
 
 
187
240
    takes_options
188
 
        List of options that may be given for this command.  These can
189
 
        be either strings, referring to globally-defined options,
190
 
        or option objects.  Retrieve through options().
 
241
        List of options that may be given for this command.
191
242
 
192
243
    hidden
193
244
        If true, this command isn't advertised.  This is typically
194
245
        for commands intended for expert users.
195
 
 
196
 
    encoding_type
197
 
        Command objects will get a 'outf' attribute, which has been
198
 
        setup to properly handle encoding of unicode strings.
199
 
        encoding_type determines what will happen when characters cannot
200
 
        be encoded
201
 
            strict - abort if we cannot decode
202
 
            replace - put in a bogus character (typically '?')
203
 
            exact - do not encode sys.stdout
204
 
 
205
246
    """
206
247
    aliases = []
 
248
    
207
249
    takes_args = []
208
250
    takes_options = []
209
 
    encoding_type = 'strict'
210
251
 
211
252
    hidden = False
212
253
    
215
256
        if self.__doc__ == Command.__doc__:
216
257
            warn("No help message set for %r" % self)
217
258
 
218
 
    def options(self):
219
 
        """Return dict of valid options for this command.
220
 
 
221
 
        Maps from long option name to option object."""
222
 
        r = dict()
223
 
        r['help'] = Option.OPTIONS['help']
224
 
        for o in self.takes_options:
225
 
            if isinstance(o, basestring):
226
 
                o = Option.OPTIONS[o]
227
 
            r[o.name] = o
228
 
        return r
229
 
 
230
 
    def _setup_outf(self):
231
 
        """Return a file linked to stdout, which has proper encoding."""
232
 
        assert self.encoding_type in ['strict', 'exact', 'replace']
233
 
 
234
 
        # Originally I was using self.stdout, but that looks
235
 
        # *way* too much like sys.stdout
236
 
        if self.encoding_type == 'exact':
237
 
            self.outf = sys.stdout
238
 
            return
239
 
 
240
 
        output_encoding = bzrlib.osutils.get_terminal_encoding()
241
 
 
242
 
        # use 'replace' so that we don't abort if trying to write out
243
 
        # in e.g. the default C locale.
244
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
245
 
        # For whatever reason codecs.getwriter() does not advertise its encoding
246
 
        # it just returns the encoding of the wrapped file, which is completely
247
 
        # bogus. So set the attribute, so we can find the correct encoding later.
248
 
        self.outf.encoding = output_encoding
249
 
 
250
 
    @deprecated_method(zero_eight)
 
259
 
251
260
    def run_argv(self, argv):
252
 
        """Parse command line and run.
253
 
        
254
 
        See run_argv_aliases for the 0.8 and beyond api.
255
 
        """
256
 
        return self.run_argv_aliases(argv)
 
261
        """Parse command line and run."""
 
262
        args, opts = parse_args(argv)
257
263
 
258
 
    def run_argv_aliases(self, argv, alias_argv=None):
259
 
        """Parse the command line and run with extra aliases in alias_argv."""
260
 
        if argv is None:
261
 
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
262
 
                 DeprecationWarning, stacklevel=2)
263
 
            argv = []
264
 
        args, opts = parse_args(self, argv, alias_argv)
265
264
        if 'help' in opts:  # e.g. bzr add --help
266
265
            from bzrlib.help import help_on_command
267
266
            help_on_command(self.name())
268
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
 
269
276
        # mix arguments and options into one dictionary
270
277
        cmdargs = _match_argform(self.name(), self.takes_args, args)
271
278
        cmdopts = {}
275
282
        all_cmd_args = cmdargs.copy()
276
283
        all_cmd_args.update(cmdopts)
277
284
 
278
 
        self._setup_outf()
279
 
 
280
285
        return self.run(**all_cmd_args)
 
286
 
281
287
    
282
288
    def run(self):
283
289
        """Actually run the command.
289
295
        shell error code if not.  It's OK for this method to allow
290
296
        an exception to raise up.
291
297
        """
292
 
        raise NotImplementedError('no implementation of command %r' 
293
 
                                  % self.name())
 
298
        raise NotImplementedError()
 
299
 
294
300
 
295
301
    def help(self):
296
302
        """Return help message for this class."""
297
 
        from inspect import getdoc
298
303
        if self.__doc__ is Command.__doc__:
299
304
            return None
300
305
        return getdoc(self)
302
307
    def name(self):
303
308
        return _unsquish_command_name(self.__class__.__name__)
304
309
 
305
 
    def plugin_name(self):
306
 
        """Get the name of the plugin that provides this command.
307
 
 
308
 
        :return: The name of the plugin or None if the command is builtin.
309
 
        """
310
 
        mod_parts = self.__module__.split('.')
311
 
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
312
 
            return mod_parts[2]
313
 
        else:
314
 
            return None
315
 
 
316
310
 
317
311
def parse_spec(spec):
318
312
    """
345
339
        parsed = [spec, None]
346
340
    return parsed
347
341
 
348
 
def parse_args(command, argv, alias_argv=None):
 
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):
349
384
    """Parse command line.
350
385
    
351
386
    Arguments and options are parsed at this level before being passed
352
387
    down to specific command handlers.  This routine knows, from a
353
388
    lookup table, something about the available options, what optargs
354
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>]})
355
409
    """
356
 
    # TODO: make it a method of the Command?
357
 
    parser = option.get_optparser(command.options())
358
 
    if alias_argv is not None:
359
 
        args = alias_argv + argv
360
 
    else:
361
 
        args = argv
362
 
 
363
 
    options, args = parser.parse_args(args)
364
 
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if 
365
 
                 v is not option.OptionParser.DEFAULT_VALUE])
 
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
 
366
482
    return args, opts
367
483
 
368
484
 
 
485
 
 
486
 
369
487
def _match_argform(cmd, takes_args, args):
370
488
    argdict = {}
371
489
 
393
511
                raise BzrCommandError("command %r needs one or more %s"
394
512
                        % (cmd, argname.upper()))
395
513
            argdict[argname + '_list'] = args[:-1]
396
 
            args[:-1] = []
 
514
            args[:-1] = []                
397
515
        else:
398
516
            # just a plain arg
399
517
            argname = ap
414
532
def apply_profiled(the_callable, *args, **kwargs):
415
533
    import hotshot
416
534
    import tempfile
417
 
    import hotshot.stats
418
535
    pffileno, pfname = tempfile.mkstemp()
419
536
    try:
420
537
        prof = hotshot.Profile(pfname)
422
539
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
423
540
        finally:
424
541
            prof.close()
 
542
 
 
543
        import hotshot.stats
425
544
        stats = hotshot.stats.load(pfname)
426
 
        stats.strip_dirs()
427
 
        stats.sort_stats('cum')   # 'time'
 
545
        #stats.strip_dirs()
 
546
        stats.sort_stats('time')
428
547
        ## XXX: Might like to write to stderr or the trace file instead but
429
548
        ## print_stats seems hardcoded to stdout
430
549
        stats.print_stats(20)
 
550
 
431
551
        return ret
432
552
    finally:
433
553
        os.close(pffileno)
434
554
        os.remove(pfname)
435
555
 
436
556
 
437
 
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
438
 
    from bzrlib.lsprof import profile
439
 
    import cPickle
440
 
    ret, stats = profile(the_callable, *args, **kwargs)
441
 
    stats.sort()
442
 
    if filename is None:
443
 
        stats.pprint()
444
 
    else:
445
 
        stats.freeze()
446
 
        cPickle.dump(stats, open(filename, 'w'), 2)
447
 
        print 'Profile data written to %r.' % filename
448
 
    return ret
449
 
 
450
 
 
451
 
def get_alias(cmd):
452
 
    """Return an expanded alias, or None if no alias exists"""
453
 
    import bzrlib.config
454
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
455
 
    if (alias):
456
 
        return alias.split(' ')
457
 
    return None
458
 
 
459
 
 
460
557
def run_bzr(argv):
461
558
    """Execute a command.
462
559
 
465
562
    
466
563
    argv
467
564
       The command-line arguments, without the program name from argv[0]
468
 
       These should already be decoded. All library/test code calling
469
 
       run_bzr should be passing valid strings (don't need decoding).
470
565
    
471
566
    Returns a command status or raises an exception.
472
567
 
476
571
    --no-plugins
477
572
        Do not load plugin modules at all
478
573
 
479
 
    --no-aliases
480
 
        Do not allow aliases
481
 
 
482
574
    --builtin
483
575
        Only use builtin commands.  (Plugins are still allowed to change
484
576
        other behaviour.)
485
577
 
486
578
    --profile
487
 
        Run under the Python hotshot profiler.
488
 
 
489
 
    --lsprof
490
 
        Run under the Python lsprof profiler.
 
579
        Run under the Python profiler.
491
580
    """
492
 
    argv = list(argv)
 
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]
493
585
 
494
 
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
495
 
                opt_no_aliases = False
496
 
    opt_lsprof_file = None
 
586
    opt_profile = opt_no_plugins = opt_builtin = False
497
587
 
498
588
    # --no-plugins is handled specially at a very early stage. We need
499
589
    # to load plugins before doing other command parsing so that they
500
590
    # can override commands, but this needs to happen first.
501
591
 
502
 
    argv_copy = []
503
 
    i = 0
504
 
    while i < len(argv):
505
 
        a = argv[i]
 
592
    for a in argv:
506
593
        if a == '--profile':
507
594
            opt_profile = True
508
 
        elif a == '--lsprof':
509
 
            opt_lsprof = True
510
 
        elif a == '--lsprof-file':
511
 
            opt_lsprof = True
512
 
            opt_lsprof_file = argv[i + 1]
513
 
            i += 1
514
595
        elif a == '--no-plugins':
515
596
            opt_no_plugins = True
516
 
        elif a == '--no-aliases':
517
 
            opt_no_aliases = True
518
597
        elif a == '--builtin':
519
598
            opt_builtin = True
520
 
        elif a in ('--quiet', '-q'):
521
 
            be_quiet()
522
599
        else:
523
 
            argv_copy.append(a)
524
 
        i += 1
 
600
            break
 
601
        argv.remove(a)
525
602
 
526
 
    argv = argv_copy
527
 
    if (not argv):
528
 
        from bzrlib.builtins import cmd_help
529
 
        cmd_help().run_argv_aliases([])
 
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()
530
609
        return 0
531
610
 
532
611
    if argv[0] == '--version':
533
 
        from bzrlib.version import show_version
 
612
        from bzrlib.builtins import show_version
534
613
        show_version()
535
614
        return 0
536
615
        
537
616
    if not opt_no_plugins:
538
617
        from bzrlib.plugin import load_plugins
539
618
        load_plugins()
540
 
    else:
541
 
        from bzrlib.plugin import disable_plugins
542
 
        disable_plugins()
543
 
 
544
 
    alias_argv = None
545
 
 
546
 
    if not opt_no_aliases:
547
 
        alias_argv = get_alias(argv[0])
548
 
        if alias_argv:
549
 
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
550
 
            argv[0] = alias_argv.pop(0)
551
619
 
552
620
    cmd = str(argv.pop(0))
553
621
 
554
622
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
555
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
556
 
        run = cmd_obj.run_argv
557
 
        run_argv = [argv]
 
623
 
 
624
    if opt_profile:
 
625
        ret = apply_profiled(cmd_obj.run_argv, argv)
558
626
    else:
559
 
        run = cmd_obj.run_argv_aliases
560
 
        run_argv = [argv, alias_argv]
561
 
 
562
 
    try:
563
 
        if opt_lsprof:
564
 
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
565
 
        elif opt_profile:
566
 
            ret = apply_profiled(run, *run_argv)
567
 
        else:
568
 
            ret = run(*run_argv)
569
 
        return ret or 0
570
 
    finally:
571
 
        # reset, in case we may do other commands later within the same process
572
 
        be_quiet(False)
573
 
 
574
 
def display_command(func):
575
 
    """Decorator that suppresses pipe/interrupt errors."""
576
 
    def ignore_pipe(*args, **kwargs):
577
 
        try:
578
 
            result = func(*args, **kwargs)
579
 
            sys.stdout.flush()
580
 
            return result
581
 
        except IOError, e:
582
 
            if not hasattr(e, 'errno'):
583
 
                raise
584
 
            if e.errno != errno.EPIPE:
585
 
                # Win32 raises IOError with errno=0 on a broken pipe
586
 
                if sys.platform != 'win32' or e.errno != 0:
587
 
                    raise
588
 
            pass
589
 
        except KeyboardInterrupt:
590
 
            pass
591
 
    return ignore_pipe
 
627
        ret = cmd_obj.run_argv(argv)
 
628
    return ret or 0
592
629
 
593
630
 
594
631
def main(argv):
595
632
    import bzrlib.ui
596
 
    from bzrlib.ui.text import TextUIFactory
597
 
    bzrlib.ui.ui_factory = TextUIFactory()
598
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
599
 
    ret = run_bzr_catch_errors(argv)
600
 
    mutter("return code %d", ret)
601
 
    return ret
 
633
    bzrlib.trace.log_startup(argv)
 
634
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
635
 
 
636
    return run_bzr_catch_errors(argv[1:])
602
637
 
603
638
 
604
639
def run_bzr_catch_errors(argv):
605
640
    try:
606
 
        return run_bzr(argv)
607
 
        # do this here inside the exception wrappers to catch EPIPE
608
 
        sys.stdout.flush()
 
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
609
663
    except Exception, e:
610
 
        # used to handle AssertionError and KeyboardInterrupt
611
 
        # specially here, but hopefully they're handled ok by the logger now
612
 
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
613
 
        if os.environ.get('BZR_PDB'):
614
 
            print '**** entering debugger'
615
 
            import pdb
616
 
            pdb.post_mortem(sys.exc_traceback)
617
 
        return 3
 
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
618
673
 
619
674
if __name__ == '__main__':
620
675
    sys.exit(main(sys.argv))