~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-18 05:22:35 UTC
  • mfrom: (1551.15.27 Aaron's mergeable stuff)
  • Revision ID: pqm@pqm.ubuntu.com-20070618052235-mvns8j28szyzscy0
Turn list-weave into list-versionedfile

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
    option,
45
45
    osutils,
46
46
    trace,
47
 
    win32utils,
48
47
    )
49
48
""")
50
49
 
51
50
from bzrlib.symbol_versioning import (
52
51
    deprecated_function,
53
52
    deprecated_method,
 
53
    zero_eight,
 
54
    zero_eleven,
54
55
    )
55
56
# Compatibility
56
57
from bzrlib.option import Option
85
86
    else:
86
87
        trace.log_error('Two plugins defined the same command: %r' % k)
87
88
        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
89
 
91
90
 
92
91
def _squish_command_name(cmd):
240
239
        """Construct an instance of this command."""
241
240
        if self.__doc__ == Command.__doc__:
242
241
            warn("No help message set for %r" % self)
243
 
        # List of standard options directly supported
244
 
        self.supported_std_options = []
245
 
 
246
 
    def _maybe_expand_globs(self, file_list):
247
 
        """Glob expand file_list if the platform does not do that itself.
248
 
        
249
 
        :return: A possibly empty list of unicode paths.
250
 
 
251
 
        Introduced in bzrlib 0.18.
252
 
        """
253
 
        if not file_list:
254
 
            file_list = []
255
 
        if sys.platform == 'win32':
256
 
            file_list = win32utils.glob_expand(file_list)
257
 
        return list(file_list)
258
242
 
259
243
    def _usage(self):
260
244
        """Return single-line grammar for this command.
276
260
        s = s[:-1]
277
261
        return s
278
262
 
279
 
    def get_help_text(self, additional_see_also=None, plain=True,
280
 
                      see_also_as_links=False):
 
263
    def get_help_text(self, additional_see_also=None):
281
264
        """Return a text string with help for this command.
282
265
        
283
266
        :param additional_see_also: Additional help topics to be
284
267
            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)
289
268
        """
290
269
        doc = self.help()
291
270
        if doc is None:
292
271
            raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
293
272
 
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
304
273
        result = ""
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
 
274
        result += 'usage: %s\n' % self._usage()
 
275
 
338
276
        if self.aliases:
339
 
            result += ':Aliases:  '
 
277
            result += 'aliases: '
340
278
            result += ', '.join(self.aliases) + '\n'
 
279
 
 
280
        result += '\n'
 
281
 
341
282
        plugin_name = self.plugin_name()
342
283
        if plugin_name is not None:
343
 
            result += ':From:     plugin "%s"\n' % plugin_name
 
284
            result += '(From plugin "%s")' % plugin_name
 
285
            result += '\n\n'
 
286
 
 
287
        result += doc
 
288
        if result[-1] != '\n':
 
289
            result += '\n'
 
290
        result += '\n'
 
291
        result += option.get_optparser(self.options()).format_option_help()
344
292
        see_also = self.get_see_also(additional_see_also)
345
293
        if see_also:
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)
 
294
            result += '\nSee also: '
 
295
            result += ', '.join(see_also)
 
296
            result += '\n'
364
297
        return result
365
298
 
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
 
 
403
299
    def get_help_topic(self):
404
300
        """Return the commands help topic - its name."""
405
301
        return self.name()
406
302
 
407
303
    def get_see_also(self, additional_terms=None):
408
 
        """Return a list of help topics that are related to this command.
 
304
        """Return a list of help topics that are related to this ommand.
409
305
        
410
306
        The list is derived from the content of the _see_also attribute. Any
411
307
        duplicates are removed and the result is in lexical order.
421
317
        """Return dict of valid options for this command.
422
318
 
423
319
        Maps from long option name to option object."""
424
 
        r = Option.STD_OPTIONS.copy()
425
 
        std_names = r.keys()
 
320
        r = dict()
 
321
        r['help'] = option.Option.OPTIONS['help']
426
322
        for o in self.takes_options:
427
323
            if isinstance(o, basestring):
428
324
                o = option.Option.OPTIONS[o]
429
325
            r[o.name] = o
430
 
            if o.name in std_names:
431
 
                self.supported_std_options.append(o.name)
432
326
        return r
433
327
 
434
328
    def _setup_outf(self):
449
343
 
450
344
        output_encoding = osutils.get_terminal_encoding()
451
345
 
452
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout,
453
 
                        errors=self.encoding_type)
 
346
        # use 'replace' so that we don't abort if trying to write out
 
347
        # in e.g. the default C locale.
 
348
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
454
349
        # For whatever reason codecs.getwriter() does not advertise its encoding
455
350
        # it just returns the encoding of the wrapped file, which is completely
456
351
        # bogus. So set the attribute, so we can find the correct encoding later.
463
358
                 DeprecationWarning, stacklevel=2)
464
359
            argv = []
465
360
        args, opts = parse_args(self, argv, alias_argv)
466
 
 
467
 
        # Process the standard options
468
361
        if 'help' in opts:  # e.g. bzr add --help
469
362
            sys.stdout.write(self.get_help_text())
470
363
            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
 
 
481
364
        # mix arguments and options into one dictionary
482
365
        cmdargs = _match_argform(self.name(), self.takes_args, args)
483
366
        cmdopts = {}
526
409
            return None
527
410
 
528
411
 
 
412
# Technically, this function hasn't been use in a *really* long time
 
413
# but we are only deprecating it now.
 
414
@deprecated_function(zero_eleven)
 
415
def parse_spec(spec):
 
416
    """
 
417
    >>> parse_spec(None)
 
418
    [None, None]
 
419
    >>> parse_spec("./")
 
420
    ['./', None]
 
421
    >>> parse_spec("../@")
 
422
    ['..', -1]
 
423
    >>> parse_spec("../f/@35")
 
424
    ['../f', 35]
 
425
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
426
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
427
    """
 
428
    if spec is None:
 
429
        return [None, None]
 
430
    if '/@' in spec:
 
431
        parsed = spec.split('/@')
 
432
        assert len(parsed) == 2
 
433
        if parsed[1] == "":
 
434
            parsed[1] = -1
 
435
        else:
 
436
            try:
 
437
                parsed[1] = int(parsed[1])
 
438
            except ValueError:
 
439
                pass # We can allow stuff like ./@revid:blahblahblah
 
440
            else:
 
441
                assert parsed[1] >=0
 
442
    else:
 
443
        parsed = [spec, None]
 
444
    return parsed
 
445
 
529
446
def parse_args(command, argv, alias_argv=None):
530
447
    """Parse command line.
531
448
    
617
534
 
618
535
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
619
536
    from bzrlib.lsprof import profile
 
537
    import cPickle
620
538
    ret, stats = profile(the_callable, *args, **kwargs)
621
539
    stats.sort()
622
540
    if filename is None:
623
541
        stats.pprint()
624
542
    else:
625
543
        stats.save(filename)
626
 
        trace.note('Profile data written to "%s".', filename)
 
544
        print 'Profile data written to %r.' % filename
627
545
    return ret
628
546
 
629
547
 
630
 
def shlex_split_unicode(unsplit):
631
 
    import shlex
632
 
    return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
633
 
 
634
 
 
635
548
def get_alias(cmd, config=None):
636
549
    """Return an expanded alias, or None if no alias exists.
637
550
 
647
560
        config = bzrlib.config.GlobalConfig()
648
561
    alias = config.get_alias(cmd)
649
562
    if (alias):
650
 
        return shlex_split_unicode(alias)
 
563
        import shlex
 
564
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
651
565
    return None
652
566
 
653
567
 
712
626
            opt_no_aliases = True
713
627
        elif a == '--builtin':
714
628
            opt_builtin = True
 
629
        elif a in ('--quiet', '-q'):
 
630
            trace.be_quiet()
715
631
        elif a.startswith('-D'):
716
632
            debug.debug_flags.add(a[2:])
717
633
        else:
725
641
        return 0
726
642
 
727
643
    if argv[0] == '--version':
728
 
        from bzrlib.builtins import cmd_version
729
 
        cmd_version().run_argv_aliases([])
 
644
        from bzrlib.version import show_version
 
645
        show_version()
730
646
        return 0
731
647
        
732
648
    if not opt_no_plugins:
763
679
        return ret or 0
764
680
    finally:
765
681
        # reset, in case we may do other commands later within the same process
766
 
        option._verbosity_level = 0
 
682
        trace.be_quiet(False)
767
683
 
768
684
def display_command(func):
769
685
    """Decorator that suppresses pipe/interrupt errors."""
789
705
    import bzrlib.ui
790
706
    from bzrlib.ui.text import TextUIFactory
791
707
    bzrlib.ui.ui_factory = TextUIFactory()
792
 
    try:
793
 
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
794
 
    except UnicodeDecodeError:
795
 
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
796
 
                                                            "encoding." % a))
 
708
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
797
709
    ret = run_bzr_catch_errors(argv)
798
710
    trace.mutter("return code %d", ret)
799
711
    return ret
800
712
 
801
713
 
802
714
def run_bzr_catch_errors(argv):
803
 
    # Note: The except clause logic below should be kept in sync with the
804
 
    # profile() routine in lsprof.py.
805
715
    try:
806
 
        return run_bzr(argv)
 
716
        try:
 
717
            return run_bzr(argv)
 
718
        finally:
 
719
            # do this here inside the exception wrappers to catch EPIPE
 
720
            sys.stdout.flush()
807
721
    except (KeyboardInterrupt, Exception), e:
808
722
        # used to handle AssertionError and KeyboardInterrupt
809
723
        # specially here, but hopefully they're handled ok by the logger now
810
 
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
 
724
        trace.report_exception(sys.exc_info(), sys.stderr)
811
725
        if os.environ.get('BZR_PDB'):
812
726
            print '**** entering debugger'
813
727
            import pdb
814
728
            pdb.post_mortem(sys.exc_traceback)
815
 
        return exitcode
816
 
 
817
 
 
818
 
def run_bzr_catch_user_errors(argv):
819
 
    """Run bzr and report user errors, but let internal errors propagate.
820
 
 
821
 
    This is used for the test suite, and might be useful for other programs
822
 
    that want to wrap the commandline interface.
823
 
    """
824
 
    try:
825
 
        return run_bzr(argv)
826
 
    except Exception, e:
827
 
        if (isinstance(e, (OSError, IOError))
828
 
            or not getattr(e, 'internal_error', True)):
829
 
            trace.report_exception(sys.exc_info(), sys.stderr)
830
 
            return 3
831
 
        else:
832
 
            raise
 
729
        return 3
833
730
 
834
731
 
835
732
class HelpCommandIndex(object):