~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-10-06 22:15:52 UTC
  • mfrom: (1185.13.2)
  • mto: This revision was merged to the branch mainline in revision 1420.
  • Revision ID: robertc@robertcollins.net-20051006221552-9b15c96fa504e0ad
mergeĀ fromĀ upstream

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