~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-08-17 13:41:14 UTC
  • mfrom: (1930.2.3 testament-unicode-54723)
  • Revision ID: pqm@pqm.ubuntu.com-20060817134114-6eadfca2a735ddae
(jam) fix bug #54723, allow Testament to handle unicode revision properties (avoid double handling encode/decode)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2006 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
 
 
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
26
 
 
27
 
 
28
 
 
29
 
import sys
 
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
30
33
import os
31
34
from warnings import warn
32
 
from inspect import getdoc
 
35
import sys
33
36
 
34
37
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.revisionspec import RevisionSpec
 
47
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
35
48
import bzrlib.trace
36
 
from bzrlib.trace import mutter, note, log_error, warning
37
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
38
 
from bzrlib.branch import find_branch
39
 
from bzrlib import BZRDIR
 
49
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
40
50
 
41
51
plugin_cmds = {}
42
52
 
43
53
 
44
 
def register_command(cmd):
45
 
    "Utility function to help register a command"
 
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
    """
46
62
    global plugin_cmds
47
63
    k = cmd.__name__
48
64
    if k.startswith("cmd_"):
51
67
        k_unsquished = k
52
68
    if not plugin_cmds.has_key(k_unsquished):
53
69
        plugin_cmds[k_unsquished] = cmd
54
 
        mutter('registered plugin command %s', k_unsquished)      
 
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
55
77
    else:
56
78
        log_error('Two plugins defined the same command: %r' % k)
57
79
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
66
88
    return cmd[4:].replace('_','-')
67
89
 
68
90
 
69
 
def _parse_revision_str(revstr):
70
 
    """This handles a revision string -> revno.
71
 
 
72
 
    This always returns a list.  The list will have one element for 
73
 
 
74
 
    It supports integers directly, but everything else it
75
 
    defers for passing to Branch.get_revision_info()
76
 
 
77
 
    >>> _parse_revision_str('234')
78
 
    [234]
79
 
    >>> _parse_revision_str('234..567')
80
 
    [234, 567]
81
 
    >>> _parse_revision_str('..')
82
 
    [None, None]
83
 
    >>> _parse_revision_str('..234')
84
 
    [None, 234]
85
 
    >>> _parse_revision_str('234..')
86
 
    [234, None]
87
 
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
88
 
    [234, 456, 789]
89
 
    >>> _parse_revision_str('234....789') # Error?
90
 
    [234, None, 789]
91
 
    >>> _parse_revision_str('revid:test@other.com-234234')
92
 
    ['revid:test@other.com-234234']
93
 
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
94
 
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
95
 
    >>> _parse_revision_str('revid:test@other.com-234234..23')
96
 
    ['revid:test@other.com-234234', 23]
97
 
    >>> _parse_revision_str('date:2005-04-12')
98
 
    ['date:2005-04-12']
99
 
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
100
 
    ['date:2005-04-12 12:24:33']
101
 
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
102
 
    ['date:2005-04-12T12:24:33']
103
 
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
104
 
    ['date:2005-04-12,12:24:33']
105
 
    >>> _parse_revision_str('-5..23')
106
 
    [-5, 23]
107
 
    >>> _parse_revision_str('-5')
108
 
    [-5]
109
 
    >>> _parse_revision_str('123a')
110
 
    ['123a']
111
 
    >>> _parse_revision_str('abc')
112
 
    ['abc']
113
 
    """
114
 
    import re
115
 
    old_format_re = re.compile('\d*:\d*')
116
 
    m = old_format_re.match(revstr)
117
 
    if m:
118
 
        warning('Colon separator for revision numbers is deprecated.'
119
 
                ' Use .. instead')
120
 
        revs = []
121
 
        for rev in revstr.split(':'):
122
 
            if rev:
123
 
                revs.append(int(rev))
124
 
            else:
125
 
                revs.append(None)
126
 
        return revs
127
 
    revs = []
128
 
    for x in revstr.split('..'):
129
 
        if not x:
130
 
            revs.append(None)
131
 
        else:
132
 
            try:
133
 
                revs.append(int(x))
134
 
            except ValueError:
135
 
                revs.append(x)
136
 
    return revs
137
 
 
138
 
 
139
 
def get_merge_type(typestring):
140
 
    """Attempt to find the merge class/factory associated with a string."""
141
 
    from merge import merge_types
142
 
    try:
143
 
        return merge_types[typestring][0]
144
 
    except KeyError:
145
 
        templ = '%s%%7s: %%s' % (' '*12)
146
 
        lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
147
 
        type_list = '\n'.join(lines)
148
 
        msg = "No known merge type %s. Supported types are:\n%s" %\
149
 
            (typestring, type_list)
150
 
        raise BzrCommandError(msg)
151
 
    
152
 
 
153
91
def _builtin_commands():
154
92
    import bzrlib.builtins
155
93
    r = {}
156
94
    builtins = bzrlib.builtins.__dict__
157
95
    for name in builtins:
158
96
        if name.startswith("cmd_"):
159
 
            real_name = _unsquish_command_name(name)        
 
97
            real_name = _unsquish_command_name(name)
160
98
            r[real_name] = builtins[name]
161
99
    return r
162
 
 
163
100
            
164
101
 
165
102
def builtin_command_names():
211
148
    if cmd_obj:
212
149
        return cmd_obj
213
150
 
214
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
151
    raise BzrCommandError('unknown command "%s"' % cmd_name)
215
152
 
216
153
 
217
154
class Command(object):
239
176
        List of argument forms, marked with whether they are optional,
240
177
        repeated, etc.
241
178
 
 
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
 
242
187
    takes_options
243
 
        List of options that may be given for this command.
 
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().
244
191
 
245
192
    hidden
246
193
        If true, this command isn't advertised.  This is typically
247
194
        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
 
248
205
    """
249
206
    aliases = []
250
 
    
251
207
    takes_args = []
252
208
    takes_options = []
 
209
    encoding_type = 'strict'
253
210
 
254
211
    hidden = False
255
212
    
258
215
        if self.__doc__ == Command.__doc__:
259
216
            warn("No help message set for %r" % self)
260
217
 
261
 
 
 
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)
262
251
    def run_argv(self, argv):
263
 
        """Parse command line and run."""
264
 
        args, opts = parse_args(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)
265
257
 
 
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)
266
265
        if 'help' in opts:  # e.g. bzr add --help
267
266
            from bzrlib.help import help_on_command
268
267
            help_on_command(self.name())
269
268
            return 0
270
 
 
271
 
        # check options are reasonable
272
 
        allowed = self.takes_options
273
 
        for oname in opts:
274
 
            if oname not in allowed:
275
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
276
 
                                      % (oname, self.name()))
277
 
 
278
269
        # mix arguments and options into one dictionary
279
270
        cmdargs = _match_argform(self.name(), self.takes_args, args)
280
271
        cmdopts = {}
284
275
        all_cmd_args = cmdargs.copy()
285
276
        all_cmd_args.update(cmdopts)
286
277
 
 
278
        self._setup_outf()
 
279
 
287
280
        return self.run(**all_cmd_args)
288
 
 
289
281
    
290
282
    def run(self):
291
283
        """Actually run the command.
297
289
        shell error code if not.  It's OK for this method to allow
298
290
        an exception to raise up.
299
291
        """
300
 
        raise NotImplementedError()
301
 
 
 
292
        raise NotImplementedError('no implementation of command %r' 
 
293
                                  % self.name())
302
294
 
303
295
    def help(self):
304
296
        """Return help message for this class."""
 
297
        from inspect import getdoc
305
298
        if self.__doc__ is Command.__doc__:
306
299
            return None
307
300
        return getdoc(self)
309
302
    def name(self):
310
303
        return _unsquish_command_name(self.__class__.__name__)
311
304
 
 
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
 
312
316
 
313
317
def parse_spec(spec):
314
318
    """
341
345
        parsed = [spec, None]
342
346
    return parsed
343
347
 
344
 
 
345
 
 
346
 
 
347
 
# list of all available options; the rhs can be either None for an
348
 
# option that takes no argument, or a constructor function that checks
349
 
# the type.
350
 
OPTIONS = {
351
 
    'all':                    None,
352
 
    'diff-options':           str,
353
 
    'help':                   None,
354
 
    'file':                   unicode,
355
 
    'force':                  None,
356
 
    'format':                 unicode,
357
 
    'forward':                None,
358
 
    'message':                unicode,
359
 
    'no-recurse':             None,
360
 
    'profile':                None,
361
 
    'revision':               _parse_revision_str,
362
 
    'short':                  None,
363
 
    'show-ids':               None,
364
 
    'timezone':               str,
365
 
    'verbose':                None,
366
 
    'version':                None,
367
 
    'email':                  None,
368
 
    'unchanged':              None,
369
 
    'update':                 None,
370
 
    'long':                   None,
371
 
    'root':                   str,
372
 
    'no-backup':              None,
373
 
    'merge-type':             get_merge_type,
374
 
    'pattern':                str,
375
 
    }
376
 
 
377
 
SHORT_OPTIONS = {
378
 
    'F':                      'file', 
379
 
    'h':                      'help',
380
 
    'm':                      'message',
381
 
    'r':                      'revision',
382
 
    'v':                      'verbose',
383
 
    'l':                      'long',
384
 
}
385
 
 
386
 
 
387
 
def parse_args(argv):
 
348
def parse_args(command, argv, alias_argv=None):
388
349
    """Parse command line.
389
350
    
390
351
    Arguments and options are parsed at this level before being passed
391
352
    down to specific command handlers.  This routine knows, from a
392
353
    lookup table, something about the available options, what optargs
393
354
    they take, and which commands will accept them.
394
 
 
395
 
    >>> parse_args('--help'.split())
396
 
    ([], {'help': True})
397
 
    >>> parse_args('help -- --invalidcmd'.split())
398
 
    (['help', '--invalidcmd'], {})
399
 
    >>> parse_args('--version'.split())
400
 
    ([], {'version': True})
401
 
    >>> parse_args('status --all'.split())
402
 
    (['status'], {'all': True})
403
 
    >>> parse_args('commit --message=biter'.split())
404
 
    (['commit'], {'message': u'biter'})
405
 
    >>> parse_args('log -r 500'.split())
406
 
    (['log'], {'revision': [500]})
407
 
    >>> parse_args('log -r500..600'.split())
408
 
    (['log'], {'revision': [500, 600]})
409
 
    >>> parse_args('log -vr500..600'.split())
410
 
    (['log'], {'verbose': True, 'revision': [500, 600]})
411
 
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
412
 
    (['log'], {'revision': ['v500', 600]})
413
355
    """
414
 
    args = []
415
 
    opts = {}
416
 
 
417
 
    argsover = False
418
 
    while argv:
419
 
        a = argv.pop(0)
420
 
        if not argsover and a[0] == '-':
421
 
            # option names must not be unicode
422
 
            a = str(a)
423
 
            optarg = None
424
 
            if a[1] == '-':
425
 
                if a == '--':
426
 
                    # We've received a standalone -- No more flags
427
 
                    argsover = True
428
 
                    continue
429
 
                mutter("  got option %r" % a)
430
 
                if '=' in a:
431
 
                    optname, optarg = a[2:].split('=', 1)
432
 
                else:
433
 
                    optname = a[2:]
434
 
                if optname not in OPTIONS:
435
 
                    raise BzrError('unknown long option %r' % a)
436
 
            else:
437
 
                shortopt = a[1:]
438
 
                if shortopt in SHORT_OPTIONS:
439
 
                    # Multi-character options must have a space to delimit
440
 
                    # their value
441
 
                    optname = SHORT_OPTIONS[shortopt]
442
 
                else:
443
 
                    # Single character short options, can be chained,
444
 
                    # and have their value appended to their name
445
 
                    shortopt = a[1:2]
446
 
                    if shortopt not in SHORT_OPTIONS:
447
 
                        # We didn't find the multi-character name, and we
448
 
                        # didn't find the single char name
449
 
                        raise BzrError('unknown short option %r' % a)
450
 
                    optname = SHORT_OPTIONS[shortopt]
451
 
 
452
 
                    if a[2:]:
453
 
                        # There are extra things on this option
454
 
                        # see if it is the value, or if it is another
455
 
                        # short option
456
 
                        optargfn = OPTIONS[optname]
457
 
                        if optargfn is None:
458
 
                            # This option does not take an argument, so the
459
 
                            # next entry is another short option, pack it back
460
 
                            # into the list
461
 
                            argv.insert(0, '-' + a[2:])
462
 
                        else:
463
 
                            # This option takes an argument, so pack it
464
 
                            # into the array
465
 
                            optarg = a[2:]
466
 
            
467
 
            if optname in opts:
468
 
                # XXX: Do we ever want to support this, e.g. for -r?
469
 
                raise BzrError('repeated option %r' % a)
470
 
                
471
 
            optargfn = OPTIONS[optname]
472
 
            if optargfn:
473
 
                if optarg == None:
474
 
                    if not argv:
475
 
                        raise BzrError('option %r needs an argument' % a)
476
 
                    else:
477
 
                        optarg = argv.pop(0)
478
 
                opts[optname] = optargfn(optarg)
479
 
            else:
480
 
                if optarg != None:
481
 
                    raise BzrError('option %r takes no argument' % optname)
482
 
                opts[optname] = True
483
 
        else:
484
 
            args.append(a)
485
 
 
 
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])
486
366
    return args, opts
487
367
 
488
368
 
489
 
 
490
 
 
491
369
def _match_argform(cmd, takes_args, args):
492
370
    argdict = {}
493
371
 
515
393
                raise BzrCommandError("command %r needs one or more %s"
516
394
                        % (cmd, argname.upper()))
517
395
            argdict[argname + '_list'] = args[:-1]
518
 
            args[:-1] = []                
 
396
            args[:-1] = []
519
397
        else:
520
398
            # just a plain arg
521
399
            argname = ap
536
414
def apply_profiled(the_callable, *args, **kwargs):
537
415
    import hotshot
538
416
    import tempfile
 
417
    import hotshot.stats
539
418
    pffileno, pfname = tempfile.mkstemp()
540
419
    try:
541
420
        prof = hotshot.Profile(pfname)
543
422
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
544
423
        finally:
545
424
            prof.close()
546
 
 
547
 
        import hotshot.stats
548
425
        stats = hotshot.stats.load(pfname)
549
 
        #stats.strip_dirs()
550
 
        stats.sort_stats('time')
 
426
        stats.strip_dirs()
 
427
        stats.sort_stats('cum')   # 'time'
551
428
        ## XXX: Might like to write to stderr or the trace file instead but
552
429
        ## print_stats seems hardcoded to stdout
553
430
        stats.print_stats(20)
554
 
 
555
431
        return ret
556
432
    finally:
557
433
        os.close(pffileno)
558
434
        os.remove(pfname)
559
435
 
560
436
 
 
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
 
561
460
def run_bzr(argv):
562
461
    """Execute a command.
563
462
 
566
465
    
567
466
    argv
568
467
       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).
569
470
    
570
471
    Returns a command status or raises an exception.
571
472
 
575
476
    --no-plugins
576
477
        Do not load plugin modules at all
577
478
 
 
479
    --no-aliases
 
480
        Do not allow aliases
 
481
 
578
482
    --builtin
579
483
        Only use builtin commands.  (Plugins are still allowed to change
580
484
        other behaviour.)
581
485
 
582
486
    --profile
583
 
        Run under the Python profiler.
 
487
        Run under the Python hotshot profiler.
 
488
 
 
489
    --lsprof
 
490
        Run under the Python lsprof profiler.
584
491
    """
585
 
    
586
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
492
    argv = list(argv)
587
493
 
588
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
494
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
495
                opt_no_aliases = False
 
496
    opt_lsprof_file = None
589
497
 
590
498
    # --no-plugins is handled specially at a very early stage. We need
591
499
    # to load plugins before doing other command parsing so that they
592
500
    # can override commands, but this needs to happen first.
593
501
 
594
 
    for a in argv:
 
502
    argv_copy = []
 
503
    i = 0
 
504
    while i < len(argv):
 
505
        a = argv[i]
595
506
        if a == '--profile':
596
507
            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
597
514
        elif a == '--no-plugins':
598
515
            opt_no_plugins = True
 
516
        elif a == '--no-aliases':
 
517
            opt_no_aliases = True
599
518
        elif a == '--builtin':
600
519
            opt_builtin = True
 
520
        elif a in ('--quiet', '-q'):
 
521
            be_quiet()
601
522
        else:
602
 
            break
603
 
        argv.remove(a)
 
523
            argv_copy.append(a)
 
524
        i += 1
604
525
 
605
 
    if (not argv) or (argv[0] == '--help'):
606
 
        from bzrlib.help import help
607
 
        if len(argv) > 1:
608
 
            help(argv[1])
609
 
        else:
610
 
            help()
 
526
    argv = argv_copy
 
527
    if (not argv):
 
528
        from bzrlib.builtins import cmd_help
 
529
        cmd_help().run_argv_aliases([])
611
530
        return 0
612
531
 
613
532
    if argv[0] == '--version':
614
 
        from bzrlib.builtins import show_version
 
533
        from bzrlib.version import show_version
615
534
        show_version()
616
535
        return 0
617
536
        
618
537
    if not opt_no_plugins:
619
538
        from bzrlib.plugin import load_plugins
620
539
        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)
621
551
 
622
552
    cmd = str(argv.pop(0))
623
553
 
624
554
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
625
 
 
626
 
    if opt_profile:
627
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
 
555
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
556
        run = cmd_obj.run_argv
 
557
        run_argv = [argv]
628
558
    else:
629
 
        ret = cmd_obj.run_argv(argv)
630
 
    return ret or 0
 
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
631
592
 
632
593
 
633
594
def main(argv):
634
595
    import bzrlib.ui
635
 
    bzrlib.trace.log_startup(argv)
636
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
637
 
 
 
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
 
602
 
 
603
 
 
604
def run_bzr_catch_errors(argv):
638
605
    try:
639
 
        try:
640
 
            return run_bzr(argv[1:])
641
 
        finally:
642
 
            # do this here inside the exception wrappers to catch EPIPE
643
 
            sys.stdout.flush()
644
 
    except BzrCommandError, e:
645
 
        # command line syntax error, etc
646
 
        log_error(str(e))
647
 
        return 1
648
 
    except BzrError, e:
649
 
        bzrlib.trace.log_exception()
650
 
        return 1
651
 
    except AssertionError, e:
652
 
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
 
606
        return run_bzr(argv)
 
607
        # do this here inside the exception wrappers to catch EPIPE
 
608
        sys.stdout.flush()
 
609
    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)
653
617
        return 3
654
 
    except KeyboardInterrupt, e:
655
 
        bzrlib.trace.note('interrupted')
656
 
        return 2
657
 
    except Exception, e:
658
 
        import errno
659
 
        if (isinstance(e, IOError) 
660
 
            and hasattr(e, 'errno')
661
 
            and e.errno == errno.EPIPE):
662
 
            bzrlib.trace.note('broken pipe')
663
 
            return 2
664
 
        else:
665
 
            bzrlib.trace.log_exception()
666
 
            return 2
667
 
 
668
618
 
669
619
if __name__ == '__main__':
670
620
    sys.exit(main(sys.argv))