/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2006-06-11 21:26:20 UTC
  • mto: This revision was merged to the branch mainline in revision 1764.
  • Revision ID: aaron.bentley@utoronto.ca-20060611212620-2517ff1406176fd9
Rename run_bzr_external -> run_bzr_subprocess, add docstring

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 by Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
# TODO: probably should say which arguments are candidates for glob
 
19
# expansion on windows and do that at the command level.
 
20
 
 
21
# TODO: 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 sys
 
32
import os
 
33
from warnings import warn
 
34
import errno
 
35
import codecs
 
36
 
 
37
import bzrlib
 
38
from bzrlib.errors import (BzrError,
 
39
                           BzrCheckError,
 
40
                           BzrCommandError,
 
41
                           BzrOptionError,
 
42
                           NotBranchError)
 
43
from bzrlib.option import Option
 
44
from bzrlib.revisionspec import RevisionSpec
 
45
from bzrlib.symbol_versioning import *
 
46
import bzrlib.trace
 
47
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
48
 
 
49
plugin_cmds = {}
 
50
 
 
51
 
 
52
def register_command(cmd, decorate=False):
 
53
    """Utility function to help register a command
 
54
 
 
55
    :param cmd: Command subclass to register
 
56
    :param decorate: If true, allow overriding an existing command
 
57
        of the same name; the old command is returned by this function.
 
58
        Otherwise it is an error to try to override an existing command.
 
59
    """
 
60
    global plugin_cmds
 
61
    k = cmd.__name__
 
62
    if k.startswith("cmd_"):
 
63
        k_unsquished = _unsquish_command_name(k)
 
64
    else:
 
65
        k_unsquished = k
 
66
    if not plugin_cmds.has_key(k_unsquished):
 
67
        plugin_cmds[k_unsquished] = cmd
 
68
        mutter('registered plugin command %s', k_unsquished)
 
69
        if decorate and k_unsquished in builtin_command_names():
 
70
            return _builtin_commands()[k_unsquished]
 
71
    elif decorate:
 
72
        result = plugin_cmds[k_unsquished]
 
73
        plugin_cmds[k_unsquished] = cmd
 
74
        return result
 
75
    else:
 
76
        log_error('Two plugins defined the same command: %r' % k)
 
77
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
78
 
 
79
 
 
80
def _squish_command_name(cmd):
 
81
    return 'cmd_' + cmd.replace('-', '_')
 
82
 
 
83
 
 
84
def _unsquish_command_name(cmd):
 
85
    assert cmd.startswith("cmd_")
 
86
    return cmd[4:].replace('_','-')
 
87
 
 
88
 
 
89
def _builtin_commands():
 
90
    import bzrlib.builtins
 
91
    r = {}
 
92
    builtins = bzrlib.builtins.__dict__
 
93
    for name in builtins:
 
94
        if name.startswith("cmd_"):
 
95
            real_name = _unsquish_command_name(name)
 
96
            r[real_name] = builtins[name]
 
97
    return r
 
98
            
 
99
 
 
100
def builtin_command_names():
 
101
    """Return list of builtin command names."""
 
102
    return _builtin_commands().keys()
 
103
    
 
104
 
 
105
def plugin_command_names():
 
106
    return plugin_cmds.keys()
 
107
 
 
108
 
 
109
def _get_cmd_dict(plugins_override=True):
 
110
    """Return name->class mapping for all commands."""
 
111
    d = _builtin_commands()
 
112
    if plugins_override:
 
113
        d.update(plugin_cmds)
 
114
    return d
 
115
 
 
116
    
 
117
def get_all_cmds(plugins_override=True):
 
118
    """Return canonical name and class for all registered commands."""
 
119
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
120
        yield k,v
 
121
 
 
122
 
 
123
def get_cmd_object(cmd_name, plugins_override=True):
 
124
    """Return the canonical name and command class for a command.
 
125
 
 
126
    plugins_override
 
127
        If true, plugin commands can override builtins.
 
128
    """
 
129
    from bzrlib.externalcommand import ExternalCommand
 
130
 
 
131
    cmd_name = str(cmd_name)            # not unicode
 
132
 
 
133
    # first look up this command under the specified name
 
134
    cmds = _get_cmd_dict(plugins_override=plugins_override)
 
135
    try:
 
136
        return cmds[cmd_name]()
 
137
    except KeyError:
 
138
        pass
 
139
 
 
140
    # look for any command which claims this as an alias
 
141
    for real_cmd_name, cmd_class in cmds.iteritems():
 
142
        if cmd_name in cmd_class.aliases:
 
143
            return cmd_class()
 
144
 
 
145
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
146
    if cmd_obj:
 
147
        return cmd_obj
 
148
 
 
149
    raise BzrCommandError("unknown command %r" % cmd_name)
 
150
 
 
151
 
 
152
class Command(object):
 
153
    """Base class for commands.
 
154
 
 
155
    Commands are the heart of the command-line bzr interface.
 
156
 
 
157
    The command object mostly handles the mapping of command-line
 
158
    parameters into one or more bzrlib operations, and of the results
 
159
    into textual output.
 
160
 
 
161
    Commands normally don't have any state.  All their arguments are
 
162
    passed in to the run method.  (Subclasses may take a different
 
163
    policy if the behaviour of the instance needs to depend on e.g. a
 
164
    shell plugin and not just its Python class.)
 
165
 
 
166
    The docstring for an actual command should give a single-line
 
167
    summary, then a complete description of the command.  A grammar
 
168
    description will be inserted.
 
169
 
 
170
    aliases
 
171
        Other accepted names for this command.
 
172
 
 
173
    takes_args
 
174
        List of argument forms, marked with whether they are optional,
 
175
        repeated, etc.
 
176
 
 
177
                Examples:
 
178
 
 
179
                ['to_location', 'from_branch?', 'file*']
 
180
 
 
181
                'to_location' is required
 
182
                'from_branch' is optional
 
183
                'file' can be specified 0 or more times
 
184
 
 
185
    takes_options
 
186
        List of options that may be given for this command.  These can
 
187
        be either strings, referring to globally-defined options,
 
188
        or option objects.  Retrieve through options().
 
189
 
 
190
    hidden
 
191
        If true, this command isn't advertised.  This is typically
 
192
        for commands intended for expert users.
 
193
 
 
194
    encoding_type
 
195
        Command objects will get a 'outf' attribute, which has been
 
196
        setup to properly handle encoding of unicode strings.
 
197
        encoding_type determines what will happen when characters cannot
 
198
        be encoded
 
199
            strict - abort if we cannot decode
 
200
            replace - put in a bogus character (typically '?')
 
201
            exact - do not encode sys.stdout
 
202
 
 
203
    """
 
204
    aliases = []
 
205
    takes_args = []
 
206
    takes_options = []
 
207
    encoding_type = 'strict'
 
208
 
 
209
    hidden = False
 
210
    
 
211
    def __init__(self):
 
212
        """Construct an instance of this command."""
 
213
        if self.__doc__ == Command.__doc__:
 
214
            warn("No help message set for %r" % self)
 
215
 
 
216
    def options(self):
 
217
        """Return dict of valid options for this command.
 
218
 
 
219
        Maps from long option name to option object."""
 
220
        r = dict()
 
221
        r['help'] = Option.OPTIONS['help']
 
222
        for o in self.takes_options:
 
223
            if not isinstance(o, Option):
 
224
                o = Option.OPTIONS[o]
 
225
            r[o.name] = o
 
226
        return r
 
227
 
 
228
    def _setup_outf(self):
 
229
        """Return a file linked to stdout, which has proper encoding."""
 
230
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
231
 
 
232
        # Originally I was using self.stdout, but that looks
 
233
        # *way* too much like sys.stdout
 
234
        if self.encoding_type == 'exact':
 
235
            self.outf = sys.stdout
 
236
            return
 
237
 
 
238
        output_encoding = getattr(sys.stdout, 'encoding', None)
 
239
        if not output_encoding:
 
240
            output_encoding = bzrlib.user_encoding
 
241
            mutter('encoding stdout bzrlib.user_encoding %r', output_encoding)
 
242
        else:
 
243
            mutter('encoding stdout log as sys.stdout encoding %r', output_encoding)
 
244
 
 
245
        # use 'replace' so that we don't abort if trying to write out
 
246
        # in e.g. the default C locale.
 
247
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
248
 
 
249
    @deprecated_method(zero_eight)
 
250
    def run_argv(self, argv):
 
251
        """Parse command line and run.
 
252
        
 
253
        See run_argv_aliases for the 0.8 and beyond api.
 
254
        """
 
255
        return self.run_argv_aliases(argv)
 
256
 
 
257
    def run_argv_aliases(self, argv, alias_argv=None):
 
258
        """Parse the command line and run with extra aliases in alias_argv."""
 
259
        args, opts = parse_args(self, argv, alias_argv)
 
260
        if 'help' in opts:  # e.g. bzr add --help
 
261
            from bzrlib.help import help_on_command
 
262
            help_on_command(self.name())
 
263
            return 0
 
264
        # XXX: This should be handled by the parser
 
265
        allowed_names = self.options().keys()
 
266
        for oname in opts:
 
267
            if oname not in allowed_names:
 
268
                raise BzrCommandError("option '--%s' is not allowed for"
 
269
                                      " command %r" % (oname, self.name()))
 
270
        # mix arguments and options into one dictionary
 
271
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
272
        cmdopts = {}
 
273
        for k, v in opts.items():
 
274
            cmdopts[k.replace('-', '_')] = v
 
275
 
 
276
        all_cmd_args = cmdargs.copy()
 
277
        all_cmd_args.update(cmdopts)
 
278
 
 
279
        self._setup_outf()
 
280
 
 
281
        return self.run(**all_cmd_args)
 
282
    
 
283
    def run(self):
 
284
        """Actually run the command.
 
285
 
 
286
        This is invoked with the options and arguments bound to
 
287
        keyword parameters.
 
288
 
 
289
        Return 0 or None if the command was successful, or a non-zero
 
290
        shell error code if not.  It's OK for this method to allow
 
291
        an exception to raise up.
 
292
        """
 
293
        raise NotImplementedError('no implementation of command %r' 
 
294
                                  % self.name())
 
295
 
 
296
    def help(self):
 
297
        """Return help message for this class."""
 
298
        from inspect import getdoc
 
299
        if self.__doc__ is Command.__doc__:
 
300
            return None
 
301
        return getdoc(self)
 
302
 
 
303
    def name(self):
 
304
        return _unsquish_command_name(self.__class__.__name__)
 
305
 
 
306
 
 
307
def parse_spec(spec):
 
308
    """
 
309
    >>> parse_spec(None)
 
310
    [None, None]
 
311
    >>> parse_spec("./")
 
312
    ['./', None]
 
313
    >>> parse_spec("../@")
 
314
    ['..', -1]
 
315
    >>> parse_spec("../f/@35")
 
316
    ['../f', 35]
 
317
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
318
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
319
    """
 
320
    if spec is None:
 
321
        return [None, None]
 
322
    if '/@' in spec:
 
323
        parsed = spec.split('/@')
 
324
        assert len(parsed) == 2
 
325
        if parsed[1] == "":
 
326
            parsed[1] = -1
 
327
        else:
 
328
            try:
 
329
                parsed[1] = int(parsed[1])
 
330
            except ValueError:
 
331
                pass # We can allow stuff like ./@revid:blahblahblah
 
332
            else:
 
333
                assert parsed[1] >=0
 
334
    else:
 
335
        parsed = [spec, None]
 
336
    return parsed
 
337
 
 
338
def parse_args(command, argv, alias_argv=None):
 
339
    """Parse command line.
 
340
    
 
341
    Arguments and options are parsed at this level before being passed
 
342
    down to specific command handlers.  This routine knows, from a
 
343
    lookup table, something about the available options, what optargs
 
344
    they take, and which commands will accept them.
 
345
    """
 
346
    # TODO: chop up this beast; make it a method of the Command
 
347
    args = []
 
348
    opts = {}
 
349
    alias_opts = {}
 
350
 
 
351
    cmd_options = command.options()
 
352
    argsover = False
 
353
    proc_aliasarg = True # Are we processing alias_argv now?
 
354
    for proc_argv in alias_argv, argv:
 
355
        while proc_argv:
 
356
            a = proc_argv.pop(0)
 
357
            if argsover:
 
358
                args.append(a)
 
359
                continue
 
360
            elif a == '--':
 
361
                # We've received a standalone -- No more flags
 
362
                argsover = True
 
363
                continue
 
364
            if a[0] == '-':
 
365
                # option names must not be unicode
 
366
                a = str(a)
 
367
                optarg = None
 
368
                if a[1] == '-':
 
369
                    mutter("  got option %r", a)
 
370
                    if '=' in a:
 
371
                        optname, optarg = a[2:].split('=', 1)
 
372
                    else:
 
373
                        optname = a[2:]
 
374
                    if optname not in cmd_options:
 
375
                        raise BzrOptionError('unknown long option %r for'
 
376
                                             ' command %s' % 
 
377
                                             (a, command.name()))
 
378
                else:
 
379
                    shortopt = a[1:]
 
380
                    if shortopt in Option.SHORT_OPTIONS:
 
381
                        # Multi-character options must have a space to delimit
 
382
                        # their value
 
383
                        # ^^^ what does this mean? mbp 20051014
 
384
                        optname = Option.SHORT_OPTIONS[shortopt].name
 
385
                    else:
 
386
                        # Single character short options, can be chained,
 
387
                        # and have their value appended to their name
 
388
                        shortopt = a[1:2]
 
389
                        if shortopt not in Option.SHORT_OPTIONS:
 
390
                            # We didn't find the multi-character name, and we
 
391
                            # didn't find the single char name
 
392
                            raise BzrError('unknown short option %r' % a)
 
393
                        optname = Option.SHORT_OPTIONS[shortopt].name
 
394
 
 
395
                        if a[2:]:
 
396
                            # There are extra things on this option
 
397
                            # see if it is the value, or if it is another
 
398
                            # short option
 
399
                            optargfn = Option.OPTIONS[optname].type
 
400
                            if optargfn is None:
 
401
                                # This option does not take an argument, so the
 
402
                                # next entry is another short option, pack it
 
403
                                # back into the list
 
404
                                proc_argv.insert(0, '-' + a[2:])
 
405
                            else:
 
406
                                # This option takes an argument, so pack it
 
407
                                # into the array
 
408
                                optarg = a[2:]
 
409
                
 
410
                    if optname not in cmd_options:
 
411
                        raise BzrOptionError('unknown short option %r for'
 
412
                                             ' command %s' % 
 
413
                                             (shortopt, command.name()))
 
414
                if optname in opts:
 
415
                    # XXX: Do we ever want to support this, e.g. for -r?
 
416
                    if proc_aliasarg:
 
417
                        raise BzrError('repeated option %r' % a)
 
418
                    elif optname in alias_opts:
 
419
                        # Replace what's in the alias with what's in the real
 
420
                        # argument
 
421
                        del alias_opts[optname]
 
422
                        del opts[optname]
 
423
                        proc_argv.insert(0, a)
 
424
                        continue
 
425
                    else:
 
426
                        raise BzrError('repeated option %r' % a)
 
427
                    
 
428
                option_obj = cmd_options[optname]
 
429
                optargfn = option_obj.type
 
430
                if optargfn:
 
431
                    if optarg == None:
 
432
                        if not proc_argv:
 
433
                            raise BzrError('option %r needs an argument' % a)
 
434
                        else:
 
435
                            optarg = proc_argv.pop(0)
 
436
                    opts[optname] = optargfn(optarg)
 
437
                    if proc_aliasarg:
 
438
                        alias_opts[optname] = optargfn(optarg)
 
439
                else:
 
440
                    if optarg != None:
 
441
                        raise BzrError('option %r takes no argument' % optname)
 
442
                    opts[optname] = True
 
443
                    if proc_aliasarg:
 
444
                        alias_opts[optname] = True
 
445
            else:
 
446
                args.append(a)
 
447
        proc_aliasarg = False # Done with alias argv
 
448
    return args, opts
 
449
 
 
450
 
 
451
def _match_argform(cmd, takes_args, args):
 
452
    argdict = {}
 
453
 
 
454
    # step through args and takes_args, allowing appropriate 0-many matches
 
455
    for ap in takes_args:
 
456
        argname = ap[:-1]
 
457
        if ap[-1] == '?':
 
458
            if args:
 
459
                argdict[argname] = args.pop(0)
 
460
        elif ap[-1] == '*': # all remaining arguments
 
461
            if args:
 
462
                argdict[argname + '_list'] = args[:]
 
463
                args = []
 
464
            else:
 
465
                argdict[argname + '_list'] = None
 
466
        elif ap[-1] == '+':
 
467
            if not args:
 
468
                raise BzrCommandError("command %r needs one or more %s"
 
469
                        % (cmd, argname.upper()))
 
470
            else:
 
471
                argdict[argname + '_list'] = args[:]
 
472
                args = []
 
473
        elif ap[-1] == '$': # all but one
 
474
            if len(args) < 2:
 
475
                raise BzrCommandError("command %r needs one or more %s"
 
476
                        % (cmd, argname.upper()))
 
477
            argdict[argname + '_list'] = args[:-1]
 
478
            args[:-1] = []                
 
479
        else:
 
480
            # just a plain arg
 
481
            argname = ap
 
482
            if not args:
 
483
                raise BzrCommandError("command %r requires argument %s"
 
484
                        % (cmd, argname.upper()))
 
485
            else:
 
486
                argdict[argname] = args.pop(0)
 
487
            
 
488
    if args:
 
489
        raise BzrCommandError("extra argument to command %s: %s"
 
490
                              % (cmd, args[0]))
 
491
 
 
492
    return argdict
 
493
 
 
494
 
 
495
 
 
496
def apply_profiled(the_callable, *args, **kwargs):
 
497
    import hotshot
 
498
    import tempfile
 
499
    import hotshot.stats
 
500
    pffileno, pfname = tempfile.mkstemp()
 
501
    try:
 
502
        prof = hotshot.Profile(pfname)
 
503
        try:
 
504
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
505
        finally:
 
506
            prof.close()
 
507
        stats = hotshot.stats.load(pfname)
 
508
        stats.strip_dirs()
 
509
        stats.sort_stats('cum')   # 'time'
 
510
        ## XXX: Might like to write to stderr or the trace file instead but
 
511
        ## print_stats seems hardcoded to stdout
 
512
        stats.print_stats(20)
 
513
        return ret
 
514
    finally:
 
515
        os.close(pffileno)
 
516
        os.remove(pfname)
 
517
 
 
518
 
 
519
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
520
    from bzrlib.lsprof import profile
 
521
    import cPickle
 
522
    ret, stats = profile(the_callable, *args, **kwargs)
 
523
    stats.sort()
 
524
    if filename is None:
 
525
        stats.pprint()
 
526
    else:
 
527
        stats.freeze()
 
528
        cPickle.dump(stats, open(filename, 'w'), 2)
 
529
        print 'Profile data written to %r.' % filename
 
530
    return ret
 
531
 
 
532
 
 
533
def get_alias(cmd):
 
534
    """Return an expanded alias, or None if no alias exists"""
 
535
    import bzrlib.config
 
536
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
537
    if (alias):
 
538
        return alias.split(' ')
 
539
    return None
 
540
 
 
541
 
 
542
def run_bzr(argv):
 
543
    """Execute a command.
 
544
 
 
545
    This is similar to main(), but without all the trappings for
 
546
    logging and error handling.  
 
547
    
 
548
    argv
 
549
       The command-line arguments, without the program name from argv[0]
 
550
       These should already be decoded. All library/test code calling
 
551
       run_bzr should be passing valid strings (don't need decoding).
 
552
    
 
553
    Returns a command status or raises an exception.
 
554
 
 
555
    Special master options: these must come before the command because
 
556
    they control how the command is interpreted.
 
557
 
 
558
    --no-plugins
 
559
        Do not load plugin modules at all
 
560
 
 
561
    --no-aliases
 
562
        Do not allow aliases
 
563
 
 
564
    --builtin
 
565
        Only use builtin commands.  (Plugins are still allowed to change
 
566
        other behaviour.)
 
567
 
 
568
    --profile
 
569
        Run under the Python hotshot profiler.
 
570
 
 
571
    --lsprof
 
572
        Run under the Python lsprof profiler.
 
573
    """
 
574
    argv = list(argv)
 
575
 
 
576
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
577
                opt_no_aliases = False
 
578
    opt_lsprof_file = None
 
579
 
 
580
    # --no-plugins is handled specially at a very early stage. We need
 
581
    # to load plugins before doing other command parsing so that they
 
582
    # can override commands, but this needs to happen first.
 
583
 
 
584
    argv_copy = []
 
585
    i = 0
 
586
    while i < len(argv):
 
587
        a = argv[i]
 
588
        if a == '--profile':
 
589
            opt_profile = True
 
590
        elif a == '--lsprof':
 
591
            opt_lsprof = True
 
592
        elif a == '--lsprof-file':
 
593
            opt_lsprof_file = argv[i + 1]
 
594
            i += 1
 
595
        elif a == '--no-plugins':
 
596
            opt_no_plugins = True
 
597
        elif a == '--no-aliases':
 
598
            opt_no_aliases = True
 
599
        elif a == '--builtin':
 
600
            opt_builtin = True
 
601
        elif a in ('--quiet', '-q'):
 
602
            be_quiet()
 
603
        else:
 
604
            argv_copy.append(a)
 
605
        i += 1
 
606
 
 
607
    argv = argv_copy
 
608
    if (not argv):
 
609
        from bzrlib.builtins import cmd_help
 
610
        cmd_help().run_argv_aliases([])
 
611
        return 0
 
612
 
 
613
    if argv[0] == '--version':
 
614
        from bzrlib.builtins import show_version
 
615
        show_version()
 
616
        return 0
 
617
        
 
618
    if not opt_no_plugins:
 
619
        from bzrlib.plugin import load_plugins
 
620
        load_plugins()
 
621
    else:
 
622
        from bzrlib.plugin import disable_plugins
 
623
        disable_plugins()
 
624
 
 
625
    alias_argv = None
 
626
 
 
627
    if not opt_no_aliases:
 
628
        alias_argv = get_alias(argv[0])
 
629
        if alias_argv:
 
630
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
631
            argv[0] = alias_argv.pop(0)
 
632
 
 
633
    cmd = str(argv.pop(0))
 
634
 
 
635
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
636
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
637
        run = cmd_obj.run_argv
 
638
        run_argv = [argv]
 
639
    else:
 
640
        run = cmd_obj.run_argv_aliases
 
641
        run_argv = [argv, alias_argv]
 
642
 
 
643
    try:
 
644
        if opt_lsprof:
 
645
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
646
        elif opt_profile:
 
647
            ret = apply_profiled(run, *run_argv)
 
648
        else:
 
649
            ret = run(*run_argv)
 
650
        return ret or 0
 
651
    finally:
 
652
        # reset, in case we may do other commands later within the same process
 
653
        be_quiet(False)
 
654
 
 
655
def display_command(func):
 
656
    """Decorator that suppresses pipe/interrupt errors."""
 
657
    def ignore_pipe(*args, **kwargs):
 
658
        try:
 
659
            result = func(*args, **kwargs)
 
660
            sys.stdout.flush()
 
661
            return result
 
662
        except IOError, e:
 
663
            if not hasattr(e, 'errno'):
 
664
                raise
 
665
            if e.errno != errno.EPIPE:
 
666
                raise
 
667
            pass
 
668
        except KeyboardInterrupt:
 
669
            pass
 
670
    return ignore_pipe
 
671
 
 
672
 
 
673
def main(argv):
 
674
    import bzrlib.ui
 
675
    from bzrlib.ui.text import TextUIFactory
 
676
    ## bzrlib.trace.enable_default_logging()
 
677
    bzrlib.trace.log_startup(argv)
 
678
    bzrlib.ui.ui_factory = TextUIFactory()
 
679
 
 
680
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
681
    ret = run_bzr_catch_errors(argv)
 
682
    mutter("return code %d", ret)
 
683
    return ret
 
684
 
 
685
 
 
686
def run_bzr_catch_errors(argv):
 
687
    try:
 
688
        try:
 
689
            return run_bzr(argv)
 
690
        finally:
 
691
            # do this here inside the exception wrappers to catch EPIPE
 
692
            sys.stdout.flush()
 
693
    except Exception, e:
 
694
        # used to handle AssertionError and KeyboardInterrupt
 
695
        # specially here, but hopefully they're handled ok by the logger now
 
696
        import errno
 
697
        if (isinstance(e, IOError) 
 
698
            and hasattr(e, 'errno')
 
699
            and e.errno == errno.EPIPE):
 
700
            bzrlib.trace.note('broken pipe')
 
701
            return 3
 
702
        else:
 
703
            bzrlib.trace.log_exception()
 
704
            if os.environ.get('BZR_PDB'):
 
705
                print '**** entering debugger'
 
706
                import pdb
 
707
                pdb.post_mortem(sys.exc_traceback)
 
708
            return 3
 
709
 
 
710
if __name__ == '__main__':
 
711
    sys.exit(main(sys.argv))