~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: 2007-02-01 20:36:41 UTC
  • mfrom: (2249.2.1 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070201203641-a125a85aee82c725
(John Arbash Meinel) hard-code the whitespace chars to avoid problems in some locales.

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 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.
 
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.
26
27
 
27
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
28
29
# the profile output behind so it can be interactively examined?
29
30
 
 
31
import os
30
32
import sys
31
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
32
38
from warnings import warn
33
 
from inspect import getdoc
34
39
 
35
40
import bzrlib
36
 
import bzrlib.trace
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
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    trace,
 
47
    )
 
48
""")
 
49
 
 
50
from bzrlib.symbol_versioning import (
 
51
    deprecated_function,
 
52
    deprecated_method,
 
53
    zero_eight,
 
54
    zero_eleven,
 
55
    )
 
56
# Compatibility
 
57
from bzrlib.option import Option
 
58
 
41
59
 
42
60
plugin_cmds = {}
43
61
 
44
62
 
45
 
def register_command(cmd):
46
 
    "Utility function to help register a command"
 
63
def register_command(cmd, decorate=False):
 
64
    """Utility function to help register a command
 
65
 
 
66
    :param cmd: Command subclass to register
 
67
    :param decorate: If true, allow overriding an existing command
 
68
        of the same name; the old command is returned by this function.
 
69
        Otherwise it is an error to try to override an existing command.
 
70
    """
47
71
    global plugin_cmds
48
72
    k = cmd.__name__
49
73
    if k.startswith("cmd_"):
50
74
        k_unsquished = _unsquish_command_name(k)
51
75
    else:
52
76
        k_unsquished = k
53
 
    if not plugin_cmds.has_key(k_unsquished):
54
 
        plugin_cmds[k_unsquished] = cmd
55
 
        mutter('registered plugin command %s', k_unsquished)      
 
77
    if k_unsquished not in plugin_cmds:
 
78
        plugin_cmds[k_unsquished] = cmd
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
 
80
        if decorate and k_unsquished in builtin_command_names():
 
81
            return _builtin_commands()[k_unsquished]
 
82
    elif decorate:
 
83
        result = plugin_cmds[k_unsquished]
 
84
        plugin_cmds[k_unsquished] = cmd
 
85
        return result
56
86
    else:
57
 
        log_error('Two plugins defined the same command: %r' % k)
58
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
59
89
 
60
90
 
61
91
def _squish_command_name(cmd):
67
97
    return cmd[4:].replace('_','-')
68
98
 
69
99
 
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
 
 
138
100
def _builtin_commands():
139
101
    import bzrlib.builtins
140
102
    r = {}
141
103
    builtins = bzrlib.builtins.__dict__
142
104
    for name in builtins:
143
105
        if name.startswith("cmd_"):
144
 
            real_name = _unsquish_command_name(name)        
 
106
            real_name = _unsquish_command_name(name)
145
107
            r[real_name] = builtins[name]
146
108
    return r
147
 
 
148
109
            
149
110
 
150
111
def builtin_command_names():
178
139
    """
179
140
    from bzrlib.externalcommand import ExternalCommand
180
141
 
181
 
    cmd_name = str(cmd_name)            # not unicode
 
142
    # We want only 'ascii' command names, but the user may have typed
 
143
    # in a Unicode name. In that case, they should just get a
 
144
    # 'command not found' error later.
 
145
    # In the future, we may actually support Unicode command names.
182
146
 
183
147
    # first look up this command under the specified name
184
148
    cmds = _get_cmd_dict(plugins_override=plugins_override)
196
160
    if cmd_obj:
197
161
        return cmd_obj
198
162
 
199
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
163
    raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
200
164
 
201
165
 
202
166
class Command(object):
224
188
        List of argument forms, marked with whether they are optional,
225
189
        repeated, etc.
226
190
 
 
191
                Examples:
 
192
 
 
193
                ['to_location', 'from_branch?', 'file*']
 
194
 
 
195
                'to_location' is required
 
196
                'from_branch' is optional
 
197
                'file' can be specified 0 or more times
 
198
 
227
199
    takes_options
228
 
        List of options that may be given for this command.
 
200
        List of options that may be given for this command.  These can
 
201
        be either strings, referring to globally-defined options,
 
202
        or option objects.  Retrieve through options().
229
203
 
230
204
    hidden
231
205
        If true, this command isn't advertised.  This is typically
232
206
        for commands intended for expert users.
 
207
 
 
208
    encoding_type
 
209
        Command objects will get a 'outf' attribute, which has been
 
210
        setup to properly handle encoding of unicode strings.
 
211
        encoding_type determines what will happen when characters cannot
 
212
        be encoded
 
213
            strict - abort if we cannot decode
 
214
            replace - put in a bogus character (typically '?')
 
215
            exact - do not encode sys.stdout
 
216
 
 
217
            NOTE: by default on Windows, sys.stdout is opened as a text
 
218
            stream, therefore LF line-endings are converted to CRLF.
 
219
            When a command uses encoding_type = 'exact', then
 
220
            sys.stdout is forced to be a binary stream, and line-endings
 
221
            will not mangled.
 
222
 
233
223
    """
234
224
    aliases = []
235
 
    
236
225
    takes_args = []
237
226
    takes_options = []
 
227
    encoding_type = 'strict'
238
228
 
239
229
    hidden = False
240
230
    
243
233
        if self.__doc__ == Command.__doc__:
244
234
            warn("No help message set for %r" % self)
245
235
 
246
 
 
 
236
    def options(self):
 
237
        """Return dict of valid options for this command.
 
238
 
 
239
        Maps from long option name to option object."""
 
240
        r = dict()
 
241
        r['help'] = option.Option.OPTIONS['help']
 
242
        for o in self.takes_options:
 
243
            if isinstance(o, basestring):
 
244
                o = option.Option.OPTIONS[o]
 
245
            r[o.name] = o
 
246
        return r
 
247
 
 
248
    def _setup_outf(self):
 
249
        """Return a file linked to stdout, which has proper encoding."""
 
250
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
251
 
 
252
        # Originally I was using self.stdout, but that looks
 
253
        # *way* too much like sys.stdout
 
254
        if self.encoding_type == 'exact':
 
255
            # force sys.stdout to be binary stream on win32
 
256
            if sys.platform == 'win32':
 
257
                fileno = getattr(sys.stdout, 'fileno', None)
 
258
                if fileno:
 
259
                    import msvcrt
 
260
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
261
            self.outf = sys.stdout
 
262
            return
 
263
 
 
264
        output_encoding = osutils.get_terminal_encoding()
 
265
 
 
266
        # use 'replace' so that we don't abort if trying to write out
 
267
        # in e.g. the default C locale.
 
268
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
269
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
270
        # it just returns the encoding of the wrapped file, which is completely
 
271
        # bogus. So set the attribute, so we can find the correct encoding later.
 
272
        self.outf.encoding = output_encoding
 
273
 
 
274
    @deprecated_method(zero_eight)
247
275
    def run_argv(self, argv):
248
 
        """Parse command line and run."""
249
 
        args, opts = parse_args(argv)
 
276
        """Parse command line and run.
 
277
        
 
278
        See run_argv_aliases for the 0.8 and beyond api.
 
279
        """
 
280
        return self.run_argv_aliases(argv)
250
281
 
 
282
    def run_argv_aliases(self, argv, alias_argv=None):
 
283
        """Parse the command line and run with extra aliases in alias_argv."""
 
284
        if argv is None:
 
285
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
286
                 DeprecationWarning, stacklevel=2)
 
287
            argv = []
 
288
        args, opts = parse_args(self, argv, alias_argv)
251
289
        if 'help' in opts:  # e.g. bzr add --help
252
290
            from bzrlib.help import help_on_command
253
291
            help_on_command(self.name())
254
292
            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
 
 
263
293
        # mix arguments and options into one dictionary
264
294
        cmdargs = _match_argform(self.name(), self.takes_args, args)
265
295
        cmdopts = {}
269
299
        all_cmd_args = cmdargs.copy()
270
300
        all_cmd_args.update(cmdopts)
271
301
 
 
302
        self._setup_outf()
 
303
 
272
304
        return self.run(**all_cmd_args)
273
 
 
274
305
    
275
306
    def run(self):
276
307
        """Actually run the command.
282
313
        shell error code if not.  It's OK for this method to allow
283
314
        an exception to raise up.
284
315
        """
285
 
        raise NotImplementedError()
286
 
 
 
316
        raise NotImplementedError('no implementation of command %r'
 
317
                                  % self.name())
287
318
 
288
319
    def help(self):
289
320
        """Return help message for this class."""
 
321
        from inspect import getdoc
290
322
        if self.__doc__ is Command.__doc__:
291
323
            return None
292
324
        return getdoc(self)
294
326
    def name(self):
295
327
        return _unsquish_command_name(self.__class__.__name__)
296
328
 
297
 
 
 
329
    def plugin_name(self):
 
330
        """Get the name of the plugin that provides this command.
 
331
 
 
332
        :return: The name of the plugin or None if the command is builtin.
 
333
        """
 
334
        mod_parts = self.__module__.split('.')
 
335
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
336
            return mod_parts[2]
 
337
        else:
 
338
            return None
 
339
 
 
340
 
 
341
# Technically, this function hasn't been use in a *really* long time
 
342
# but we are only deprecating it now.
 
343
@deprecated_function(zero_eleven)
298
344
def parse_spec(spec):
299
345
    """
300
346
    >>> parse_spec(None)
326
372
        parsed = [spec, None]
327
373
    return parsed
328
374
 
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):
 
375
def parse_args(command, argv, alias_argv=None):
372
376
    """Parse command line.
373
377
    
374
378
    Arguments and options are parsed at this level before being passed
375
379
    down to specific command handlers.  This routine knows, from a
376
380
    lookup table, something about the available options, what optargs
377
381
    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>]})
397
382
    """
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
 
 
 
383
    # TODO: make it a method of the Command?
 
384
    parser = option.get_optparser(command.options())
 
385
    if alias_argv is not None:
 
386
        args = alias_argv + argv
 
387
    else:
 
388
        args = argv
 
389
 
 
390
    options, args = parser.parse_args(args)
 
391
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
392
                 v is not option.OptionParser.DEFAULT_VALUE])
470
393
    return args, opts
471
394
 
472
395
 
473
 
 
474
 
 
475
396
def _match_argform(cmd, takes_args, args):
476
397
    argdict = {}
477
398
 
489
410
                argdict[argname + '_list'] = None
490
411
        elif ap[-1] == '+':
491
412
            if not args:
492
 
                raise BzrCommandError("command %r needs one or more %s"
493
 
                        % (cmd, argname.upper()))
 
413
                raise errors.BzrCommandError("command %r needs one or more %s"
 
414
                                             % (cmd, argname.upper()))
494
415
            else:
495
416
                argdict[argname + '_list'] = args[:]
496
417
                args = []
497
418
        elif ap[-1] == '$': # all but one
498
419
            if len(args) < 2:
499
 
                raise BzrCommandError("command %r needs one or more %s"
500
 
                        % (cmd, argname.upper()))
 
420
                raise errors.BzrCommandError("command %r needs one or more %s"
 
421
                                             % (cmd, argname.upper()))
501
422
            argdict[argname + '_list'] = args[:-1]
502
 
            args[:-1] = []                
 
423
            args[:-1] = []
503
424
        else:
504
425
            # just a plain arg
505
426
            argname = ap
506
427
            if not args:
507
 
                raise BzrCommandError("command %r requires argument %s"
508
 
                        % (cmd, argname.upper()))
 
428
                raise errors.BzrCommandError("command %r requires argument %s"
 
429
                               % (cmd, argname.upper()))
509
430
            else:
510
431
                argdict[argname] = args.pop(0)
511
432
            
512
433
    if args:
513
 
        raise BzrCommandError("extra argument to command %s: %s"
514
 
                              % (cmd, args[0]))
 
434
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
435
                                     % (cmd, args[0]))
515
436
 
516
437
    return argdict
517
438
 
540
461
        os.remove(pfname)
541
462
 
542
463
 
 
464
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
465
    from bzrlib.lsprof import profile
 
466
    import cPickle
 
467
    ret, stats = profile(the_callable, *args, **kwargs)
 
468
    stats.sort()
 
469
    if filename is None:
 
470
        stats.pprint()
 
471
    else:
 
472
        stats.freeze()
 
473
        cPickle.dump(stats, open(filename, 'w'), 2)
 
474
        print 'Profile data written to %r.' % filename
 
475
    return ret
 
476
 
 
477
 
 
478
def get_alias(cmd, config=None):
 
479
    """Return an expanded alias, or None if no alias exists.
 
480
 
 
481
    cmd
 
482
        Command to be checked for an alias.
 
483
    config
 
484
        Used to specify an alternative config to use,
 
485
        which is especially useful for testing.
 
486
        If it is unspecified, the global config will be used.
 
487
    """
 
488
    if config is None:
 
489
        import bzrlib.config
 
490
        config = bzrlib.config.GlobalConfig()
 
491
    alias = config.get_alias(cmd)
 
492
    if (alias):
 
493
        import shlex
 
494
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
 
495
    return None
 
496
 
 
497
 
543
498
def run_bzr(argv):
544
499
    """Execute a command.
545
500
 
548
503
    
549
504
    argv
550
505
       The command-line arguments, without the program name from argv[0]
 
506
       These should already be decoded. All library/test code calling
 
507
       run_bzr should be passing valid strings (don't need decoding).
551
508
    
552
509
    Returns a command status or raises an exception.
553
510
 
557
514
    --no-plugins
558
515
        Do not load plugin modules at all
559
516
 
 
517
    --no-aliases
 
518
        Do not allow aliases
 
519
 
560
520
    --builtin
561
521
        Only use builtin commands.  (Plugins are still allowed to change
562
522
        other behaviour.)
563
523
 
564
524
    --profile
565
 
        Run under the Python profiler.
 
525
        Run under the Python hotshot profiler.
 
526
 
 
527
    --lsprof
 
528
        Run under the Python lsprof profiler.
566
529
    """
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]
 
530
    argv = list(argv)
 
531
    trace.mutter("bzr arguments: %r", argv)
571
532
 
572
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
533
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
534
                opt_no_aliases = False
 
535
    opt_lsprof_file = None
573
536
 
574
537
    # --no-plugins is handled specially at a very early stage. We need
575
538
    # to load plugins before doing other command parsing so that they
576
539
    # can override commands, but this needs to happen first.
577
540
 
578
 
    for a in argv:
 
541
    argv_copy = []
 
542
    i = 0
 
543
    while i < len(argv):
 
544
        a = argv[i]
579
545
        if a == '--profile':
580
546
            opt_profile = True
 
547
        elif a == '--lsprof':
 
548
            opt_lsprof = True
 
549
        elif a == '--lsprof-file':
 
550
            opt_lsprof = True
 
551
            opt_lsprof_file = argv[i + 1]
 
552
            i += 1
581
553
        elif a == '--no-plugins':
582
554
            opt_no_plugins = True
 
555
        elif a == '--no-aliases':
 
556
            opt_no_aliases = True
583
557
        elif a == '--builtin':
584
558
            opt_builtin = True
 
559
        elif a in ('--quiet', '-q'):
 
560
            trace.be_quiet()
 
561
        elif a.startswith('-D'):
 
562
            debug.debug_flags.add(a[2:])
585
563
        else:
586
 
            break
587
 
        argv.remove(a)
 
564
            argv_copy.append(a)
 
565
        i += 1
588
566
 
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()
 
567
    argv = argv_copy
 
568
    if (not argv):
 
569
        from bzrlib.builtins import cmd_help
 
570
        cmd_help().run_argv_aliases([])
595
571
        return 0
596
572
 
597
573
    if argv[0] == '--version':
598
 
        from bzrlib.builtins import show_version
 
574
        from bzrlib.version import show_version
599
575
        show_version()
600
576
        return 0
601
577
        
602
578
    if not opt_no_plugins:
603
579
        from bzrlib.plugin import load_plugins
604
580
        load_plugins()
605
 
 
606
 
    cmd = str(argv.pop(0))
 
581
    else:
 
582
        from bzrlib.plugin import disable_plugins
 
583
        disable_plugins()
 
584
 
 
585
    alias_argv = None
 
586
 
 
587
    if not opt_no_aliases:
 
588
        alias_argv = get_alias(argv[0])
 
589
        if alias_argv:
 
590
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
591
            argv[0] = alias_argv.pop(0)
 
592
 
 
593
    cmd = argv.pop(0)
 
594
    # We want only 'ascii' command names, but the user may have typed
 
595
    # in a Unicode name. In that case, they should just get a
 
596
    # 'command not found' error later.
607
597
 
608
598
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
609
 
 
610
 
    if opt_profile:
611
 
        ret = apply_profiled(cmd_obj.run_argv, argv)
 
599
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
600
        run = cmd_obj.run_argv
 
601
        run_argv = [argv]
612
602
    else:
613
 
        ret = cmd_obj.run_argv(argv)
614
 
    return ret or 0
 
603
        run = cmd_obj.run_argv_aliases
 
604
        run_argv = [argv, alias_argv]
 
605
 
 
606
    try:
 
607
        if opt_lsprof:
 
608
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
609
        elif opt_profile:
 
610
            ret = apply_profiled(run, *run_argv)
 
611
        else:
 
612
            ret = run(*run_argv)
 
613
        return ret or 0
 
614
    finally:
 
615
        # reset, in case we may do other commands later within the same process
 
616
        trace.be_quiet(False)
 
617
 
 
618
def display_command(func):
 
619
    """Decorator that suppresses pipe/interrupt errors."""
 
620
    def ignore_pipe(*args, **kwargs):
 
621
        try:
 
622
            result = func(*args, **kwargs)
 
623
            sys.stdout.flush()
 
624
            return result
 
625
        except IOError, e:
 
626
            if getattr(e, 'errno', None) is None:
 
627
                raise
 
628
            if e.errno != errno.EPIPE:
 
629
                # Win32 raises IOError with errno=0 on a broken pipe
 
630
                if sys.platform != 'win32' or e.errno != 0:
 
631
                    raise
 
632
            pass
 
633
        except KeyboardInterrupt:
 
634
            pass
 
635
    return ignore_pipe
615
636
 
616
637
 
617
638
def main(argv):
618
639
    import bzrlib.ui
619
 
    bzrlib.trace.log_startup(argv)
620
 
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
621
 
 
622
 
    return run_bzr_catch_errors(argv[1:])
 
640
    from bzrlib.ui.text import TextUIFactory
 
641
    bzrlib.ui.ui_factory = TextUIFactory()
 
642
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
643
    ret = run_bzr_catch_errors(argv)
 
644
    trace.mutter("return code %d", ret)
 
645
    return ret
623
646
 
624
647
 
625
648
def run_bzr_catch_errors(argv):
626
649
    try:
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))
 
650
        return run_bzr(argv)
 
651
        # do this here inside the exception wrappers to catch EPIPE
 
652
        sys.stdout.flush()
 
653
    except (KeyboardInterrupt, Exception), e:
 
654
        # used to handle AssertionError and KeyboardInterrupt
 
655
        # specially here, but hopefully they're handled ok by the logger now
 
656
        trace.report_exception(sys.exc_info(), sys.stderr)
 
657
        if os.environ.get('BZR_PDB'):
 
658
            print '**** entering debugger'
 
659
            import pdb
 
660
            pdb.post_mortem(sys.exc_traceback)
641
661
        return 3
642
 
    except KeyboardInterrupt, e:
643
 
        bzrlib.trace.log_exception('interrupted')
644
 
        return 2
645
 
    except Exception, e:
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
657
662
 
658
663
if __name__ == '__main__':
659
664
    sys.exit(main(sys.argv))