~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Andrew Bennetts
  • Date: 2008-09-05 10:48:03 UTC
  • mto: This revision was merged to the branch mainline in revision 3693.
  • Revision ID: andrew.bennetts@canonical.com-20080905104803-6g72dz6wcldosfs2
Remove monkey-patching of branch._ensure_real from test_remote.py.

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 plain 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
 
            input_encoding = getattr(sys.stdin, 'encoding', None)
241
 
            if not input_encoding:
242
 
                output_encoding = bzrlib.user_encoding
243
 
                mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
244
 
            else:
245
 
                output_encoding = input_encoding
246
 
                mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
247
 
        else:
248
 
            mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
 
457
        output_encoding = osutils.get_terminal_encoding()
249
458
 
250
 
        # use 'replace' so that we don't abort if trying to write out
251
 
        # in e.g. the default C locale.
252
 
        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)
253
461
        # For whatever reason codecs.getwriter() does not advertise its encoding
254
462
        # it just returns the encoding of the wrapped file, which is completely
255
463
        # bogus. So set the attribute, so we can find the correct encoding later.
256
464
        self.outf.encoding = output_encoding
257
465
 
258
 
    @deprecated_method(zero_eight)
259
 
    def run_argv(self, argv):
260
 
        """Parse command line and run.
261
 
        
262
 
        See run_argv_aliases for the 0.8 and beyond api.
263
 
        """
264
 
        return self.run_argv_aliases(argv)
265
 
 
266
466
    def run_argv_aliases(self, argv, alias_argv=None):
267
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 = []
268
472
        args, opts = parse_args(self, argv, alias_argv)
 
473
 
 
474
        # Process the standard options
269
475
        if 'help' in opts:  # e.g. bzr add --help
270
 
            from bzrlib.help import help_on_command
271
 
            help_on_command(self.name())
 
476
            sys.stdout.write(self.get_help_text())
272
477
            return 0
273
 
        # XXX: This should be handled by the parser
274
 
        allowed_names = self.options().keys()
275
 
        for oname in opts:
276
 
            if oname not in allowed_names:
277
 
                raise BzrCommandError("option '--%s' is not allowed for"
278
 
                                      " 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
 
279
488
        # mix arguments and options into one dictionary
280
489
        cmdargs = _match_argform(self.name(), self.takes_args, args)
281
490
        cmdopts = {}
288
497
        self._setup_outf()
289
498
 
290
499
        return self.run(**all_cmd_args)
291
 
    
 
500
 
292
501
    def run(self):
293
502
        """Actually run the command.
294
503
 
299
508
        shell error code if not.  It's OK for this method to allow
300
509
        an exception to raise up.
301
510
        """
302
 
        raise NotImplementedError('no implementation of command %r' 
 
511
        raise NotImplementedError('no implementation of command %r'
303
512
                                  % self.name())
304
513
 
305
514
    def help(self):
312
521
    def name(self):
313
522
        return _unsquish_command_name(self.__class__.__name__)
314
523
 
 
524
    def plugin_name(self):
 
525
        """Get the name of the plugin that provides this command.
315
526
 
316
 
def parse_spec(spec):
317
 
    """
318
 
    >>> parse_spec(None)
319
 
    [None, None]
320
 
    >>> parse_spec("./")
321
 
    ['./', None]
322
 
    >>> parse_spec("../@")
323
 
    ['..', -1]
324
 
    >>> parse_spec("../f/@35")
325
 
    ['../f', 35]
326
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
327
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
328
 
    """
329
 
    if spec is None:
330
 
        return [None, None]
331
 
    if '/@' in spec:
332
 
        parsed = spec.split('/@')
333
 
        assert len(parsed) == 2
334
 
        if parsed[1] == "":
335
 
            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]
336
532
        else:
337
 
            try:
338
 
                parsed[1] = int(parsed[1])
339
 
            except ValueError:
340
 
                pass # We can allow stuff like ./@revid:blahblahblah
341
 
            else:
342
 
                assert parsed[1] >=0
343
 
    else:
344
 
        parsed = [spec, None]
345
 
    return parsed
 
533
            return None
 
534
 
346
535
 
347
536
def parse_args(command, argv, alias_argv=None):
348
537
    """Parse command line.
352
541
    lookup table, something about the available options, what optargs
353
542
    they take, and which commands will accept them.
354
543
    """
355
 
    # TODO: chop up this beast; make it a method of the Command
356
 
    args = []
357
 
    opts = {}
358
 
    alias_opts = {}
359
 
 
360
 
    cmd_options = command.options()
361
 
    argsover = False
362
 
    proc_aliasarg = True # Are we processing alias_argv now?
363
 
    for proc_argv in alias_argv, argv:
364
 
        while proc_argv:
365
 
            a = proc_argv.pop(0)
366
 
            if argsover:
367
 
                args.append(a)
368
 
                continue
369
 
            elif a == '--':
370
 
                # We've received a standalone -- No more flags
371
 
                argsover = True
372
 
                continue
373
 
            if a[0] == '-':
374
 
                # option names must not be unicode
375
 
                a = str(a)
376
 
                optarg = None
377
 
                if a[1] == '-':
378
 
                    mutter("  got option %r", a)
379
 
                    if '=' in a:
380
 
                        optname, optarg = a[2:].split('=', 1)
381
 
                    else:
382
 
                        optname = a[2:]
383
 
                    if optname not in cmd_options:
384
 
                        raise BzrOptionError('unknown long option %r for'
385
 
                                             ' command %s' % 
386
 
                                             (a, command.name()))
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 BzrError('unknown short option %r' % a)
402
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
403
 
 
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
 
                
419
 
                    if optname not in cmd_options:
420
 
                        raise BzrOptionError('unknown short option %r for'
421
 
                                             ' command %s' % 
422
 
                                             (shortopt, command.name()))
423
 
                if optname in opts:
424
 
                    # XXX: Do we ever want to support this, e.g. for -r?
425
 
                    if proc_aliasarg:
426
 
                        raise BzrError('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 BzrError('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 BzrError('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 BzrError('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
 
            args[:-1] = []                
 
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
599
727
        elif a == '--lsprof':
600
728
            opt_lsprof = True
601
729
        elif a == '--lsprof-file':
 
730
            opt_lsprof = True
602
731
            opt_lsprof_file = argv[i + 1]
603
732
            i += 1
604
733
        elif a == '--no-plugins':
607
736
            opt_no_aliases = True
608
737
        elif a == '--builtin':
609
738
            opt_builtin = True
610
 
        elif a in ('--quiet', '-q'):
611
 
            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:])
612
744
        else:
613
745
            argv_copy.append(a)
614
746
        i += 1
620
752
        return 0
621
753
 
622
754
    if argv[0] == '--version':
623
 
        from bzrlib.builtins import show_version
624
 
        show_version()
 
755
        from bzrlib.builtins import cmd_version
 
756
        cmd_version().run_argv_aliases([])
625
757
        return 0
626
758
        
627
759
    if not opt_no_plugins:
639
771
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
640
772
            argv[0] = alias_argv.pop(0)
641
773
 
642
 
    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.
643
778
 
644
779
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
645
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
646
 
        run = cmd_obj.run_argv
647
 
        run_argv = [argv]
648
 
    else:
649
 
        run = cmd_obj.run_argv_aliases
650
 
        run_argv = [argv, alias_argv]
 
780
    run = cmd_obj.run_argv_aliases
 
781
    run_argv = [argv, alias_argv]
651
782
 
652
783
    try:
653
784
        if opt_lsprof:
 
785
            if opt_coverage_dir:
 
786
                trace.warning(
 
787
                    '--coverage ignored, because --lsprof is in use.')
654
788
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
655
789
        elif opt_profile:
 
790
            if opt_coverage_dir:
 
791
                trace.warning(
 
792
                    '--coverage ignored, because --profile is in use.')
656
793
            ret = apply_profiled(run, *run_argv)
 
794
        elif opt_coverage_dir:
 
795
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
657
796
        else:
658
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)
659
809
        return ret or 0
660
810
    finally:
661
811
        # reset, in case we may do other commands later within the same process
662
 
        be_quiet(False)
 
812
        option._verbosity_level = 0
663
813
 
664
814
def display_command(func):
665
815
    """Decorator that suppresses pipe/interrupt errors."""
669
819
            sys.stdout.flush()
670
820
            return result
671
821
        except IOError, e:
672
 
            if not hasattr(e, 'errno'):
 
822
            if getattr(e, 'errno', None) is None:
673
823
                raise
674
824
            if e.errno != errno.EPIPE:
675
 
                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
676
828
            pass
677
829
        except KeyboardInterrupt:
678
830
            pass
682
834
def main(argv):
683
835
    import bzrlib.ui
684
836
    from bzrlib.ui.text import TextUIFactory
685
 
    ## bzrlib.trace.enable_default_logging()
686
 
    bzrlib.trace.log_startup(argv)
687
837
    bzrlib.ui.ui_factory = TextUIFactory()
688
 
 
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
 
        try:
698
 
            return run_bzr(argv)
699
 
        finally:
700
 
            # do this here inside the exception wrappers to catch EPIPE
701
 
            sys.stdout.flush()
702
 
    except Exception, e:
 
857
        return run_bzr(argv)
 
858
    except (KeyboardInterrupt, Exception), e:
703
859
        # used to handle AssertionError and KeyboardInterrupt
704
860
        # specially here, but hopefully they're handled ok by the logger now
705
 
        import errno
706
 
        if (isinstance(e, IOError) 
707
 
            and hasattr(e, 'errno')
708
 
            and e.errno == errno.EPIPE):
709
 
            bzrlib.trace.note('broken pipe')
710
 
            return 3
711
 
        else:
712
 
            bzrlib.trace.log_exception()
713
 
            if os.environ.get('BZR_PDB'):
714
 
                print '**** entering debugger'
715
 
                import pdb
716
 
                pdb.post_mortem(sys.exc_traceback)
717
 
            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
 
718
930
 
719
931
if __name__ == '__main__':
720
932
    sys.exit(main(sys.argv))