~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2006-06-26 16:23:10 UTC
  • mfrom: (1780.2.1 misc-fixen)
  • mto: This revision was merged to the branch mainline in revision 1815.
  • Revision ID: robertc@robertcollins.net-20060626162310-98f5b55b8cc19d46
(robertc) Misc minor typos and the like.

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.
 
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?
30
31
import sys
31
32
import os
32
33
from warnings import warn
33
 
from inspect import getdoc
 
34
import errno
 
35
import codecs
34
36
 
35
37
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
 
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import (BzrError,
 
40
                           BzrCommandError,
 
41
                           BzrCheckError,
 
42
                           NotBranchError)
 
43
from bzrlib.option import Option
 
44
import bzrlib.osutils
39
45
from bzrlib.revisionspec import RevisionSpec
40
 
from bzrlib import BZRDIR
 
46
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
 
47
from bzrlib import trace
 
48
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
41
49
 
42
50
plugin_cmds = {}
43
51
 
44
52
 
45
 
def register_command(cmd):
46
 
    "Utility function to help register a command"
 
53
def register_command(cmd, decorate=False):
 
54
    """Utility function to help register a command
 
55
 
 
56
    :param cmd: Command subclass to register
 
57
    :param decorate: If true, allow overriding an existing command
 
58
        of the same name; the old command is returned by this function.
 
59
        Otherwise it is an error to try to override an existing command.
 
60
    """
47
61
    global plugin_cmds
48
62
    k = cmd.__name__
49
63
    if k.startswith("cmd_"):
52
66
        k_unsquished = k
53
67
    if not plugin_cmds.has_key(k_unsquished):
54
68
        plugin_cmds[k_unsquished] = cmd
55
 
        mutter('registered plugin command %s', k_unsquished)      
 
69
        mutter('registered plugin command %s', k_unsquished)
 
70
        if decorate and k_unsquished in builtin_command_names():
 
71
            return _builtin_commands()[k_unsquished]
 
72
    elif decorate:
 
73
        result = plugin_cmds[k_unsquished]
 
74
        plugin_cmds[k_unsquished] = cmd
 
75
        return result
56
76
    else:
57
77
        log_error('Two plugins defined the same command: %r' % k)
58
78
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
67
87
    return cmd[4:].replace('_','-')
68
88
 
69
89
 
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
90
def _builtin_commands():
139
91
    import bzrlib.builtins
140
92
    r = {}
141
93
    builtins = bzrlib.builtins.__dict__
142
94
    for name in builtins:
143
95
        if name.startswith("cmd_"):
144
 
            real_name = _unsquish_command_name(name)        
 
96
            real_name = _unsquish_command_name(name)
145
97
            r[real_name] = builtins[name]
146
98
    return r
147
 
 
148
99
            
149
100
 
150
101
def builtin_command_names():
196
147
    if cmd_obj:
197
148
        return cmd_obj
198
149
 
199
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
150
    raise BzrCommandError('unknown command "%s"' % cmd_name)
200
151
 
201
152
 
202
153
class Command(object):
224
175
        List of argument forms, marked with whether they are optional,
225
176
        repeated, etc.
226
177
 
 
178
                Examples:
 
179
 
 
180
                ['to_location', 'from_branch?', 'file*']
 
181
 
 
182
                'to_location' is required
 
183
                'from_branch' is optional
 
184
                'file' can be specified 0 or more times
 
185
 
227
186
    takes_options
228
 
        List of options that may be given for this command.
 
187
        List of options that may be given for this command.  These can
 
188
        be either strings, referring to globally-defined options,
 
189
        or option objects.  Retrieve through options().
229
190
 
230
191
    hidden
231
192
        If true, this command isn't advertised.  This is typically
232
193
        for commands intended for expert users.
 
194
 
 
195
    encoding_type
 
196
        Command objects will get a 'outf' attribute, which has been
 
197
        setup to properly handle encoding of unicode strings.
 
198
        encoding_type determines what will happen when characters cannot
 
199
        be encoded
 
200
            strict - abort if we cannot decode
 
201
            replace - put in a bogus character (typically '?')
 
202
            exact - do not encode sys.stdout
 
203
 
233
204
    """
234
205
    aliases = []
235
 
    
236
206
    takes_args = []
237
207
    takes_options = []
 
208
    encoding_type = 'strict'
238
209
 
239
210
    hidden = False
240
211
    
243
214
        if self.__doc__ == Command.__doc__:
244
215
            warn("No help message set for %r" % self)
245
216
 
246
 
 
 
217
    def options(self):
 
218
        """Return dict of valid options for this command.
 
219
 
 
220
        Maps from long option name to option object."""
 
221
        r = dict()
 
222
        r['help'] = Option.OPTIONS['help']
 
223
        for o in self.takes_options:
 
224
            if not isinstance(o, Option):
 
225
                o = Option.OPTIONS[o]
 
226
            r[o.name] = o
 
227
        return r
 
228
 
 
229
    def _setup_outf(self):
 
230
        """Return a file linked to stdout, which has proper encoding."""
 
231
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
232
 
 
233
        # Originally I was using self.stdout, but that looks
 
234
        # *way* too much like sys.stdout
 
235
        if self.encoding_type == 'exact':
 
236
            self.outf = sys.stdout
 
237
            return
 
238
 
 
239
        output_encoding = bzrlib.osutils.get_terminal_encoding()
 
240
 
 
241
        # use 'replace' so that we don't abort if trying to write out
 
242
        # in e.g. the default C locale.
 
243
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
244
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
245
        # it just returns the encoding of the wrapped file, which is completely
 
246
        # bogus. So set the attribute, so we can find the correct encoding later.
 
247
        self.outf.encoding = output_encoding
 
248
 
 
249
    @deprecated_method(zero_eight)
247
250
    def run_argv(self, argv):
248
 
        """Parse command line and run."""
249
 
        args, opts = parse_args(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)
250
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)
251
260
        if 'help' in opts:  # e.g. bzr add --help
252
261
            from bzrlib.help import help_on_command
253
262
            help_on_command(self.name())
254
263
            return 0
255
 
 
256
 
        # check options are reasonable
257
 
        allowed = self.takes_options
 
264
        # XXX: This should be handled by the parser
 
265
        allowed_names = self.options().keys()
258
266
        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
 
 
 
267
            if oname not in allowed_names:
 
268
                raise BzrOptionError("option '--%s' is not allowed for"
 
269
                                " command %r" % (oname, self.name()))
263
270
        # mix arguments and options into one dictionary
264
271
        cmdargs = _match_argform(self.name(), self.takes_args, args)
265
272
        cmdopts = {}
269
276
        all_cmd_args = cmdargs.copy()
270
277
        all_cmd_args.update(cmdopts)
271
278
 
 
279
        self._setup_outf()
 
280
 
272
281
        return self.run(**all_cmd_args)
273
 
 
274
282
    
275
283
    def run(self):
276
284
        """Actually run the command.
282
290
        shell error code if not.  It's OK for this method to allow
283
291
        an exception to raise up.
284
292
        """
285
 
        raise NotImplementedError()
286
 
 
 
293
        raise NotImplementedError('no implementation of command %r' 
 
294
                                  % self.name())
287
295
 
288
296
    def help(self):
289
297
        """Return help message for this class."""
 
298
        from inspect import getdoc
290
299
        if self.__doc__ is Command.__doc__:
291
300
            return None
292
301
        return getdoc(self)
294
303
    def name(self):
295
304
        return _unsquish_command_name(self.__class__.__name__)
296
305
 
 
306
    def plugin_name(self):
 
307
        """Get the name of the plugin that provides this command.
 
308
 
 
309
        :return: The name of the plugin or None if the command is builtin.
 
310
        """
 
311
        mod_parts = self.__module__.split('.')
 
312
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
313
            return mod_parts[2]
 
314
        else:
 
315
            return None
 
316
 
297
317
 
298
318
def parse_spec(spec):
299
319
    """
326
346
        parsed = [spec, None]
327
347
    return parsed
328
348
 
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):
 
349
def parse_args(command, argv, alias_argv=None):
372
350
    """Parse command line.
373
351
    
374
352
    Arguments and options are parsed at this level before being passed
375
353
    down to specific command handlers.  This routine knows, from a
376
354
    lookup table, something about the available options, what optargs
377
355
    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
356
    """
 
357
    # TODO: chop up this beast; make it a method of the Command
398
358
    args = []
399
359
    opts = {}
 
360
    alias_opts = {}
400
361
 
 
362
    cmd_options = command.options()
401
363
    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]
 
364
    proc_aliasarg = True # Are we processing alias_argv now?
 
365
    for proc_argv in alias_argv, argv:
 
366
        while proc_argv:
 
367
            a = proc_argv.pop(0)
 
368
            if argsover:
 
369
                args.append(a)
 
370
                continue
 
371
            elif a == '--':
 
372
                # We've received a standalone -- No more flags
 
373
                argsover = True
 
374
                continue
 
375
            if a[0] == '-':
 
376
                # option names must not be unicode
 
377
                a = str(a)
 
378
                optarg = None
 
379
                if a[1] == '-':
 
380
                    mutter("  got option %r", a)
 
381
                    if '=' in a:
 
382
                        optname, optarg = a[2:].split('=', 1)
 
383
                    else:
 
384
                        optname = a[2:]
 
385
                    if optname not in cmd_options:
 
386
                        raise BzrCommandError('unknown option "%s"' % a)
 
387
                else:
 
388
                    shortopt = a[1:]
 
389
                    if shortopt in Option.SHORT_OPTIONS:
 
390
                        # Multi-character options must have a space to delimit
 
391
                        # their value
 
392
                        # ^^^ what does this mean? mbp 20051014
 
393
                        optname = Option.SHORT_OPTIONS[shortopt].name
 
394
                    else:
 
395
                        # Single character short options, can be chained,
 
396
                        # and have their value appended to their name
 
397
                        shortopt = a[1:2]
 
398
                        if shortopt not in Option.SHORT_OPTIONS:
 
399
                            # We didn't find the multi-character name, and we
 
400
                            # didn't find the single char name
 
401
                            raise BzrCommandError('unknown option "%s"' % a)
 
402
                        optname = Option.SHORT_OPTIONS[shortopt].name
435
403
 
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:])
 
404
                        if a[2:]:
 
405
                            # There are extra things on this option
 
406
                            # see if it is the value, or if it is another
 
407
                            # short option
 
408
                            optargfn = Option.OPTIONS[optname].type
 
409
                            if optargfn is None:
 
410
                                # This option does not take an argument, so the
 
411
                                # next entry is another short option, pack it
 
412
                                # back into the list
 
413
                                proc_argv.insert(0, '-' + a[2:])
 
414
                            else:
 
415
                                # This option takes an argument, so pack it
 
416
                                # into the array
 
417
                                optarg = a[2:]
 
418
                    if optname not in cmd_options:
 
419
                        raise BzrCommandError('unknown option "%s"' % shortopt)
 
420
                if optname in opts:
 
421
                    # XXX: Do we ever want to support this, e.g. for -r?
 
422
                    if proc_aliasarg:
 
423
                        raise BzrCommandError('repeated option %r' % a)
 
424
                    elif optname in alias_opts:
 
425
                        # Replace what's in the alias with what's in the real
 
426
                        # argument
 
427
                        del alias_opts[optname]
 
428
                        del opts[optname]
 
429
                        proc_argv.insert(0, a)
 
430
                        continue
 
431
                    else:
 
432
                        raise BzrCommandError('repeated option %r' % a)
 
433
                    
 
434
                option_obj = cmd_options[optname]
 
435
                optargfn = option_obj.type
 
436
                if optargfn:
 
437
                    if optarg == None:
 
438
                        if not proc_argv:
 
439
                            raise BzrCommandError('option %r needs an argument' % a)
446
440
                        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)
 
441
                            optarg = proc_argv.pop(0)
 
442
                    opts[optname] = optargfn(optarg)
 
443
                    if proc_aliasarg:
 
444
                        alias_opts[optname] = optargfn(optarg)
 
445
                else:
 
446
                    if optarg != None:
 
447
                        raise BzrCommandError('option %r takes no argument' % optname)
 
448
                    opts[optname] = True
 
449
                    if proc_aliasarg:
 
450
                        alias_opts[optname] = True
463
451
            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
 
 
 
452
                args.append(a)
 
453
        proc_aliasarg = False # Done with alias argv
470
454
    return args, opts
471
455
 
472
456
 
473
 
 
474
 
 
475
457
def _match_argform(cmd, takes_args, args):
476
458
    argdict = {}
477
459
 
499
481
                raise BzrCommandError("command %r needs one or more %s"
500
482
                        % (cmd, argname.upper()))
501
483
            argdict[argname + '_list'] = args[:-1]
502
 
            args[:-1] = []                
 
484
            args[:-1] = []
503
485
        else:
504
486
            # just a plain arg
505
487
            argname = ap
540
522
        os.remove(pfname)
541
523
 
542
524
 
 
525
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
526
    from bzrlib.lsprof import profile
 
527
    import cPickle
 
528
    ret, stats = profile(the_callable, *args, **kwargs)
 
529
    stats.sort()
 
530
    if filename is None:
 
531
        stats.pprint()
 
532
    else:
 
533
        stats.freeze()
 
534
        cPickle.dump(stats, open(filename, 'w'), 2)
 
535
        print 'Profile data written to %r.' % filename
 
536
    return ret
 
537
 
 
538
 
 
539
def get_alias(cmd):
 
540
    """Return an expanded alias, or None if no alias exists"""
 
541
    import bzrlib.config
 
542
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
543
    if (alias):
 
544
        return alias.split(' ')
 
545
    return None
 
546
 
 
547
 
543
548
def run_bzr(argv):
544
549
    """Execute a command.
545
550
 
548
553
    
549
554
    argv
550
555
       The command-line arguments, without the program name from argv[0]
 
556
       These should already be decoded. All library/test code calling
 
557
       run_bzr should be passing valid strings (don't need decoding).
551
558
    
552
559
    Returns a command status or raises an exception.
553
560
 
557
564
    --no-plugins
558
565
        Do not load plugin modules at all
559
566
 
 
567
    --no-aliases
 
568
        Do not allow aliases
 
569
 
560
570
    --builtin
561
571
        Only use builtin commands.  (Plugins are still allowed to change
562
572
        other behaviour.)
563
573
 
564
574
    --profile
565
 
        Run under the Python profiler.
 
575
        Run under the Python hotshot profiler.
 
576
 
 
577
    --lsprof
 
578
        Run under the Python lsprof profiler.
566
579
    """
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]
 
580
    argv = list(argv)
571
581
 
572
 
    opt_profile = opt_no_plugins = opt_builtin = False
 
582
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
583
                opt_no_aliases = False
 
584
    opt_lsprof_file = None
573
585
 
574
586
    # --no-plugins is handled specially at a very early stage. We need
575
587
    # to load plugins before doing other command parsing so that they
576
588
    # can override commands, but this needs to happen first.
577
589
 
578
 
    for a in argv:
 
590
    argv_copy = []
 
591
    i = 0
 
592
    while i < len(argv):
 
593
        a = argv[i]
579
594
        if a == '--profile':
580
595
            opt_profile = True
 
596
        elif a == '--lsprof':
 
597
            opt_lsprof = True
 
598
        elif a == '--lsprof-file':
 
599
            opt_lsprof_file = argv[i + 1]
 
600
            i += 1
581
601
        elif a == '--no-plugins':
582
602
            opt_no_plugins = True
 
603
        elif a == '--no-aliases':
 
604
            opt_no_aliases = True
583
605
        elif a == '--builtin':
584
606
            opt_builtin = True
 
607
        elif a in ('--quiet', '-q'):
 
608
            be_quiet()
585
609
        else:
586
 
            break
587
 
        argv.remove(a)
 
610
            argv_copy.append(a)
 
611
        i += 1
588
612
 
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()
 
613
    argv = argv_copy
 
614
    if (not argv):
 
615
        from bzrlib.builtins import cmd_help
 
616
        cmd_help().run_argv_aliases([])
595
617
        return 0
596
618
 
597
619
    if argv[0] == '--version':
602
624
    if not opt_no_plugins:
603
625
        from bzrlib.plugin import load_plugins
604
626
        load_plugins()
 
627
    else:
 
628
        from bzrlib.plugin import disable_plugins
 
629
        disable_plugins()
 
630
 
 
631
    alias_argv = None
 
632
 
 
633
    if not opt_no_aliases:
 
634
        alias_argv = get_alias(argv[0])
 
635
        if alias_argv:
 
636
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
637
            argv[0] = alias_argv.pop(0)
605
638
 
606
639
    cmd = str(argv.pop(0))
607
640
 
608
641
    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)
 
642
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
643
        run = cmd_obj.run_argv
 
644
        run_argv = [argv]
612
645
    else:
613
 
        ret = cmd_obj.run_argv(argv)
614
 
    return ret or 0
 
646
        run = cmd_obj.run_argv_aliases
 
647
        run_argv = [argv, alias_argv]
 
648
 
 
649
    try:
 
650
        if opt_lsprof:
 
651
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
652
        elif opt_profile:
 
653
            ret = apply_profiled(run, *run_argv)
 
654
        else:
 
655
            ret = run(*run_argv)
 
656
        return ret or 0
 
657
    finally:
 
658
        # reset, in case we may do other commands later within the same process
 
659
        be_quiet(False)
 
660
 
 
661
def display_command(func):
 
662
    """Decorator that suppresses pipe/interrupt errors."""
 
663
    def ignore_pipe(*args, **kwargs):
 
664
        try:
 
665
            result = func(*args, **kwargs)
 
666
            sys.stdout.flush()
 
667
            return result
 
668
        except IOError, e:
 
669
            if not hasattr(e, 'errno'):
 
670
                raise
 
671
            if e.errno != errno.EPIPE:
 
672
                raise
 
673
            pass
 
674
        except KeyboardInterrupt:
 
675
            pass
 
676
    return ignore_pipe
615
677
 
616
678
 
617
679
def main(argv):
618
680
    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:])
 
681
    from bzrlib.ui.text import TextUIFactory
 
682
    bzrlib.ui.ui_factory = TextUIFactory()
 
683
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
684
    ret = run_bzr_catch_errors(argv)
 
685
    mutter("return code %d", ret)
 
686
    return ret
623
687
 
624
688
 
625
689
def run_bzr_catch_errors(argv):
629
693
        finally:
630
694
            # do this here inside the exception wrappers to catch EPIPE
631
695
            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))
 
696
    except Exception, e:
 
697
        # used to handle AssertionError and KeyboardInterrupt
 
698
        # specially here, but hopefully they're handled ok by the logger now
 
699
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
700
        if os.environ.get('BZR_PDB'):
 
701
            print '**** entering debugger'
 
702
            import pdb
 
703
            pdb.post_mortem(sys.exc_traceback)
641
704
        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
705
 
658
706
if __name__ == '__main__':
659
707
    sys.exit(main(sys.argv))