/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2008-08-20 02:07:36 UTC
  • mfrom: (3640 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3682.
  • Revision ID: robertc@robertcollins.net-20080820020736-g2xe4921zzxtymle
Merge bzr.dev

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
 
from bzrlib.errors import (BzrError,
39
 
                           BzrCheckError,
40
 
                           BzrCommandError,
41
 
                           BzrOptionError,
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
 
from bzrlib.revisionspec import RevisionSpec
45
 
from bzrlib.symbol_versioning import *
46
 
import bzrlib.trace
47
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
58
 
48
59
 
49
60
plugin_cmds = {}
50
61
 
63
74
        k_unsquished = _unsquish_command_name(k)
64
75
    else:
65
76
        k_unsquished = k
66
 
    if not plugin_cmds.has_key(k_unsquished):
 
77
    if k_unsquished not in plugin_cmds:
67
78
        plugin_cmds[k_unsquished] = cmd
68
 
        mutter('registered plugin command %s', k_unsquished)
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
69
80
        if decorate and k_unsquished in builtin_command_names():
70
81
            return _builtin_commands()[k_unsquished]
71
82
    elif decorate:
73
84
        plugin_cmds[k_unsquished] = cmd
74
85
        return result
75
86
    else:
76
 
        log_error('Two plugins defined the same command: %r' % k)
77
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
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__])
78
91
 
79
92
 
80
93
def _squish_command_name(cmd):
82
95
 
83
96
 
84
97
def _unsquish_command_name(cmd):
85
 
    assert cmd.startswith("cmd_")
86
98
    return cmd[4:].replace('_','-')
87
99
 
88
100
 
126
138
    plugins_override
127
139
        If true, plugin commands can override builtins.
128
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."""
129
149
    from bzrlib.externalcommand import ExternalCommand
130
150
 
131
 
    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.
132
155
 
133
156
    # first look up this command under the specified name
134
157
    cmds = _get_cmd_dict(plugins_override=plugins_override)
146
169
    if cmd_obj:
147
170
        return cmd_obj
148
171
 
149
 
    raise BzrCommandError("unknown command %r" % 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
150
183
 
151
184
 
152
185
class Command(object):
200
233
            replace - put in a bogus character (typically '?')
201
234
            exact - do not encode sys.stdout
202
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
 
203
242
    """
204
243
    aliases = []
205
244
    takes_args = []
212
251
        """Construct an instance of this command."""
213
252
        if self.__doc__ == Command.__doc__:
214
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)
215
428
 
216
429
    def options(self):
217
430
        """Return dict of valid options for this command.
218
431
 
219
432
        Maps from long option name to option object."""
220
 
        r = dict()
221
 
        r['help'] = Option.OPTIONS['help']
 
433
        r = Option.STD_OPTIONS.copy()
 
434
        std_names = r.keys()
222
435
        for o in self.takes_options:
223
 
            if not isinstance(o, Option):
224
 
                o = Option.OPTIONS[o]
 
436
            if isinstance(o, basestring):
 
437
                o = option.Option.OPTIONS[o]
225
438
            r[o.name] = o
 
439
            if o.name in std_names:
 
440
                self.supported_std_options.append(o.name)
226
441
        return r
227
442
 
228
443
    def _setup_outf(self):
229
444
        """Return a file linked to stdout, which has proper encoding."""
230
 
        assert self.encoding_type in ['strict', 'exact', 'replace']
231
 
 
232
445
        # Originally I was using self.stdout, but that looks
233
446
        # *way* too much like sys.stdout
234
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)
235
454
            self.outf = sys.stdout
236
455
            return
237
456
 
238
 
        output_encoding = getattr(sys.stdout, 'encoding', None)
239
 
        if not output_encoding:
240
 
            output_encoding = bzrlib.user_encoding
241
 
            mutter('encoding stdout bzrlib.user_encoding %r', output_encoding)
242
 
        else:
243
 
            mutter('encoding stdout log as sys.stdout encoding %r', output_encoding)
244
 
 
245
 
        # use 'replace' so that we don't abort if trying to write out
246
 
        # in e.g. the default C locale.
247
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
248
 
 
249
 
    @deprecated_method(zero_eight)
250
 
    def run_argv(self, argv):
251
 
        """Parse command line and run.
252
 
        
253
 
        See run_argv_aliases for the 0.8 and beyond api.
254
 
        """
255
 
        return self.run_argv_aliases(argv)
 
457
        output_encoding = osutils.get_terminal_encoding()
 
458
 
 
459
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
460
                        errors=self.encoding_type)
 
461
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
462
        # it just returns the encoding of the wrapped file, which is completely
 
463
        # bogus. So set the attribute, so we can find the correct encoding later.
 
464
        self.outf.encoding = output_encoding
256
465
 
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 BzrCommandError("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):
303
521
    def name(self):
304
522
        return _unsquish_command_name(self.__class__.__name__)
305
523
 
 
524
    def plugin_name(self):
 
525
        """Get the name of the plugin that provides this command.
306
526
 
307
 
def parse_spec(spec):
308
 
    """
309
 
    >>> parse_spec(None)
310
 
    [None, None]
311
 
    >>> parse_spec("./")
312
 
    ['./', None]
313
 
    >>> parse_spec("../@")
314
 
    ['..', -1]
315
 
    >>> parse_spec("../f/@35")
316
 
    ['../f', 35]
317
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
318
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
319
 
    """
320
 
    if spec is None:
321
 
        return [None, None]
322
 
    if '/@' in spec:
323
 
        parsed = spec.split('/@')
324
 
        assert len(parsed) == 2
325
 
        if parsed[1] == "":
326
 
            parsed[1] = -1
 
527
        :return: The name of the plugin or None if the command is builtin.
 
528
        """
 
529
        mod_parts = self.__module__.split('.')
 
530
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
531
            return mod_parts[2]
327
532
        else:
328
 
            try:
329
 
                parsed[1] = int(parsed[1])
330
 
            except ValueError:
331
 
                pass # We can allow stuff like ./@revid:blahblahblah
332
 
            else:
333
 
                assert parsed[1] >=0
334
 
    else:
335
 
        parsed = [spec, None]
336
 
    return parsed
 
533
            return None
 
534
 
337
535
 
338
536
def parse_args(command, argv, alias_argv=None):
339
537
    """Parse command line.
343
541
    lookup table, something about the available options, what optargs
344
542
    they take, and which commands will accept them.
345
543
    """
346
 
    # TODO: chop up this beast; make it a method of the Command
347
 
    args = []
348
 
    opts = {}
349
 
    alias_opts = {}
350
 
 
351
 
    cmd_options = command.options()
352
 
    argsover = False
353
 
    proc_aliasarg = True # Are we processing alias_argv now?
354
 
    for proc_argv in alias_argv, argv:
355
 
        while proc_argv:
356
 
            a = proc_argv.pop(0)
357
 
            if argsover:
358
 
                args.append(a)
359
 
                continue
360
 
            elif a == '--':
361
 
                # We've received a standalone -- No more flags
362
 
                argsover = True
363
 
                continue
364
 
            if a[0] == '-':
365
 
                # option names must not be unicode
366
 
                a = str(a)
367
 
                optarg = None
368
 
                if a[1] == '-':
369
 
                    mutter("  got option %r", a)
370
 
                    if '=' in a:
371
 
                        optname, optarg = a[2:].split('=', 1)
372
 
                    else:
373
 
                        optname = a[2:]
374
 
                    if optname not in cmd_options:
375
 
                        raise BzrOptionError('unknown long option %r for'
376
 
                                             ' command %s' % 
377
 
                                             (a, command.name()))
378
 
                else:
379
 
                    shortopt = a[1:]
380
 
                    if shortopt in Option.SHORT_OPTIONS:
381
 
                        # Multi-character options must have a space to delimit
382
 
                        # their value
383
 
                        # ^^^ what does this mean? mbp 20051014
384
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
385
 
                    else:
386
 
                        # Single character short options, can be chained,
387
 
                        # and have their value appended to their name
388
 
                        shortopt = a[1:2]
389
 
                        if shortopt not in Option.SHORT_OPTIONS:
390
 
                            # We didn't find the multi-character name, and we
391
 
                            # didn't find the single char name
392
 
                            raise BzrError('unknown short option %r' % a)
393
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
394
 
 
395
 
                        if a[2:]:
396
 
                            # There are extra things on this option
397
 
                            # see if it is the value, or if it is another
398
 
                            # short option
399
 
                            optargfn = Option.OPTIONS[optname].type
400
 
                            if optargfn is None:
401
 
                                # This option does not take an argument, so the
402
 
                                # next entry is another short option, pack it
403
 
                                # back into the list
404
 
                                proc_argv.insert(0, '-' + a[2:])
405
 
                            else:
406
 
                                # This option takes an argument, so pack it
407
 
                                # into the array
408
 
                                optarg = a[2:]
409
 
                
410
 
                    if optname not in cmd_options:
411
 
                        raise BzrOptionError('unknown short option %r for'
412
 
                                             ' command %s' % 
413
 
                                             (shortopt, command.name()))
414
 
                if optname in opts:
415
 
                    # XXX: Do we ever want to support this, e.g. for -r?
416
 
                    if proc_aliasarg:
417
 
                        raise BzrError('repeated option %r' % a)
418
 
                    elif optname in alias_opts:
419
 
                        # Replace what's in the alias with what's in the real
420
 
                        # argument
421
 
                        del alias_opts[optname]
422
 
                        del opts[optname]
423
 
                        proc_argv.insert(0, a)
424
 
                        continue
425
 
                    else:
426
 
                        raise BzrError('repeated option %r' % a)
427
 
                    
428
 
                option_obj = cmd_options[optname]
429
 
                optargfn = option_obj.type
430
 
                if optargfn:
431
 
                    if optarg == None:
432
 
                        if not proc_argv:
433
 
                            raise BzrError('option %r needs an argument' % a)
434
 
                        else:
435
 
                            optarg = proc_argv.pop(0)
436
 
                    opts[optname] = optargfn(optarg)
437
 
                    if proc_aliasarg:
438
 
                        alias_opts[optname] = optargfn(optarg)
439
 
                else:
440
 
                    if optarg != None:
441
 
                        raise BzrError('option %r takes no argument' % optname)
442
 
                    opts[optname] = True
443
 
                    if proc_aliasarg:
444
 
                        alias_opts[optname] = True
445
 
            else:
446
 
                args.append(a)
447
 
        proc_aliasarg = False # Done with alias argv
 
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])
448
554
    return args, opts
449
555
 
450
556
 
465
571
                argdict[argname + '_list'] = None
466
572
        elif ap[-1] == '+':
467
573
            if not args:
468
 
                raise BzrCommandError("command %r needs one or more %s"
469
 
                        % (cmd, argname.upper()))
 
574
                raise errors.BzrCommandError("command %r needs one or more %s"
 
575
                                             % (cmd, argname.upper()))
470
576
            else:
471
577
                argdict[argname + '_list'] = args[:]
472
578
                args = []
473
579
        elif ap[-1] == '$': # all but one
474
580
            if len(args) < 2:
475
 
                raise BzrCommandError("command %r needs one or more %s"
476
 
                        % (cmd, argname.upper()))
 
581
                raise errors.BzrCommandError("command %r needs one or more %s"
 
582
                                             % (cmd, argname.upper()))
477
583
            argdict[argname + '_list'] = args[:-1]
478
 
            args[:-1] = []                
 
584
            args[:-1] = []
479
585
        else:
480
586
            # just a plain arg
481
587
            argname = ap
482
588
            if not args:
483
 
                raise BzrCommandError("command %r requires argument %s"
484
 
                        % (cmd, argname.upper()))
 
589
                raise errors.BzrCommandError("command %r requires argument %s"
 
590
                               % (cmd, argname.upper()))
485
591
            else:
486
592
                argdict[argname] = args.pop(0)
487
593
            
488
594
    if args:
489
 
        raise BzrCommandError("extra argument to command %s: %s"
490
 
                              % (cmd, args[0]))
 
595
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
596
                                     % (cmd, args[0]))
491
597
 
492
598
    return argdict
493
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)
494
614
 
495
615
 
496
616
def apply_profiled(the_callable, *args, **kwargs):
518
638
 
519
639
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
520
640
    from bzrlib.lsprof import profile
521
 
    import cPickle
522
641
    ret, stats = profile(the_callable, *args, **kwargs)
523
642
    stats.sort()
524
643
    if filename is None:
525
644
        stats.pprint()
526
645
    else:
527
 
        stats.freeze()
528
 
        cPickle.dump(stats, open(filename, 'w'), 2)
529
 
        print 'Profile data written to %r.' % filename
 
646
        stats.save(filename)
 
647
        trace.note('Profile data written to "%s".', filename)
530
648
    return ret
531
649
 
532
650
 
533
 
def get_alias(cmd):
534
 
    """Return an expanded alias, or None if no alias exists"""
535
 
    import bzrlib.config
536
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
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)
537
670
    if (alias):
538
 
        return alias.split(' ')
 
671
        return shlex_split_unicode(alias)
539
672
    return None
540
673
 
541
674
 
570
703
 
571
704
    --lsprof
572
705
        Run under the Python lsprof profiler.
 
706
 
 
707
    --coverage
 
708
        Generate line coverage report in the specified directory.
573
709
    """
574
710
    argv = list(argv)
 
711
    trace.mutter("bzr arguments: %r", argv)
575
712
 
576
713
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
577
714
                opt_no_aliases = False
578
 
    opt_lsprof_file = None
 
715
    opt_lsprof_file = opt_coverage_dir = None
579
716
 
580
717
    # --no-plugins is handled specially at a very early stage. We need
581
718
    # to load plugins before doing other command parsing so that they
590
727
        elif a == '--lsprof':
591
728
            opt_lsprof = True
592
729
        elif a == '--lsprof-file':
 
730
            opt_lsprof = True
593
731
            opt_lsprof_file = argv[i + 1]
594
732
            i += 1
595
733
        elif a == '--no-plugins':
598
736
            opt_no_aliases = True
599
737
        elif a == '--builtin':
600
738
            opt_builtin = True
601
 
        elif a in ('--quiet', '-q'):
602
 
            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:])
603
744
        else:
604
745
            argv_copy.append(a)
605
746
        i += 1
611
752
        return 0
612
753
 
613
754
    if argv[0] == '--version':
614
 
        from bzrlib.builtins import show_version
615
 
        show_version()
 
755
        from bzrlib.builtins import cmd_version
 
756
        cmd_version().run_argv_aliases([])
616
757
        return 0
617
758
        
618
759
    if not opt_no_plugins:
630
771
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
631
772
            argv[0] = alias_argv.pop(0)
632
773
 
633
 
    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.
634
778
 
635
779
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
636
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
637
 
        run = cmd_obj.run_argv
638
 
        run_argv = [argv]
639
 
    else:
640
 
        run = cmd_obj.run_argv_aliases
641
 
        run_argv = [argv, alias_argv]
 
780
    run = cmd_obj.run_argv_aliases
 
781
    run_argv = [argv, alias_argv]
642
782
 
643
783
    try:
644
784
        if opt_lsprof:
 
785
            if opt_coverage_dir:
 
786
                trace.warning(
 
787
                    '--coverage ignored, because --lsprof is in use.')
645
788
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
646
789
        elif opt_profile:
 
790
            if opt_coverage_dir:
 
791
                trace.warning(
 
792
                    '--coverage ignored, because --profile is in use.')
647
793
            ret = apply_profiled(run, *run_argv)
 
794
        elif opt_coverage_dir:
 
795
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
648
796
        else:
649
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)
650
809
        return ret or 0
651
810
    finally:
652
811
        # reset, in case we may do other commands later within the same process
653
 
        be_quiet(False)
 
812
        option._verbosity_level = 0
654
813
 
655
814
def display_command(func):
656
815
    """Decorator that suppresses pipe/interrupt errors."""
660
819
            sys.stdout.flush()
661
820
            return result
662
821
        except IOError, e:
663
 
            if not hasattr(e, 'errno'):
 
822
            if getattr(e, 'errno', None) is None:
664
823
                raise
665
824
            if e.errno != errno.EPIPE:
666
 
                raise
 
825
                # Win32 raises IOError with errno=0 on a broken pipe
 
826
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
827
                    raise
667
828
            pass
668
829
        except KeyboardInterrupt:
669
830
            pass
673
834
def main(argv):
674
835
    import bzrlib.ui
675
836
    from bzrlib.ui.text import TextUIFactory
676
 
    ## bzrlib.trace.enable_default_logging()
677
 
    bzrlib.trace.log_startup(argv)
678
837
    bzrlib.ui.ui_factory = TextUIFactory()
679
 
 
680
 
    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))
681
848
    ret = run_bzr_catch_errors(argv)
682
 
    mutter("return code %d", ret)
 
849
    trace.mutter("return code %d", ret)
683
850
    return ret
684
851
 
685
852
 
686
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.
687
856
    try:
688
 
        try:
689
 
            return run_bzr(argv)
690
 
        finally:
691
 
            # do this here inside the exception wrappers to catch EPIPE
692
 
            sys.stdout.flush()
693
 
    except Exception, e:
 
857
        return run_bzr(argv)
 
858
    except (KeyboardInterrupt, Exception), e:
694
859
        # used to handle AssertionError and KeyboardInterrupt
695
860
        # specially here, but hopefully they're handled ok by the logger now
696
 
        import errno
697
 
        if (isinstance(e, IOError) 
698
 
            and hasattr(e, 'errno')
699
 
            and e.errno == errno.EPIPE):
700
 
            bzrlib.trace.note('broken pipe')
701
 
            return 3
702
 
        else:
703
 
            bzrlib.trace.log_exception()
704
 
            if os.environ.get('BZR_PDB'):
705
 
                print '**** entering debugger'
706
 
                import pdb
707
 
                pdb.post_mortem(sys.exc_traceback)
708
 
            return 3
 
861
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
 
862
        if os.environ.get('BZR_PDB'):
 
863
            print '**** entering debugger'
 
864
            import pdb
 
865
            pdb.post_mortem(sys.exc_traceback)
 
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))