/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: Martin Pool
  • Date: 2007-09-14 06:31:28 UTC
  • mfrom: (2822 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2823.
  • Revision ID: mbp@sourcefrog.net-20070914063128-0p7mh6zfb4pzdg9p
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
from bzrlib.symbol_versioning import (
52
52
    deprecated_function,
53
53
    deprecated_method,
54
 
    zero_eight,
55
 
    zero_eleven,
56
54
    )
57
55
# Compatibility
58
56
from bzrlib.option import Option
87
85
    else:
88
86
        trace.log_error('Two plugins defined the same command: %r' % k)
89
87
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
88
        trace.log_error('Previously this command was registered from %r' %
 
89
                        sys.modules[plugin_cmds[k_unsquished].__module__])
90
90
 
91
91
 
92
92
def _squish_command_name(cmd):
240
240
        """Construct an instance of this command."""
241
241
        if self.__doc__ == Command.__doc__:
242
242
            warn("No help message set for %r" % self)
 
243
        # List of standard options directly supported
 
244
        self.supported_std_options = []
243
245
 
244
246
    def _maybe_expand_globs(self, file_list):
245
247
        """Glob expand file_list if the platform does not do that itself.
274
276
        s = s[:-1]
275
277
        return s
276
278
 
277
 
    def get_help_text(self, additional_see_also=None):
 
279
    def get_help_text(self, additional_see_also=None, plain=True,
 
280
                      see_also_as_links=False):
278
281
        """Return a text string with help for this command.
279
282
        
280
283
        :param additional_see_also: Additional help topics to be
281
284
            cross-referenced.
 
285
        :param plain: if False, raw help (reStructuredText) is
 
286
            returned instead of plain text.
 
287
        :param see_also_as_links: if True, convert items in 'See also'
 
288
            list to internal links (used by bzr_man rstx generator)
282
289
        """
283
290
        doc = self.help()
284
291
        if doc is None:
285
292
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
286
293
 
 
294
        # Extract the summary (purpose) and sections out from the text
 
295
        purpose,sections = self._get_help_parts(doc)
 
296
 
 
297
        # If a custom usage section was provided, use it
 
298
        if sections.has_key('Usage'):
 
299
            usage = sections.pop('Usage')
 
300
        else:
 
301
            usage = self._usage()
 
302
 
 
303
        # The header is the purpose and usage
287
304
        result = ""
288
 
        result += 'usage: %s\n' % self._usage()
289
 
 
 
305
        result += ':Purpose: %s\n' % purpose
 
306
        if usage.find('\n') >= 0:
 
307
            result += ':Usage:\n%s\n' % usage
 
308
        else:
 
309
            result += ':Usage:   %s\n' % usage
 
310
        result += '\n'
 
311
 
 
312
        # Add the options
 
313
        options = option.get_optparser(self.options()).format_option_help()
 
314
        if options.startswith('Options:'):
 
315
            result += ':' + options
 
316
        elif options.startswith('options:'):
 
317
            # Python 2.4 version of optparse
 
318
            result += ':Options:' + options[len('options:'):]
 
319
        else:
 
320
            result += options
 
321
        result += '\n'
 
322
 
 
323
        # Add the description, indenting it 2 spaces
 
324
        # to match the indentation of the options
 
325
        if sections.has_key(None):
 
326
            text = sections.pop(None)
 
327
            text = '\n  '.join(text.splitlines())
 
328
            result += ':%s:\n  %s\n\n' % ('Description',text)
 
329
 
 
330
        # Add the custom sections (e.g. Examples). Note that there's no need
 
331
        # to indent these as they must be indented already in the source.
 
332
        if sections:
 
333
            labels = sorted(sections.keys())
 
334
            for label in labels:
 
335
                result += ':%s:\n%s\n\n' % (label,sections[label])
 
336
 
 
337
        # Add the aliases, source (plug-in) and see also links, if any
290
338
        if self.aliases:
291
 
            result += 'aliases: '
 
339
            result += ':Aliases:  '
292
340
            result += ', '.join(self.aliases) + '\n'
293
 
 
294
 
        result += '\n'
295
 
 
296
341
        plugin_name = self.plugin_name()
297
342
        if plugin_name is not None:
298
 
            result += '(From plugin "%s")' % plugin_name
299
 
            result += '\n\n'
300
 
 
301
 
        result += doc
302
 
        if result[-1] != '\n':
303
 
            result += '\n'
304
 
        result += '\n'
305
 
        result += option.get_optparser(self.options()).format_option_help()
 
343
            result += ':From:     plugin "%s"\n' % plugin_name
306
344
        see_also = self.get_see_also(additional_see_also)
307
345
        if see_also:
308
 
            result += '\nSee also: '
309
 
            result += ', '.join(see_also)
310
 
            result += '\n'
 
346
            if not plain and see_also_as_links:
 
347
                see_also_links = []
 
348
                for item in see_also:
 
349
                    if item == 'topics':
 
350
                        # topics doesn't have an independent section
 
351
                        # so don't create a real link
 
352
                        see_also_links.append(item)
 
353
                    else:
 
354
                        # Use a reST link for this entry
 
355
                        see_also_links.append("`%s`_" % (item,))
 
356
                see_also = see_also_links
 
357
            result += ':See also: '
 
358
            result += ', '.join(see_also) + '\n'
 
359
 
 
360
        # If this will be rendered as plan text, convert it
 
361
        if plain:
 
362
            import bzrlib.help_topics
 
363
            result = bzrlib.help_topics.help_as_plain_text(result)
311
364
        return result
312
365
 
 
366
    @staticmethod
 
367
    def _get_help_parts(text):
 
368
        """Split help text into a summary and named sections.
 
369
 
 
370
        :return: (summary,sections) where summary is the top line and
 
371
            sections is a dictionary of the rest indexed by section name.
 
372
            A section starts with a heading line of the form ":xxx:".
 
373
            Indented text on following lines is the section value.
 
374
            All text found outside a named section is assigned to the
 
375
            default section which is given the key of None.
 
376
        """
 
377
        def save_section(sections, label, section):
 
378
            if len(section) > 0:
 
379
                if sections.has_key(label):
 
380
                    sections[label] += '\n' + section
 
381
                else:
 
382
                    sections[label] = section
 
383
            
 
384
        lines = text.rstrip().splitlines()
 
385
        summary = lines.pop(0)
 
386
        sections = {}
 
387
        label,section = None,''
 
388
        for line in lines:
 
389
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
 
390
                save_section(sections, label, section)
 
391
                label,section = line[1:-1],''
 
392
            elif label != None and len(line) > 1 and not line[0].isspace():
 
393
                save_section(sections, label, section)
 
394
                label,section = None,line
 
395
            else:
 
396
                if len(section) > 0:
 
397
                    section += '\n' + line
 
398
                else:
 
399
                    section = line
 
400
        save_section(sections, label, section)
 
401
        return summary, sections
 
402
 
313
403
    def get_help_topic(self):
314
404
        """Return the commands help topic - its name."""
315
405
        return self.name()
316
406
 
317
407
    def get_see_also(self, additional_terms=None):
318
 
        """Return a list of help topics that are related to this ommand.
 
408
        """Return a list of help topics that are related to this command.
319
409
        
320
410
        The list is derived from the content of the _see_also attribute. Any
321
411
        duplicates are removed and the result is in lexical order.
331
421
        """Return dict of valid options for this command.
332
422
 
333
423
        Maps from long option name to option object."""
334
 
        r = dict()
335
 
        r['help'] = option._help_option
 
424
        r = Option.STD_OPTIONS.copy()
 
425
        std_names = r.keys()
336
426
        for o in self.takes_options:
337
427
            if isinstance(o, basestring):
338
428
                o = option.Option.OPTIONS[o]
339
429
            r[o.name] = o
 
430
            if o.name in std_names:
 
431
                self.supported_std_options.append(o.name)
340
432
        return r
341
433
 
342
434
    def _setup_outf(self):
357
449
 
358
450
        output_encoding = osutils.get_terminal_encoding()
359
451
 
360
 
        # use 'replace' so that we don't abort if trying to write out
361
 
        # in e.g. the default C locale.
362
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
452
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
 
453
                        errors=self.encoding_type)
363
454
        # For whatever reason codecs.getwriter() does not advertise its encoding
364
455
        # it just returns the encoding of the wrapped file, which is completely
365
456
        # bogus. So set the attribute, so we can find the correct encoding later.
372
463
                 DeprecationWarning, stacklevel=2)
373
464
            argv = []
374
465
        args, opts = parse_args(self, argv, alias_argv)
 
466
 
 
467
        # Process the standard options
375
468
        if 'help' in opts:  # e.g. bzr add --help
376
469
            sys.stdout.write(self.get_help_text())
377
470
            return 0
 
471
        trace.set_verbosity_level(option._verbosity_level)
 
472
        if 'verbose' in self.supported_std_options:
 
473
            opts['verbose'] = trace.is_verbose()
 
474
        elif opts.has_key('verbose'):
 
475
            del opts['verbose']
 
476
        if 'quiet' in self.supported_std_options:
 
477
            opts['quiet'] = trace.is_quiet()
 
478
        elif opts.has_key('quiet'):
 
479
            del opts['quiet']
 
480
 
378
481
        # mix arguments and options into one dictionary
379
482
        cmdargs = _match_argform(self.name(), self.takes_args, args)
380
483
        cmdopts = {}
423
526
            return None
424
527
 
425
528
 
426
 
# Technically, this function hasn't been use in a *really* long time
427
 
# but we are only deprecating it now.
428
 
@deprecated_function(zero_eleven)
429
 
def parse_spec(spec):
430
 
    """
431
 
    >>> parse_spec(None)
432
 
    [None, None]
433
 
    >>> parse_spec("./")
434
 
    ['./', None]
435
 
    >>> parse_spec("../@")
436
 
    ['..', -1]
437
 
    >>> parse_spec("../f/@35")
438
 
    ['../f', 35]
439
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
440
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
441
 
    """
442
 
    if spec is None:
443
 
        return [None, None]
444
 
    if '/@' in spec:
445
 
        parsed = spec.split('/@')
446
 
        assert len(parsed) == 2
447
 
        if parsed[1] == "":
448
 
            parsed[1] = -1
449
 
        else:
450
 
            try:
451
 
                parsed[1] = int(parsed[1])
452
 
            except ValueError:
453
 
                pass # We can allow stuff like ./@revid:blahblahblah
454
 
            else:
455
 
                assert parsed[1] >=0
456
 
    else:
457
 
        parsed = [spec, None]
458
 
    return parsed
459
 
 
460
529
def parse_args(command, argv, alias_argv=None):
461
530
    """Parse command line.
462
531
    
639
708
            opt_no_aliases = True
640
709
        elif a == '--builtin':
641
710
            opt_builtin = True
642
 
        elif a in ('--quiet', '-q'):
643
 
            trace.be_quiet()
644
711
        elif a.startswith('-D'):
645
712
            debug.debug_flags.add(a[2:])
646
713
        else:
654
721
        return 0
655
722
 
656
723
    if argv[0] == '--version':
657
 
        from bzrlib.version import show_version
658
 
        show_version()
 
724
        from bzrlib.builtins import cmd_version
 
725
        cmd_version().run_argv_aliases([])
659
726
        return 0
660
727
        
661
728
    if not opt_no_plugins:
692
759
        return ret or 0
693
760
    finally:
694
761
        # reset, in case we may do other commands later within the same process
695
 
        trace.be_quiet(False)
 
762
        option._verbosity_level = 0
696
763
 
697
764
def display_command(func):
698
765
    """Decorator that suppresses pipe/interrupt errors."""
718
785
    import bzrlib.ui
719
786
    from bzrlib.ui.text import TextUIFactory
720
787
    bzrlib.ui.ui_factory = TextUIFactory()
721
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
788
    try:
 
789
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
790
    except UnicodeDecodeError:
 
791
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
792
                                                            "encoding." % a))
722
793
    ret = run_bzr_catch_errors(argv)
723
794
    trace.mutter("return code %d", ret)
724
795
    return ret
725
796
 
726
797
 
727
798
def run_bzr_catch_errors(argv):
 
799
    # Note: The except clause logic below should be kept in sync with the
 
800
    # profile() routine in lsprof.py.
728
801
    try:
729
802
        return run_bzr(argv)
730
803
    except (KeyboardInterrupt, Exception), e: