~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Ian Clatworthy
  • Date: 2008-07-17 01:08:21 UTC
  • mto: This revision was merged to the branch mainline in revision 3546.
  • Revision ID: ian.clatworthy@canonical.com-20080717010821-qg05ah7iobvl8hin
workaround docutils dot-in-option name bug (Mark Hammond)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
 
1
# Copyright (C) 2006 Canonical Ltd
2
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
28
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
29
29
# the profile output behind so it can be interactively examined?
30
30
 
 
31
import os
31
32
import sys
32
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
33
38
from warnings import warn
34
 
import errno
35
 
import codecs
36
39
 
37
40
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.errors import (BzrError,
40
 
                           BzrCommandError,
41
 
                           BzrCheckError,
42
 
                           NotBranchError)
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    registry,
 
47
    trace,
 
48
    win32utils,
 
49
    )
 
50
""")
 
51
 
 
52
from bzrlib.symbol_versioning import (
 
53
    deprecated_function,
 
54
    deprecated_method,
 
55
    )
 
56
# Compatibility
43
57
from bzrlib.option import Option
44
 
import bzrlib.osutils
45
 
from bzrlib.revisionspec import RevisionSpec
46
 
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
47
 
import bzrlib.trace
48
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
58
 
49
59
 
50
60
plugin_cmds = {}
51
61
 
64
74
        k_unsquished = _unsquish_command_name(k)
65
75
    else:
66
76
        k_unsquished = k
67
 
    if not plugin_cmds.has_key(k_unsquished):
 
77
    if k_unsquished not in plugin_cmds:
68
78
        plugin_cmds[k_unsquished] = cmd
69
 
        mutter('registered plugin command %s', k_unsquished)
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
70
80
        if decorate and k_unsquished in builtin_command_names():
71
81
            return _builtin_commands()[k_unsquished]
72
82
    elif decorate:
74
84
        plugin_cmds[k_unsquished] = cmd
75
85
        return result
76
86
    else:
77
 
        log_error('Two plugins defined the same command: %r' % k)
78
 
        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__])
 
89
        trace.log_error('Previously this command was registered from %r' %
 
90
                        sys.modules[plugin_cmds[k_unsquished].__module__])
79
91
 
80
92
 
81
93
def _squish_command_name(cmd):
83
95
 
84
96
 
85
97
def _unsquish_command_name(cmd):
86
 
    assert cmd.startswith("cmd_")
87
98
    return cmd[4:].replace('_','-')
88
99
 
89
100
 
127
138
    plugins_override
128
139
        If true, plugin commands can override builtins.
129
140
    """
 
141
    try:
 
142
        return _get_cmd_object(cmd_name, plugins_override)
 
143
    except KeyError:
 
144
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
145
 
 
146
 
 
147
def _get_cmd_object(cmd_name, plugins_override=True):
 
148
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
130
149
    from bzrlib.externalcommand import ExternalCommand
131
150
 
132
 
    cmd_name = str(cmd_name)            # not unicode
 
151
    # We want only 'ascii' command names, but the user may have typed
 
152
    # in a Unicode name. In that case, they should just get a
 
153
    # 'command not found' error later.
 
154
    # In the future, we may actually support Unicode command names.
133
155
 
134
156
    # first look up this command under the specified name
135
157
    cmds = _get_cmd_dict(plugins_override=plugins_override)
147
169
    if cmd_obj:
148
170
        return cmd_obj
149
171
 
150
 
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
172
    # look for plugins that provide this command but aren't installed
 
173
    for provider in command_providers_registry:
 
174
        try:
 
175
            plugin_metadata = provider.plugin_for_command(cmd_name)
 
176
        except errors.NoPluginAvailable:
 
177
            pass
 
178
        else:
 
179
            raise errors.CommandAvailableInPlugin(cmd_name, 
 
180
                                                  plugin_metadata, provider)
 
181
 
 
182
    raise KeyError
151
183
 
152
184
 
153
185
class Command(object):
201
233
            replace - put in a bogus character (typically '?')
202
234
            exact - do not encode sys.stdout
203
235
 
 
236
            NOTE: by default on Windows, sys.stdout is opened as a text
 
237
            stream, therefore LF line-endings are converted to CRLF.
 
238
            When a command uses encoding_type = 'exact', then
 
239
            sys.stdout is forced to be a binary stream, and line-endings
 
240
            will not mangled.
 
241
 
204
242
    """
205
243
    aliases = []
206
244
    takes_args = []
213
251
        """Construct an instance of this command."""
214
252
        if self.__doc__ == Command.__doc__:
215
253
            warn("No help message set for %r" % self)
 
254
        # List of standard options directly supported
 
255
        self.supported_std_options = []
 
256
 
 
257
    def _maybe_expand_globs(self, file_list):
 
258
        """Glob expand file_list if the platform does not do that itself.
 
259
        
 
260
        :return: A possibly empty list of unicode paths.
 
261
 
 
262
        Introduced in bzrlib 0.18.
 
263
        """
 
264
        if not file_list:
 
265
            file_list = []
 
266
        if sys.platform == 'win32':
 
267
            file_list = win32utils.glob_expand(file_list)
 
268
        return list(file_list)
 
269
 
 
270
    def _usage(self):
 
271
        """Return single-line grammar for this command.
 
272
 
 
273
        Only describes arguments, not options.
 
274
        """
 
275
        s = 'bzr ' + self.name() + ' '
 
276
        for aname in self.takes_args:
 
277
            aname = aname.upper()
 
278
            if aname[-1] in ['$', '+']:
 
279
                aname = aname[:-1] + '...'
 
280
            elif aname[-1] == '?':
 
281
                aname = '[' + aname[:-1] + ']'
 
282
            elif aname[-1] == '*':
 
283
                aname = '[' + aname[:-1] + '...]'
 
284
            s += aname + ' '
 
285
        s = s[:-1]      # remove last space
 
286
        return s
 
287
 
 
288
    def get_help_text(self, additional_see_also=None, plain=True,
 
289
                      see_also_as_links=False):
 
290
        """Return a text string with help for this command.
 
291
        
 
292
        :param additional_see_also: Additional help topics to be
 
293
            cross-referenced.
 
294
        :param plain: if False, raw help (reStructuredText) is
 
295
            returned instead of plain text.
 
296
        :param see_also_as_links: if True, convert items in 'See also'
 
297
            list to internal links (used by bzr_man rstx generator)
 
298
        """
 
299
        doc = self.help()
 
300
        if doc is None:
 
301
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
 
302
 
 
303
        # Extract the summary (purpose) and sections out from the text
 
304
        purpose,sections = self._get_help_parts(doc)
 
305
 
 
306
        # If a custom usage section was provided, use it
 
307
        if sections.has_key('Usage'):
 
308
            usage = sections.pop('Usage')
 
309
        else:
 
310
            usage = self._usage()
 
311
 
 
312
        # The header is the purpose and usage
 
313
        result = ""
 
314
        result += ':Purpose: %s\n' % purpose
 
315
        if usage.find('\n') >= 0:
 
316
            result += ':Usage:\n%s\n' % usage
 
317
        else:
 
318
            result += ':Usage:   %s\n' % usage
 
319
        result += '\n'
 
320
 
 
321
        # Add the options
 
322
        options = option.get_optparser(self.options()).format_option_help()
 
323
        if options.startswith('Options:'):
 
324
            result += ':' + options
 
325
        elif options.startswith('options:'):
 
326
            # Python 2.4 version of optparse
 
327
            result += ':Options:' + options[len('options:'):]
 
328
        else:
 
329
            result += options
 
330
        result += '\n'
 
331
 
 
332
        # Add the description, indenting it 2 spaces
 
333
        # to match the indentation of the options
 
334
        if sections.has_key(None):
 
335
            text = sections.pop(None)
 
336
            text = '\n  '.join(text.splitlines())
 
337
            result += ':%s:\n  %s\n\n' % ('Description',text)
 
338
 
 
339
        # Add the custom sections (e.g. Examples). Note that there's no need
 
340
        # to indent these as they must be indented already in the source.
 
341
        if sections:
 
342
            labels = sorted(sections.keys())
 
343
            for label in labels:
 
344
                result += ':%s:\n%s\n\n' % (label,sections[label])
 
345
 
 
346
        # Add the aliases, source (plug-in) and see also links, if any
 
347
        if self.aliases:
 
348
            result += ':Aliases:  '
 
349
            result += ', '.join(self.aliases) + '\n'
 
350
        plugin_name = self.plugin_name()
 
351
        if plugin_name is not None:
 
352
            result += ':From:     plugin "%s"\n' % plugin_name
 
353
        see_also = self.get_see_also(additional_see_also)
 
354
        if see_also:
 
355
            if not plain and see_also_as_links:
 
356
                see_also_links = []
 
357
                for item in see_also:
 
358
                    if item == 'topics':
 
359
                        # topics doesn't have an independent section
 
360
                        # so don't create a real link
 
361
                        see_also_links.append(item)
 
362
                    else:
 
363
                        # Use a reST link for this entry
 
364
                        see_also_links.append("`%s`_" % (item,))
 
365
                see_also = see_also_links
 
366
            result += ':See also: '
 
367
            result += ', '.join(see_also) + '\n'
 
368
 
 
369
        # If this will be rendered as plan text, convert it
 
370
        if plain:
 
371
            import bzrlib.help_topics
 
372
            result = bzrlib.help_topics.help_as_plain_text(result)
 
373
        return result
 
374
 
 
375
    @staticmethod
 
376
    def _get_help_parts(text):
 
377
        """Split help text into a summary and named sections.
 
378
 
 
379
        :return: (summary,sections) where summary is the top line and
 
380
            sections is a dictionary of the rest indexed by section name.
 
381
            A section starts with a heading line of the form ":xxx:".
 
382
            Indented text on following lines is the section value.
 
383
            All text found outside a named section is assigned to the
 
384
            default section which is given the key of None.
 
385
        """
 
386
        def save_section(sections, label, section):
 
387
            if len(section) > 0:
 
388
                if sections.has_key(label):
 
389
                    sections[label] += '\n' + section
 
390
                else:
 
391
                    sections[label] = section
 
392
            
 
393
        lines = text.rstrip().splitlines()
 
394
        summary = lines.pop(0)
 
395
        sections = {}
 
396
        label,section = None,''
 
397
        for line in lines:
 
398
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
399
                save_section(sections, label, section)
 
400
                label,section = line[1:-1],''
 
401
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
 
402
                save_section(sections, label, section)
 
403
                label,section = None,line
 
404
            else:
 
405
                if len(section) > 0:
 
406
                    section += '\n' + line
 
407
                else:
 
408
                    section = line
 
409
        save_section(sections, label, section)
 
410
        return summary, sections
 
411
 
 
412
    def get_help_topic(self):
 
413
        """Return the commands help topic - its name."""
 
414
        return self.name()
 
415
 
 
416
    def get_see_also(self, additional_terms=None):
 
417
        """Return a list of help topics that are related to this command.
 
418
        
 
419
        The list is derived from the content of the _see_also attribute. Any
 
420
        duplicates are removed and the result is in lexical order.
 
421
        :param additional_terms: Additional help topics to cross-reference.
 
422
        :return: A list of help topics.
 
423
        """
 
424
        see_also = set(getattr(self, '_see_also', []))
 
425
        if additional_terms:
 
426
            see_also.update(additional_terms)
 
427
        return sorted(see_also)
216
428
 
217
429
    def options(self):
218
430
        """Return dict of valid options for this command.
219
431
 
220
432
        Maps from long option name to option object."""
221
 
        r = dict()
222
 
        r['help'] = Option.OPTIONS['help']
 
433
        r = Option.STD_OPTIONS.copy()
 
434
        std_names = r.keys()
223
435
        for o in self.takes_options:
224
 
            if not isinstance(o, Option):
225
 
                o = Option.OPTIONS[o]
 
436
            if isinstance(o, basestring):
 
437
                o = option.Option.OPTIONS[o]
226
438
            r[o.name] = o
 
439
            if o.name in std_names:
 
440
                self.supported_std_options.append(o.name)
227
441
        return r
228
442
 
229
443
    def _setup_outf(self):
230
444
        """Return a file linked to stdout, which has proper encoding."""
231
 
        assert self.encoding_type in ['strict', 'exact', 'replace']
232
 
 
233
445
        # Originally I was using self.stdout, but that looks
234
446
        # *way* too much like sys.stdout
235
447
        if self.encoding_type == 'exact':
 
448
            # force sys.stdout to be binary stream on win32
 
449
            if sys.platform == 'win32':
 
450
                fileno = getattr(sys.stdout, 'fileno', None)
 
451
                if fileno:
 
452
                    import msvcrt
 
453
                    msvcrt.setmode(fileno(), os.O_BINARY)
236
454
            self.outf = sys.stdout
237
455
            return
238
456
 
239
 
        output_encoding = bzrlib.osutils.get_terminal_encoding()
 
457
        output_encoding = osutils.get_terminal_encoding()
240
458
 
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)
 
459
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
460
                        errors=self.encoding_type)
244
461
        # For whatever reason codecs.getwriter() does not advertise its encoding
245
462
        # it just returns the encoding of the wrapped file, which is completely
246
463
        # bogus. So set the attribute, so we can find the correct encoding later.
247
464
        self.outf.encoding = output_encoding
248
465
 
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
466
    def run_argv_aliases(self, argv, alias_argv=None):
258
467
        """Parse the command line and run with extra aliases in alias_argv."""
 
468
        if argv is None:
 
469
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
470
                 DeprecationWarning, stacklevel=2)
 
471
            argv = []
259
472
        args, opts = parse_args(self, argv, alias_argv)
 
473
 
 
474
        # Process the standard options
260
475
        if 'help' in opts:  # e.g. bzr add --help
261
 
            from bzrlib.help import help_on_command
262
 
            help_on_command(self.name())
 
476
            sys.stdout.write(self.get_help_text())
263
477
            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 BzrOptionError("option '--%s' is not allowed for"
269
 
                                " command %r" % (oname, self.name()))
 
478
        trace.set_verbosity_level(option._verbosity_level)
 
479
        if 'verbose' in self.supported_std_options:
 
480
            opts['verbose'] = trace.is_verbose()
 
481
        elif opts.has_key('verbose'):
 
482
            del opts['verbose']
 
483
        if 'quiet' in self.supported_std_options:
 
484
            opts['quiet'] = trace.is_quiet()
 
485
        elif opts.has_key('quiet'):
 
486
            del opts['quiet']
 
487
 
270
488
        # mix arguments and options into one dictionary
271
489
        cmdargs = _match_argform(self.name(), self.takes_args, args)
272
490
        cmdopts = {}
290
508
        shell error code if not.  It's OK for this method to allow
291
509
        an exception to raise up.
292
510
        """
293
 
        raise NotImplementedError('no implementation of command %r' 
 
511
        raise NotImplementedError('no implementation of command %r'
294
512
                                  % self.name())
295
513
 
296
514
    def help(self):
315
533
            return None
316
534
 
317
535
 
318
 
def parse_spec(spec):
319
 
    """
320
 
    >>> parse_spec(None)
321
 
    [None, None]
322
 
    >>> parse_spec("./")
323
 
    ['./', None]
324
 
    >>> parse_spec("../@")
325
 
    ['..', -1]
326
 
    >>> parse_spec("../f/@35")
327
 
    ['../f', 35]
328
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
329
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
330
 
    """
331
 
    if spec is None:
332
 
        return [None, None]
333
 
    if '/@' in spec:
334
 
        parsed = spec.split('/@')
335
 
        assert len(parsed) == 2
336
 
        if parsed[1] == "":
337
 
            parsed[1] = -1
338
 
        else:
339
 
            try:
340
 
                parsed[1] = int(parsed[1])
341
 
            except ValueError:
342
 
                pass # We can allow stuff like ./@revid:blahblahblah
343
 
            else:
344
 
                assert parsed[1] >=0
345
 
    else:
346
 
        parsed = [spec, None]
347
 
    return parsed
348
 
 
349
536
def parse_args(command, argv, alias_argv=None):
350
537
    """Parse command line.
351
538
    
354
541
    lookup table, something about the available options, what optargs
355
542
    they take, and which commands will accept them.
356
543
    """
357
 
    # TODO: chop up this beast; make it a method of the Command
358
 
    args = []
359
 
    opts = {}
360
 
    alias_opts = {}
361
 
 
362
 
    cmd_options = command.options()
363
 
    argsover = False
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
 
                args.append(a)
373
 
                continue
374
 
            elif a == '--':
375
 
                # We've received a standalone -- No more flags
376
 
                argsover = True
377
 
                continue
378
 
            if a[0] == '-':
379
 
                # option names must not be unicode
380
 
                a = str(a)
381
 
                optarg = None
382
 
                if a[1] == '-':
383
 
                    mutter("  got option %r", a)
384
 
                    if '=' in a:
385
 
                        optname, optarg = a[2:].split('=', 1)
386
 
                    else:
387
 
                        optname = a[2:]
388
 
                    if optname not in cmd_options:
389
 
                        raise BzrCommandError('unknown option "%s"' % a)
390
 
                else:
391
 
                    shortopt = a[1:]
392
 
                    if shortopt in Option.SHORT_OPTIONS:
393
 
                        # Multi-character options must have a space to delimit
394
 
                        # their value
395
 
                        # ^^^ what does this mean? mbp 20051014
396
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
397
 
                    else:
398
 
                        # Single character short options, can be chained,
399
 
                        # and have their value appended to their name
400
 
                        shortopt = a[1:2]
401
 
                        if shortopt not in Option.SHORT_OPTIONS:
402
 
                            # We didn't find the multi-character name, and we
403
 
                            # didn't find the single char name
404
 
                            raise BzrCommandError('unknown option "%s"' % a)
405
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
406
 
 
407
 
                        if a[2:]:
408
 
                            # There are extra things on this option
409
 
                            # see if it is the value, or if it is another
410
 
                            # short option
411
 
                            optargfn = Option.OPTIONS[optname].type
412
 
                            if optargfn is None:
413
 
                                # This option does not take an argument, so the
414
 
                                # next entry is another short option, pack it
415
 
                                # back into the list
416
 
                                proc_argv.insert(0, '-' + a[2:])
417
 
                            else:
418
 
                                # This option takes an argument, so pack it
419
 
                                # into the array
420
 
                                optarg = a[2:]
421
 
                    if optname not in cmd_options:
422
 
                        raise BzrCommandError('unknown option "%s"' % shortopt)
423
 
                if optname in opts:
424
 
                    # XXX: Do we ever want to support this, e.g. for -r?
425
 
                    if proc_aliasarg:
426
 
                        raise BzrCommandError('repeated option %r' % a)
427
 
                    elif optname in alias_opts:
428
 
                        # Replace what's in the alias with what's in the real
429
 
                        # argument
430
 
                        del alias_opts[optname]
431
 
                        del opts[optname]
432
 
                        proc_argv.insert(0, a)
433
 
                        continue
434
 
                    else:
435
 
                        raise BzrCommandError('repeated option %r' % a)
436
 
                    
437
 
                option_obj = cmd_options[optname]
438
 
                optargfn = option_obj.type
439
 
                if optargfn:
440
 
                    if optarg == None:
441
 
                        if not proc_argv:
442
 
                            raise BzrCommandError('option %r needs an argument' % a)
443
 
                        else:
444
 
                            optarg = proc_argv.pop(0)
445
 
                    opts[optname] = optargfn(optarg)
446
 
                    if proc_aliasarg:
447
 
                        alias_opts[optname] = optargfn(optarg)
448
 
                else:
449
 
                    if optarg != None:
450
 
                        raise BzrCommandError('option %r takes no argument' % optname)
451
 
                    opts[optname] = True
452
 
                    if proc_aliasarg:
453
 
                        alias_opts[optname] = True
454
 
            else:
455
 
                args.append(a)
456
 
        proc_aliasarg = False # Done with alias argv
 
544
    # TODO: make it a method of the Command?
 
545
    parser = option.get_optparser(command.options())
 
546
    if alias_argv is not None:
 
547
        args = alias_argv + argv
 
548
    else:
 
549
        args = argv
 
550
 
 
551
    options, args = parser.parse_args(args)
 
552
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
553
                 v is not option.OptionParser.DEFAULT_VALUE])
457
554
    return args, opts
458
555
 
459
556
 
474
571
                argdict[argname + '_list'] = None
475
572
        elif ap[-1] == '+':
476
573
            if not args:
477
 
                raise BzrCommandError("command %r needs one or more %s"
478
 
                        % (cmd, argname.upper()))
 
574
                raise errors.BzrCommandError("command %r needs one or more %s"
 
575
                                             % (cmd, argname.upper()))
479
576
            else:
480
577
                argdict[argname + '_list'] = args[:]
481
578
                args = []
482
579
        elif ap[-1] == '$': # all but one
483
580
            if len(args) < 2:
484
 
                raise BzrCommandError("command %r needs one or more %s"
485
 
                        % (cmd, argname.upper()))
 
581
                raise errors.BzrCommandError("command %r needs one or more %s"
 
582
                                             % (cmd, argname.upper()))
486
583
            argdict[argname + '_list'] = args[:-1]
487
584
            args[:-1] = []
488
585
        else:
489
586
            # just a plain arg
490
587
            argname = ap
491
588
            if not args:
492
 
                raise BzrCommandError("command %r requires argument %s"
493
 
                        % (cmd, argname.upper()))
 
589
                raise errors.BzrCommandError("command %r requires argument %s"
 
590
                               % (cmd, argname.upper()))
494
591
            else:
495
592
                argdict[argname] = args.pop(0)
496
593
            
497
594
    if args:
498
 
        raise BzrCommandError("extra argument to command %s: %s"
499
 
                              % (cmd, args[0]))
 
595
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
596
                                     % (cmd, args[0]))
500
597
 
501
598
    return argdict
502
599
 
 
600
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
601
    # Cannot use "import trace", as that would import bzrlib.trace instead of
 
602
    # the standard library's trace.
 
603
    trace = __import__('trace')
 
604
 
 
605
    tracer = trace.Trace(count=1, trace=0)
 
606
    sys.settrace(tracer.globaltrace)
 
607
 
 
608
    ret = the_callable(*args, **kwargs)
 
609
 
 
610
    sys.settrace(None)
 
611
    results = tracer.results()
 
612
    results.write_results(show_missing=1, summary=False,
 
613
                          coverdir=dirname)
503
614
 
504
615
 
505
616
def apply_profiled(the_callable, *args, **kwargs):
527
638
 
528
639
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
529
640
    from bzrlib.lsprof import profile
530
 
    import cPickle
531
641
    ret, stats = profile(the_callable, *args, **kwargs)
532
642
    stats.sort()
533
643
    if filename is None:
534
644
        stats.pprint()
535
645
    else:
536
 
        stats.freeze()
537
 
        cPickle.dump(stats, open(filename, 'w'), 2)
538
 
        print 'Profile data written to %r.' % filename
 
646
        stats.save(filename)
 
647
        trace.note('Profile data written to "%s".', filename)
539
648
    return ret
540
649
 
541
650
 
542
 
def get_alias(cmd):
543
 
    """Return an expanded alias, or None if no alias exists"""
544
 
    import bzrlib.config
545
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
651
def shlex_split_unicode(unsplit):
 
652
    import shlex
 
653
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
 
654
 
 
655
 
 
656
def get_alias(cmd, config=None):
 
657
    """Return an expanded alias, or None if no alias exists.
 
658
 
 
659
    cmd
 
660
        Command to be checked for an alias.
 
661
    config
 
662
        Used to specify an alternative config to use,
 
663
        which is especially useful for testing.
 
664
        If it is unspecified, the global config will be used.
 
665
    """
 
666
    if config is None:
 
667
        import bzrlib.config
 
668
        config = bzrlib.config.GlobalConfig()
 
669
    alias = config.get_alias(cmd)
546
670
    if (alias):
547
 
        return alias.split(' ')
 
671
        return shlex_split_unicode(alias)
548
672
    return None
549
673
 
550
674
 
579
703
 
580
704
    --lsprof
581
705
        Run under the Python lsprof profiler.
 
706
 
 
707
    --coverage
 
708
        Generate line coverage report in the specified directory.
582
709
    """
583
710
    argv = list(argv)
 
711
    trace.mutter("bzr arguments: %r", argv)
584
712
 
585
713
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
586
714
                opt_no_aliases = False
587
 
    opt_lsprof_file = None
 
715
    opt_lsprof_file = opt_coverage_dir = None
588
716
 
589
717
    # --no-plugins is handled specially at a very early stage. We need
590
718
    # to load plugins before doing other command parsing so that they
608
736
            opt_no_aliases = True
609
737
        elif a == '--builtin':
610
738
            opt_builtin = True
611
 
        elif a in ('--quiet', '-q'):
612
 
            be_quiet()
 
739
        elif a == '--coverage':
 
740
            opt_coverage_dir = argv[i + 1]
 
741
            i += 1
 
742
        elif a.startswith('-D'):
 
743
            debug.debug_flags.add(a[2:])
613
744
        else:
614
745
            argv_copy.append(a)
615
746
        i += 1
621
752
        return 0
622
753
 
623
754
    if argv[0] == '--version':
624
 
        from bzrlib.builtins import show_version
625
 
        show_version()
 
755
        from bzrlib.builtins import cmd_version
 
756
        cmd_version().run_argv_aliases([])
626
757
        return 0
627
758
        
628
759
    if not opt_no_plugins:
640
771
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
641
772
            argv[0] = alias_argv.pop(0)
642
773
 
643
 
    cmd = str(argv.pop(0))
 
774
    cmd = argv.pop(0)
 
775
    # We want only 'ascii' command names, but the user may have typed
 
776
    # in a Unicode name. In that case, they should just get a
 
777
    # 'command not found' error later.
644
778
 
645
779
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
646
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
647
 
        run = cmd_obj.run_argv
648
 
        run_argv = [argv]
649
 
    else:
650
 
        run = cmd_obj.run_argv_aliases
651
 
        run_argv = [argv, alias_argv]
 
780
    run = cmd_obj.run_argv_aliases
 
781
    run_argv = [argv, alias_argv]
652
782
 
653
783
    try:
654
784
        if opt_lsprof:
 
785
            if opt_coverage_dir:
 
786
                trace.warning(
 
787
                    '--coverage ignored, because --lsprof is in use.')
655
788
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
656
789
        elif opt_profile:
 
790
            if opt_coverage_dir:
 
791
                trace.warning(
 
792
                    '--coverage ignored, because --profile is in use.')
657
793
            ret = apply_profiled(run, *run_argv)
 
794
        elif opt_coverage_dir:
 
795
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
658
796
        else:
659
797
            ret = run(*run_argv)
 
798
        if 'memory' in debug.debug_flags:
 
799
            try:
 
800
                status_file = file('/proc/%s/status' % os.getpid(), 'rb')
 
801
            except IOError:
 
802
                pass
 
803
            else:
 
804
                status = status_file.read()
 
805
                status_file.close()
 
806
                trace.note("Process status after command:")
 
807
                for line in status.splitlines():
 
808
                    trace.note(line)
660
809
        return ret or 0
661
810
    finally:
662
811
        # reset, in case we may do other commands later within the same process
663
 
        be_quiet(False)
 
812
        option._verbosity_level = 0
664
813
 
665
814
def display_command(func):
666
815
    """Decorator that suppresses pipe/interrupt errors."""
670
819
            sys.stdout.flush()
671
820
            return result
672
821
        except IOError, e:
673
 
            if not hasattr(e, 'errno'):
 
822
            if getattr(e, 'errno', None) is None:
674
823
                raise
675
824
            if e.errno != errno.EPIPE:
676
825
                # Win32 raises IOError with errno=0 on a broken pipe
677
 
                if sys.platform != 'win32' or e.errno != 0:
 
826
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
678
827
                    raise
679
828
            pass
680
829
        except KeyboardInterrupt:
686
835
    import bzrlib.ui
687
836
    from bzrlib.ui.text import TextUIFactory
688
837
    bzrlib.ui.ui_factory = TextUIFactory()
689
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
838
     
 
839
    # Is this a final release version? If so, we should suppress warnings
 
840
    if bzrlib.version_info[3] == 'final':
 
841
        from bzrlib import symbol_versioning
 
842
        symbol_versioning.suppress_deprecation_warnings(override=False)
 
843
    try:
 
844
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
845
    except UnicodeDecodeError:
 
846
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
847
                                                            "encoding." % a))
690
848
    ret = run_bzr_catch_errors(argv)
691
 
    mutter("return code %d", ret)
 
849
    trace.mutter("return code %d", ret)
692
850
    return ret
693
851
 
694
852
 
695
853
def run_bzr_catch_errors(argv):
 
854
    # Note: The except clause logic below should be kept in sync with the
 
855
    # profile() routine in lsprof.py.
696
856
    try:
697
857
        return run_bzr(argv)
698
 
        # do this here inside the exception wrappers to catch EPIPE
699
 
        sys.stdout.flush()
700
 
    except Exception, e:
 
858
    except (KeyboardInterrupt, Exception), e:
701
859
        # used to handle AssertionError and KeyboardInterrupt
702
860
        # specially here, but hopefully they're handled ok by the logger now
703
 
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
861
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
704
862
        if os.environ.get('BZR_PDB'):
705
863
            print '**** entering debugger'
706
864
            import pdb
707
865
            pdb.post_mortem(sys.exc_traceback)
708
 
        return 3
 
866
        return exitcode
 
867
 
 
868
 
 
869
def run_bzr_catch_user_errors(argv):
 
870
    """Run bzr and report user errors, but let internal errors propagate.
 
871
 
 
872
    This is used for the test suite, and might be useful for other programs
 
873
    that want to wrap the commandline interface.
 
874
    """
 
875
    try:
 
876
        return run_bzr(argv)
 
877
    except Exception, e:
 
878
        if (isinstance(e, (OSError, IOError))
 
879
            or not getattr(e, 'internal_error', True)):
 
880
            trace.report_exception(sys.exc_info(), sys.stderr)
 
881
            return 3
 
882
        else:
 
883
            raise
 
884
 
 
885
 
 
886
class HelpCommandIndex(object):
 
887
    """A index for bzr help that returns commands."""
 
888
 
 
889
    def __init__(self):
 
890
        self.prefix = 'commands/'
 
891
 
 
892
    def get_topics(self, topic):
 
893
        """Search for topic amongst commands.
 
894
 
 
895
        :param topic: A topic to search for.
 
896
        :return: A list which is either empty or contains a single
 
897
            Command entry.
 
898
        """
 
899
        if topic and topic.startswith(self.prefix):
 
900
            topic = topic[len(self.prefix):]
 
901
        try:
 
902
            cmd = _get_cmd_object(topic)
 
903
        except KeyError:
 
904
            return []
 
905
        else:
 
906
            return [cmd]
 
907
 
 
908
 
 
909
class Provider(object):
 
910
    '''Generic class to be overriden by plugins'''
 
911
 
 
912
    def plugin_for_command(self, cmd_name):
 
913
        '''Takes a command and returns the information for that plugin
 
914
        
 
915
        :return: A dictionary with all the available information 
 
916
        for the requested plugin
 
917
        '''
 
918
        raise NotImplementedError
 
919
 
 
920
 
 
921
class ProvidersRegistry(registry.Registry):
 
922
    '''This registry exists to allow other providers to exist'''
 
923
 
 
924
    def __iter__(self):
 
925
        for key, provider in self.iteritems():
 
926
            yield provider
 
927
 
 
928
command_providers_registry = ProvidersRegistry()
 
929
 
709
930
 
710
931
if __name__ == '__main__':
711
932
    sys.exit(main(sys.argv))