~ubuntu-branches/debian/lenny/bzr/lenny

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-08-22 20:06:37 UTC
  • mfrom: (3.1.63 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080822200637-kxobfsnjlzojhqra
Tags: 1.5-1.1
* Non-maintainer upload.
* Apply patch from upstream VCS to fix FTBFS in tools/rst2html.py
  with older docutils. Thanks to Olivier Tétard for digging it
  up.
  Closes: #494246.

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
 
32
import sys
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
31
36
import codecs
32
37
import errno
33
 
import os
34
38
from warnings import warn
35
 
import sys
36
39
 
37
40
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.errors import (BzrError,
40
 
                           BzrCommandError,
41
 
                           BzrCheckError,
42
 
                           NotBranchError)
43
 
from bzrlib import option
 
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
44
57
from bzrlib.option import Option
45
 
import bzrlib.osutils
46
 
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
47
 
import bzrlib.trace
48
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
58
 
49
59
 
50
60
plugin_cmds = {}
51
61
 
66
76
        k_unsquished = k
67
77
    if k_unsquished not in plugin_cmds:
68
78
        plugin_cmds[k_unsquished] = cmd
69
 
        mutter('registered plugin command %s', k_unsquished)
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
70
80
        if decorate and k_unsquished in builtin_command_names():
71
81
            return _builtin_commands()[k_unsquished]
72
82
    elif decorate:
74
84
        plugin_cmds[k_unsquished] = cmd
75
85
        return result
76
86
    else:
77
 
        log_error('Two plugins defined the same command: %r' % k)
78
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
89
        trace.log_error('Previously this command was registered from %r' %
 
90
                        sys.modules[plugin_cmds[k_unsquished].__module__])
79
91
 
80
92
 
81
93
def _squish_command_name(cmd):
83
95
 
84
96
 
85
97
def _unsquish_command_name(cmd):
86
 
    assert cmd.startswith("cmd_")
87
98
    return cmd[4:].replace('_','-')
88
99
 
89
100
 
127
138
    plugins_override
128
139
        If true, plugin commands can override builtins.
129
140
    """
 
141
    try:
 
142
        return _get_cmd_object(cmd_name, plugins_override)
 
143
    except KeyError:
 
144
        raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
 
145
 
 
146
 
 
147
def _get_cmd_object(cmd_name, plugins_override=True):
 
148
    """Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
130
149
    from bzrlib.externalcommand import ExternalCommand
131
150
 
132
151
    # We want only 'ascii' command names, but the user may have typed
150
169
    if cmd_obj:
151
170
        return cmd_obj
152
171
 
153
 
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
172
    # look for plugins that provide this command but aren't installed
 
173
    for provider in command_providers_registry:
 
174
        try:
 
175
            plugin_metadata = provider.plugin_for_command(cmd_name)
 
176
        except errors.NoPluginAvailable:
 
177
            pass
 
178
        else:
 
179
            raise errors.CommandAvailableInPlugin(cmd_name, 
 
180
                                                  plugin_metadata, provider)
 
181
 
 
182
    raise KeyError
154
183
 
155
184
 
156
185
class Command(object):
204
233
            replace - put in a bogus character (typically '?')
205
234
            exact - do not encode sys.stdout
206
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
 
207
242
    """
208
243
    aliases = []
209
244
    takes_args = []
216
251
        """Construct an instance of this command."""
217
252
        if self.__doc__ == Command.__doc__:
218
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)
219
428
 
220
429
    def options(self):
221
430
        """Return dict of valid options for this command.
222
431
 
223
432
        Maps from long option name to option object."""
224
 
        r = dict()
225
 
        r['help'] = Option.OPTIONS['help']
 
433
        r = Option.STD_OPTIONS.copy()
 
434
        std_names = r.keys()
226
435
        for o in self.takes_options:
227
436
            if isinstance(o, basestring):
228
 
                o = Option.OPTIONS[o]
 
437
                o = option.Option.OPTIONS[o]
229
438
            r[o.name] = o
 
439
            if o.name in std_names:
 
440
                self.supported_std_options.append(o.name)
230
441
        return r
231
442
 
232
443
    def _setup_outf(self):
233
444
        """Return a file linked to stdout, which has proper encoding."""
234
 
        assert self.encoding_type in ['strict', 'exact', 'replace']
235
 
 
236
445
        # Originally I was using self.stdout, but that looks
237
446
        # *way* too much like sys.stdout
238
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)
239
454
            self.outf = sys.stdout
240
455
            return
241
456
 
242
 
        output_encoding = bzrlib.osutils.get_terminal_encoding()
 
457
        output_encoding = osutils.get_terminal_encoding()
243
458
 
244
 
        # use 'replace' so that we don't abort if trying to write out
245
 
        # in e.g. the default C locale.
246
 
        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)
247
461
        # For whatever reason codecs.getwriter() does not advertise its encoding
248
462
        # it just returns the encoding of the wrapped file, which is completely
249
463
        # bogus. So set the attribute, so we can find the correct encoding later.
250
464
        self.outf.encoding = output_encoding
251
465
 
252
 
    @deprecated_method(zero_eight)
253
 
    def run_argv(self, argv):
254
 
        """Parse command line and run.
255
 
        
256
 
        See run_argv_aliases for the 0.8 and beyond api.
257
 
        """
258
 
        return self.run_argv_aliases(argv)
259
 
 
260
466
    def run_argv_aliases(self, argv, alias_argv=None):
261
467
        """Parse the command line and run with extra aliases in alias_argv."""
262
468
        if argv is None:
263
 
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
 
469
            warn("Passing None for [] is deprecated from bzrlib 0.10",
264
470
                 DeprecationWarning, stacklevel=2)
265
471
            argv = []
266
472
        args, opts = parse_args(self, argv, alias_argv)
 
473
 
 
474
        # Process the standard options
267
475
        if 'help' in opts:  # e.g. bzr add --help
268
 
            from bzrlib.help import help_on_command
269
 
            help_on_command(self.name())
 
476
            sys.stdout.write(self.get_help_text())
270
477
            return 0
 
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
 
271
488
        # mix arguments and options into one dictionary
272
489
        cmdargs = _match_argform(self.name(), self.takes_args, args)
273
490
        cmdopts = {}
291
508
        shell error code if not.  It's OK for this method to allow
292
509
        an exception to raise up.
293
510
        """
294
 
        raise NotImplementedError('no implementation of command %r' 
 
511
        raise NotImplementedError('no implementation of command %r'
295
512
                                  % self.name())
296
513
 
297
514
    def help(self):
316
533
            return None
317
534
 
318
535
 
319
 
def parse_spec(spec):
320
 
    """
321
 
    >>> parse_spec(None)
322
 
    [None, None]
323
 
    >>> parse_spec("./")
324
 
    ['./', None]
325
 
    >>> parse_spec("../@")
326
 
    ['..', -1]
327
 
    >>> parse_spec("../f/@35")
328
 
    ['../f', 35]
329
 
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
330
 
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
331
 
    """
332
 
    if spec is None:
333
 
        return [None, None]
334
 
    if '/@' in spec:
335
 
        parsed = spec.split('/@')
336
 
        assert len(parsed) == 2
337
 
        if parsed[1] == "":
338
 
            parsed[1] = -1
339
 
        else:
340
 
            try:
341
 
                parsed[1] = int(parsed[1])
342
 
            except ValueError:
343
 
                pass # We can allow stuff like ./@revid:blahblahblah
344
 
            else:
345
 
                assert parsed[1] >=0
346
 
    else:
347
 
        parsed = [spec, None]
348
 
    return parsed
349
 
 
350
536
def parse_args(command, argv, alias_argv=None):
351
537
    """Parse command line.
352
538
    
363
549
        args = argv
364
550
 
365
551
    options, args = parser.parse_args(args)
366
 
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if 
 
552
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
367
553
                 v is not option.OptionParser.DEFAULT_VALUE])
368
554
    return args, opts
369
555
 
385
571
                argdict[argname + '_list'] = None
386
572
        elif ap[-1] == '+':
387
573
            if not args:
388
 
                raise BzrCommandError("command %r needs one or more %s"
389
 
                        % (cmd, argname.upper()))
 
574
                raise errors.BzrCommandError("command %r needs one or more %s"
 
575
                                             % (cmd, argname.upper()))
390
576
            else:
391
577
                argdict[argname + '_list'] = args[:]
392
578
                args = []
393
579
        elif ap[-1] == '$': # all but one
394
580
            if len(args) < 2:
395
 
                raise BzrCommandError("command %r needs one or more %s"
396
 
                        % (cmd, argname.upper()))
 
581
                raise errors.BzrCommandError("command %r needs one or more %s"
 
582
                                             % (cmd, argname.upper()))
397
583
            argdict[argname + '_list'] = args[:-1]
398
584
            args[:-1] = []
399
585
        else:
400
586
            # just a plain arg
401
587
            argname = ap
402
588
            if not args:
403
 
                raise BzrCommandError("command %r requires argument %s"
404
 
                        % (cmd, argname.upper()))
 
589
                raise errors.BzrCommandError("command %r requires argument %s"
 
590
                               % (cmd, argname.upper()))
405
591
            else:
406
592
                argdict[argname] = args.pop(0)
407
593
            
408
594
    if args:
409
 
        raise BzrCommandError("extra argument to command %s: %s"
410
 
                              % (cmd, args[0]))
 
595
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
596
                                     % (cmd, args[0]))
411
597
 
412
598
    return argdict
413
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)
414
614
 
415
615
 
416
616
def apply_profiled(the_callable, *args, **kwargs):
438
638
 
439
639
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
440
640
    from bzrlib.lsprof import profile
441
 
    import cPickle
442
641
    ret, stats = profile(the_callable, *args, **kwargs)
443
642
    stats.sort()
444
643
    if filename is None:
445
644
        stats.pprint()
446
645
    else:
447
 
        stats.freeze()
448
 
        cPickle.dump(stats, open(filename, 'w'), 2)
449
 
        print 'Profile data written to %r.' % filename
 
646
        stats.save(filename)
 
647
        trace.note('Profile data written to "%s".', filename)
450
648
    return ret
451
649
 
452
650
 
453
 
def get_alias(cmd):
454
 
    """Return an expanded alias, or None if no alias exists"""
455
 
    import bzrlib.config
456
 
    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)
457
670
    if (alias):
458
 
        return alias.split(' ')
 
671
        return shlex_split_unicode(alias)
459
672
    return None
460
673
 
461
674
 
490
703
 
491
704
    --lsprof
492
705
        Run under the Python lsprof profiler.
 
706
 
 
707
    --coverage
 
708
        Generate line coverage report in the specified directory.
493
709
    """
494
710
    argv = list(argv)
 
711
    trace.mutter("bzr arguments: %r", argv)
495
712
 
496
713
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
497
714
                opt_no_aliases = False
498
 
    opt_lsprof_file = None
 
715
    opt_lsprof_file = opt_coverage_dir = None
499
716
 
500
717
    # --no-plugins is handled specially at a very early stage. We need
501
718
    # to load plugins before doing other command parsing so that they
519
736
            opt_no_aliases = True
520
737
        elif a == '--builtin':
521
738
            opt_builtin = True
522
 
        elif a in ('--quiet', '-q'):
523
 
            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:])
524
744
        else:
525
745
            argv_copy.append(a)
526
746
        i += 1
532
752
        return 0
533
753
 
534
754
    if argv[0] == '--version':
535
 
        from bzrlib.version import show_version
536
 
        show_version()
 
755
        from bzrlib.builtins import cmd_version
 
756
        cmd_version().run_argv_aliases([])
537
757
        return 0
538
758
        
539
759
    if not opt_no_plugins:
557
777
    # 'command not found' error later.
558
778
 
559
779
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
560
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
561
 
        run = cmd_obj.run_argv
562
 
        run_argv = [argv]
563
 
    else:
564
 
        run = cmd_obj.run_argv_aliases
565
 
        run_argv = [argv, alias_argv]
 
780
    run = cmd_obj.run_argv_aliases
 
781
    run_argv = [argv, alias_argv]
566
782
 
567
783
    try:
568
784
        if opt_lsprof:
 
785
            if opt_coverage_dir:
 
786
                trace.warning(
 
787
                    '--coverage ignored, because --lsprof is in use.')
569
788
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
570
789
        elif opt_profile:
 
790
            if opt_coverage_dir:
 
791
                trace.warning(
 
792
                    '--coverage ignored, because --profile is in use.')
571
793
            ret = apply_profiled(run, *run_argv)
 
794
        elif opt_coverage_dir:
 
795
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
572
796
        else:
573
797
            ret = run(*run_argv)
574
798
        return ret or 0
575
799
    finally:
576
800
        # reset, in case we may do other commands later within the same process
577
 
        be_quiet(False)
 
801
        option._verbosity_level = 0
578
802
 
579
803
def display_command(func):
580
804
    """Decorator that suppresses pipe/interrupt errors."""
588
812
                raise
589
813
            if e.errno != errno.EPIPE:
590
814
                # Win32 raises IOError with errno=0 on a broken pipe
591
 
                if sys.platform != 'win32' or e.errno != 0:
 
815
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
592
816
                    raise
593
817
            pass
594
818
        except KeyboardInterrupt:
600
824
    import bzrlib.ui
601
825
    from bzrlib.ui.text import TextUIFactory
602
826
    bzrlib.ui.ui_factory = TextUIFactory()
603
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
827
     
 
828
    # Is this a final release version? If so, we should suppress warnings
 
829
    if bzrlib.version_info[3] == 'final':
 
830
        from bzrlib import symbol_versioning
 
831
        symbol_versioning.suppress_deprecation_warnings()
 
832
    try:
 
833
        argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
834
    except UnicodeDecodeError:
 
835
        raise errors.BzrError(("Parameter '%r' is unsupported by the current "
 
836
                                                            "encoding." % a))
604
837
    ret = run_bzr_catch_errors(argv)
605
 
    mutter("return code %d", ret)
 
838
    trace.mutter("return code %d", ret)
606
839
    return ret
607
840
 
608
841
 
609
842
def run_bzr_catch_errors(argv):
 
843
    # Note: The except clause logic below should be kept in sync with the
 
844
    # profile() routine in lsprof.py.
610
845
    try:
611
846
        return run_bzr(argv)
612
 
        # do this here inside the exception wrappers to catch EPIPE
613
 
        sys.stdout.flush()
614
847
    except (KeyboardInterrupt, Exception), e:
615
848
        # used to handle AssertionError and KeyboardInterrupt
616
849
        # specially here, but hopefully they're handled ok by the logger now
617
 
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
850
        exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
618
851
        if os.environ.get('BZR_PDB'):
619
852
            print '**** entering debugger'
620
853
            import pdb
621
854
            pdb.post_mortem(sys.exc_traceback)
622
 
        return 3
 
855
        return exitcode
 
856
 
 
857
 
 
858
def run_bzr_catch_user_errors(argv):
 
859
    """Run bzr and report user errors, but let internal errors propagate.
 
860
 
 
861
    This is used for the test suite, and might be useful for other programs
 
862
    that want to wrap the commandline interface.
 
863
    """
 
864
    try:
 
865
        return run_bzr(argv)
 
866
    except Exception, e:
 
867
        if (isinstance(e, (OSError, IOError))
 
868
            or not getattr(e, 'internal_error', True)):
 
869
            trace.report_exception(sys.exc_info(), sys.stderr)
 
870
            return 3
 
871
        else:
 
872
            raise
 
873
 
 
874
 
 
875
class HelpCommandIndex(object):
 
876
    """A index for bzr help that returns commands."""
 
877
 
 
878
    def __init__(self):
 
879
        self.prefix = 'commands/'
 
880
 
 
881
    def get_topics(self, topic):
 
882
        """Search for topic amongst commands.
 
883
 
 
884
        :param topic: A topic to search for.
 
885
        :return: A list which is either empty or contains a single
 
886
            Command entry.
 
887
        """
 
888
        if topic and topic.startswith(self.prefix):
 
889
            topic = topic[len(self.prefix):]
 
890
        try:
 
891
            cmd = _get_cmd_object(topic)
 
892
        except KeyError:
 
893
            return []
 
894
        else:
 
895
            return [cmd]
 
896
 
 
897
 
 
898
class Provider(object):
 
899
    '''Generic class to be overriden by plugins'''
 
900
 
 
901
    def plugin_for_command(self, cmd_name):
 
902
        '''Takes a command and returns the information for that plugin
 
903
        
 
904
        :return: A dictionary with all the available information 
 
905
        for the requested plugin
 
906
        '''
 
907
        raise NotImplementedError
 
908
 
 
909
 
 
910
class ProvidersRegistry(registry.Registry):
 
911
    '''This registry exists to allow other providers to exist'''
 
912
 
 
913
    def __iter__(self):
 
914
        for key, provider in self.iteritems():
 
915
            yield provider
 
916
 
 
917
command_providers_registry = ProvidersRegistry()
 
918
 
623
919
 
624
920
if __name__ == '__main__':
625
921
    sys.exit(main(sys.argv))