/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 breezy/commands.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
# TODO: Define arguments by objects, rather than just using names.
18
20
# Those objects can specify the expected type of the argument, which
19
21
# would help with validation and shell completion.  They could also provide
21
23
 
22
24
# TODO: Specific "examples" property on commands for consistent formatting.
23
25
 
24
 
import contextlib
25
26
import os
26
27
import sys
27
28
 
28
 
from . import (
29
 
    i18n,
30
 
    option,
31
 
    osutils,
32
 
    )
33
 
 
34
29
from .lazy_import import lazy_import
35
30
lazy_import(globals(), """
36
31
import errno
 
32
import threading
37
33
 
38
34
import breezy
39
35
from breezy import (
 
36
    config,
 
37
    cleanup,
40
38
    cmdline,
41
39
    debug,
 
40
    i18n,
 
41
    option,
 
42
    osutils,
42
43
    trace,
43
44
    ui,
44
45
    )
50
51
from .option import Option
51
52
from .plugin import disable_plugins, load_plugins, plugin_name
52
53
from . import errors, registry
53
 
 
54
 
 
55
 
class BzrOptionError(errors.CommandError):
 
54
from .sixish import (
 
55
    string_types,
 
56
    )
 
57
 
 
58
 
 
59
class BzrOptionError(errors.BzrCommandError):
56
60
 
57
61
    _fmt = "Error in command line options"
58
62
 
59
63
 
60
 
class CommandAvailableInPlugin(Exception):
61
 
 
62
 
    internal_error = False
63
 
 
64
 
    def __init__(self, cmd_name, plugin_metadata, provider):
65
 
 
66
 
        self.plugin_metadata = plugin_metadata
67
 
        self.cmd_name = cmd_name
68
 
        self.provider = provider
69
 
 
70
 
    def __str__(self):
71
 
 
72
 
        _fmt = ('"%s" is not a standard brz command. \n'
73
 
                'However, the following official plugin provides this command: %s\n'
74
 
                'You can install it by going to: %s'
75
 
                % (self.cmd_name, self.plugin_metadata['name'],
76
 
                    self.plugin_metadata['url']))
77
 
 
78
 
        return _fmt
79
 
 
80
 
 
81
64
class CommandInfo(object):
82
65
    """Information about a command."""
83
66
 
93
76
 
94
77
class CommandRegistry(registry.Registry):
95
78
    """Special registry mapping command names to command classes.
96
 
 
 
79
    
97
80
    :ivar overridden_registry: Look in this registry for commands being
98
81
        overridden by this registry.  This can be used to tell plugin commands
99
82
        about the builtin they're decorating.
142
125
        except KeyError:
143
126
            trace.warning('Two plugins defined the same command: %r' % k)
144
127
            trace.warning('Not loading the one in %r' %
145
 
                          sys.modules[cmd.__module__])
 
128
                sys.modules[cmd.__module__])
146
129
            trace.warning('Previously this command was registered from %r' %
147
 
                          sys.modules[previous.__module__])
 
130
                sys.modules[previous.__module__])
148
131
        for a in cmd.aliases:
149
132
            self._alias_dict[a] = k_unsquished
150
133
        return previous
171
154
def register_command(cmd, decorate=False):
172
155
    """Register a plugin command.
173
156
 
174
 
    Should generally be avoided in favor of lazy registration.
 
157
    Should generally be avoided in favor of lazy registration. 
175
158
    """
176
159
    global plugin_cmds
177
160
    return plugin_cmds.register(cmd, decorate)
182
165
 
183
166
 
184
167
def _unsquish_command_name(cmd):
185
 
    return cmd[4:].replace('_', '-')
 
168
    return cmd[4:].replace('_','-')
186
169
 
187
170
 
188
171
def _register_builtin_commands():
204
187
 
205
188
def _list_bzr_commands(names):
206
189
    """Find commands from bzr's core and plugins.
207
 
 
208
 
    This is not the public interface, just the default hook called by
209
 
    all_command_names.
 
190
    
 
191
    This is not the public interface, just the default hook called by all_command_names.
210
192
    """
211
193
    # to eliminate duplicates
212
194
    names.update(builtin_command_names())
227
209
 
228
210
def builtin_command_names():
229
211
    """Return list of builtin command names.
230
 
 
 
212
    
231
213
    Use of all_command_names() is encouraged rather than builtin_command_names
232
214
    and/or plugin_command_names.
233
215
    """
242
224
 
243
225
# Overrides for common mispellings that heuristics get wrong
244
226
_GUESS_OVERRIDES = {
245
 
    'ic': {'ci': 0},  # heuristic finds nick
 
227
    'ic': {'ci': 0}, # heuristic finds nick
246
228
    }
247
229
 
248
230
 
259
241
        names.update(cmd.aliases)
260
242
    # candidate: modified levenshtein distance against cmd_name.
261
243
    costs = {}
262
 
    import patiencediff
 
244
    from . import patiencediff
263
245
    for name in sorted(names):
264
246
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
265
247
        distance = 0.0
266
248
        opcodes = matcher.get_opcodes()
267
249
        for opcode, l1, l2, r1, r2 in opcodes:
268
250
            if opcode == 'delete':
269
 
                distance += l2 - l1
 
251
                distance += l2-l1
270
252
            elif opcode == 'replace':
271
 
                distance += max(l2 - l1, r2 - l1)
 
253
                distance += max(l2-l1, r2-l1)
272
254
            elif opcode == 'insert':
273
 
                distance += r2 - r1
 
255
                distance += r2-r1
274
256
            elif opcode == 'equal':
275
257
                # Score equal ranges lower, making similar commands of equal
276
258
                # length closer than arbitrary same length commands.
277
 
                distance -= 0.1 * (l2 - l1)
 
259
                distance -= 0.1 * (l2-l1)
278
260
        costs[name] = distance
279
261
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
280
 
    costs = sorted((costs[key], key) for key in costs)
 
262
    costs = sorted((value, key) for key, value in costs.iteritems())
281
263
    if not costs:
282
264
        return
283
265
    if costs[0][0] > 4:
298
280
        # No command found, see if this was a typo
299
281
        candidate = guess_command(cmd_name)
300
282
        if candidate is not None:
301
 
            raise errors.CommandError(
302
 
                gettext('unknown command "%s". Perhaps you meant "%s"')
303
 
                % (cmd_name, candidate))
304
 
        raise errors.CommandError(gettext('unknown command "%s"')
305
 
                                     % cmd_name)
 
283
            raise errors.BzrCommandError(
 
284
                    gettext('unknown command "%s". Perhaps you meant "%s"')
 
285
                    % (cmd_name, candidate))
 
286
        raise errors.BzrCommandError(gettext('unknown command "%s"')
 
287
                % cmd_name)
306
288
 
307
289
 
308
290
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
343
325
    return cmd
344
326
 
345
327
 
346
 
class NoPluginAvailable(errors.BzrError):
347
 
    pass
348
 
 
349
 
 
350
328
def _try_plugin_provider(cmd_name):
351
329
    """Probe for a plugin provider having cmd_name."""
352
330
    try:
353
331
        plugin_metadata, provider = probe_for_provider(cmd_name)
354
 
        raise CommandAvailableInPlugin(cmd_name, plugin_metadata, provider)
355
 
    except NoPluginAvailable:
 
332
        raise errors.CommandAvailableInPlugin(cmd_name,
 
333
            plugin_metadata, provider)
 
334
    except errors.NoPluginAvailable:
356
335
        pass
357
336
 
358
337
 
367
346
    for provider in command_providers_registry:
368
347
        try:
369
348
            return provider.plugin_for_command(cmd_name), provider
370
 
        except NoPluginAvailable:
 
349
        except errors.NoPluginAvailable:
371
350
            pass
372
 
    raise NoPluginAvailable(cmd_name)
 
351
    raise errors.NoPluginAvailable(cmd_name)
373
352
 
374
353
 
375
354
def _get_bzr_command(cmd_or_None, cmd_name):
491
470
 
492
471
        Functions will be called in LIFO order.
493
472
        """
494
 
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
 
473
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
495
474
 
496
475
    def cleanup_now(self):
497
476
        """Execute and empty pending cleanup functions immediately.
506
485
        as it releases all resources, this may release locks that the command
507
486
        wants to hold, so use should be done with care.
508
487
        """
509
 
        self._exit_stack.close()
510
 
 
511
 
    def enter_context(self, cm):
512
 
        return self._exit_stack.enter_context(cm)
 
488
        self._operation.cleanup_now()
513
489
 
514
490
    def _usage(self):
515
491
        """Return single-line grammar for this command.
558
534
            doc = gettext("No help for this command.")
559
535
 
560
536
        # Extract the summary (purpose) and sections out from the text
561
 
        purpose, sections, order = self._get_help_parts(doc)
 
537
        purpose,sections,order = self._get_help_parts(doc)
562
538
 
563
539
        # If a custom usage section was provided, use it
564
540
        if 'Usage' in sections:
580
556
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
581
557
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
582
558
        # 20090319
583
 
        parser = option.get_optparser(
584
 
            [v for k, v in sorted(self.options().items())])
 
559
        parser = option.get_optparser(self.options())
585
560
        options = parser.format_option_help()
586
561
        # FIXME: According to the spec, ReST option lists actually don't
587
562
        # support options like --1.14 so that causes syntax errors (in Sphinx
614
589
                result += '\n'
615
590
        else:
616
591
            result += (gettext("See brz help %s for more details and examples.\n\n")
617
 
                       % self.name())
 
592
                % self.name())
618
593
 
619
594
        # Add the aliases, source (plug-in) and see also links, if any
620
595
        if self.aliases:
635
610
                    else:
636
611
                        # Use a Sphinx link for this entry
637
612
                        link_text = gettext(":doc:`{0} <{1}-help>`").format(
638
 
                            item, item)
 
613
                                                                    item, item)
639
614
                        see_also_links.append(link_text)
640
615
                see_also = see_also_links
641
616
            result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
670
645
        summary = lines.pop(0)
671
646
        sections = {}
672
647
        order = []
673
 
        label, section = None, ''
 
648
        label,section = None,''
674
649
        for line in lines:
675
650
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
676
651
                save_section(sections, order, label, section)
677
 
                label, section = line[1:-1], ''
678
 
            elif (label is not None and len(line) > 1 and
679
 
                    not line[0].isspace()):
 
652
                label,section = line[1:-1],''
 
653
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
680
654
                save_section(sections, order, label, section)
681
 
                label, section = None, line
 
655
                label,section = None,line
682
656
            else:
683
657
                if len(section) > 0:
684
658
                    section += '\n' + line
711
685
        r = Option.STD_OPTIONS.copy()
712
686
        std_names = set(r)
713
687
        for o in self.takes_options:
714
 
            if isinstance(o, str):
 
688
            if isinstance(o, string_types):
715
689
                o = option.Option.OPTIONS[o]
716
690
            r[o.name] = o
717
691
            if o.name in std_names:
773
747
        you can override this method.
774
748
        """
775
749
        class_run = self.run
776
 
 
777
750
        def run(*args, **kwargs):
778
751
            for hook in Command.hooks['pre_command']:
779
752
                hook(self)
 
753
            self._operation = cleanup.OperationWithCleanups(class_run)
780
754
            try:
781
 
                with contextlib.ExitStack() as self._exit_stack:
782
 
                    return class_run(*args, **kwargs)
 
755
                return self._operation.run_simple(*args, **kwargs)
783
756
            finally:
 
757
                del self._operation
784
758
                for hook in Command.hooks['post_command']:
785
759
                    hook(self)
786
760
        self.run = run
795
769
        shell error code if not.  It's OK for this method to allow
796
770
        an exception to raise up.
797
771
 
798
 
        This method is automatically wrapped by Command.__init__ with a
799
 
        ExitStack, stored as self._exit_stack. This can be used
 
772
        This method is automatically wrapped by Command.__init__ with a 
 
773
        cleanup operation, stored as self._operation. This can be used
800
774
        via self.add_cleanup to perform automatic cleanups at the end of
801
775
        run().
802
776
 
827
801
    def name(self):
828
802
        """Return the canonical name for this command.
829
803
 
830
 
        The name under which it was actually invoked is available in invoked_as
 
804
        The name under which it was actually invoked is available in invoked_as.
831
805
        """
832
806
        return _unsquish_command_name(self.__class__.__name__)
833
807
 
849
823
        notified.
850
824
        """
851
825
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
852
 
        self.add_hook(
853
 
            'extend_command',
 
826
        self.add_hook('extend_command',
854
827
            "Called after creating a command object to allow modifications "
855
828
            "such as adding or removing options, docs etc. Called with the "
856
829
            "new breezy.commands.Command object.", (1, 13))
857
 
        self.add_hook(
858
 
            'get_command',
 
830
        self.add_hook('get_command',
859
831
            "Called when creating a single command. Called with "
860
832
            "(cmd_or_None, command_name). get_command should either return "
861
833
            "the cmd_or_None parameter, or a replacement Command object that "
863
835
            "hooks are core infrastructure. Many users will prefer to use "
864
836
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
865
837
            (1, 17))
866
 
        self.add_hook(
867
 
            'get_missing_command',
 
838
        self.add_hook('get_missing_command',
868
839
            "Called when creating a single command if no command could be "
869
840
            "found. Called with (command_name). get_missing_command should "
870
841
            "either return None, or a Command object to be used for the "
871
842
            "command.", (1, 17))
872
 
        self.add_hook(
873
 
            'list_commands',
 
843
        self.add_hook('list_commands',
874
844
            "Called when enumerating commands. Called with a set of "
875
845
            "cmd_name strings for all the commands found so far. This set "
876
846
            " is safe to mutate - e.g. to remove a command. "
877
847
            "list_commands should return the updated set of command names.",
878
848
            (1, 17))
879
 
        self.add_hook(
880
 
            'pre_command',
 
849
        self.add_hook('pre_command',
881
850
            "Called prior to executing a command. Called with the command "
882
851
            "object.", (2, 6))
883
 
        self.add_hook(
884
 
            'post_command',
 
852
        self.add_hook('post_command',
885
853
            "Called after executing a command. Called with the command "
886
854
            "object.", (2, 6))
887
855
 
888
 
 
889
856
Command.hooks = CommandHooks()
890
857
 
891
858
 
898
865
    they take, and which commands will accept them.
899
866
    """
900
867
    # TODO: make it a method of the Command?
901
 
    parser = option.get_optparser(
902
 
        [v for k, v in sorted(command.options().items())])
 
868
    parser = option.get_optparser(command.options())
903
869
    if alias_argv is not None:
904
870
        args = alias_argv + argv
905
871
    else:
906
872
        args = argv
907
873
 
908
 
    # python 2's optparse raises this exception if a non-ascii
 
874
    # for python 2.5 and later, optparse raises this exception if a non-ascii
909
875
    # option name is given.  See http://bugs.python.org/issue2931
910
876
    try:
911
877
        options, args = parser.parse_args(args)
912
 
    except UnicodeEncodeError:
913
 
        raise errors.CommandError(
 
878
    except UnicodeEncodeError as e:
 
879
        raise errors.BzrCommandError(
914
880
            gettext('Only ASCII permitted in option names'))
915
881
 
916
882
    opts = dict((k, v) for k, v in options.__dict__.items() if
927
893
        if ap[-1] == '?':
928
894
            if args:
929
895
                argdict[argname] = args.pop(0)
930
 
        elif ap[-1] == '*':  # all remaining arguments
 
896
        elif ap[-1] == '*': # all remaining arguments
931
897
            if args:
932
898
                argdict[argname + '_list'] = args[:]
933
899
                args = []
935
901
                argdict[argname + '_list'] = None
936
902
        elif ap[-1] == '+':
937
903
            if not args:
938
 
                raise errors.CommandError(gettext(
939
 
                    "command {0!r} needs one or more {1}").format(
940
 
                    cmd, argname.upper()))
 
904
                raise errors.BzrCommandError(gettext(
 
905
                      "command {0!r} needs one or more {1}").format(
 
906
                      cmd, argname.upper()))
941
907
            else:
942
908
                argdict[argname + '_list'] = args[:]
943
909
                args = []
944
 
        elif ap[-1] == '$':  # all but one
 
910
        elif ap[-1] == '$': # all but one
945
911
            if len(args) < 2:
946
 
                raise errors.CommandError(
947
 
                    gettext("command {0!r} needs one or more {1}").format(
948
 
                        cmd, argname.upper()))
 
912
                raise errors.BzrCommandError(
 
913
                      gettext("command {0!r} needs one or more {1}").format(
 
914
                                             cmd, argname.upper()))
949
915
            argdict[argname + '_list'] = args[:-1]
950
916
            args[:-1] = []
951
917
        else:
952
918
            # just a plain arg
953
919
            argname = ap
954
920
            if not args:
955
 
                raise errors.CommandError(
956
 
                    gettext("command {0!r} requires argument {1}").format(
957
 
                        cmd, argname.upper()))
 
921
                raise errors.BzrCommandError(
 
922
                     gettext("command {0!r} requires argument {1}").format(
 
923
                               cmd, argname.upper()))
958
924
            else:
959
925
                argdict[argname] = args.pop(0)
960
926
 
961
927
    if args:
962
 
        raise errors.CommandError(gettext(
963
 
            "extra argument to command {0}: {1}").format(
964
 
            cmd, args[0]))
 
928
        raise errors.BzrCommandError( gettext(
 
929
                              "extra argument to command {0}: {1}").format(
 
930
                                       cmd, args[0]) )
965
931
 
966
932
    return argdict
967
933
 
968
 
 
969
 
def apply_coveraged(the_callable, *args, **kwargs):
970
 
    import coverage
971
 
    cov = coverage.Coverage()
972
 
    try:
973
 
        config_file = cov.config.config_file
974
 
    except AttributeError:  # older versions of coverage
975
 
        config_file = cov.config_file
976
 
    os.environ['COVERAGE_PROCESS_START'] = config_file
977
 
    cov.start()
 
934
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
935
    # Cannot use "import trace", as that would import breezy.trace instead of
 
936
    # the standard library's trace.
 
937
    trace = __import__('trace')
 
938
 
 
939
    tracer = trace.Trace(count=1, trace=0)
 
940
    sys.settrace(tracer.globaltrace)
 
941
    threading.settrace(tracer.globaltrace)
 
942
 
978
943
    try:
979
944
        return exception_to_return_code(the_callable, *args, **kwargs)
980
945
    finally:
981
 
        cov.stop()
982
 
        cov.save()
 
946
        sys.settrace(None)
 
947
        results = tracer.results()
 
948
        results.write_results(show_missing=1, summary=False,
 
949
                              coverdir=dirname)
983
950
 
984
951
 
985
952
def apply_profiled(the_callable, *args, **kwargs):
991
958
        prof = hotshot.Profile(pfname)
992
959
        try:
993
960
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
994
 
                               **kwargs) or 0
 
961
                **kwargs) or 0
995
962
        finally:
996
963
            prof.close()
997
964
        stats = hotshot.stats.load(pfname)
998
965
        stats.strip_dirs()
999
966
        stats.sort_stats('cum')   # 'time'
1000
 
        # XXX: Might like to write to stderr or the trace file instead but
1001
 
        # print_stats seems hardcoded to stdout
 
967
        ## XXX: Might like to write to stderr or the trace file instead but
 
968
        ## print_stats seems hardcoded to stdout
1002
969
        stats.print_stats(20)
1003
970
        return ret
1004
971
    finally:
1015
982
    """
1016
983
    try:
1017
984
        return the_callable(*args, **kwargs)
1018
 
    except (KeyboardInterrupt, Exception):
 
985
    except (KeyboardInterrupt, Exception) as e:
1019
986
        # used to handle AssertionError and KeyboardInterrupt
1020
987
        # specially here, but hopefully they're handled ok by the logger now
1021
988
        exc_info = sys.exc_info()
1095
1062
        Run under the Python lsprof profiler.
1096
1063
 
1097
1064
    --coverage
1098
 
        Generate code coverage report
 
1065
        Generate line coverage report in the specified directory.
1099
1066
 
1100
1067
    --concurrency
1101
 
        Specify the number of processes that can be run concurrently
1102
 
        (selftest).
 
1068
        Specify the number of processes that can be run concurrently (selftest).
1103
1069
    """
1104
1070
    trace.mutter("breezy version: " + breezy.__version__)
1105
1071
    argv = _specified_or_unicode_argv(argv)
1106
1072
    trace.mutter("brz arguments: %r", argv)
1107
1073
 
1108
1074
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1109
 
        opt_coverage = opt_no_l10n = opt_no_aliases = False
1110
 
    opt_lsprof_file = None
 
1075
            opt_no_l10n = opt_no_aliases = False
 
1076
    opt_lsprof_file = opt_coverage_dir = None
1111
1077
 
1112
1078
    # --no-plugins is handled specially at a very early stage. We need
1113
1079
    # to load plugins before doing other command parsing so that they
1138
1104
            os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1139
1105
            i += 1
1140
1106
        elif a == '--coverage':
1141
 
            opt_coverage = True
 
1107
            opt_coverage_dir = argv[i + 1]
 
1108
            i += 1
1142
1109
        elif a == '--profile-imports':
1143
 
            pass  # already handled in startup script Bug #588277
 
1110
            pass # already handled in startup script Bug #588277
1144
1111
        elif a.startswith('-D'):
1145
1112
            debug.debug_flags.add(a[2:])
1146
1113
        elif a.startswith('-O'):
1149
1116
            argv_copy.append(a)
1150
1117
        i += 1
1151
1118
 
1152
 
    cmdline_overrides = breezy.get_global_state().cmdline_overrides
 
1119
    if breezy.global_state is None:
 
1120
        # FIXME: Workaround for users that imported breezy but didn't call
 
1121
        # breezy.initialize -- vila 2012-01-19
 
1122
        cmdline_overrides = config.CommandLineStore()
 
1123
    else:
 
1124
        cmdline_overrides = breezy.global_state.cmdline_overrides
1153
1125
    cmdline_overrides._from_cmdline(override_config)
1154
1126
 
1155
1127
    debug.set_debug_flags_from_config()
1156
1128
 
1157
1129
    if not opt_no_plugins:
1158
 
        from breezy import config
1159
 
        c = config.GlobalConfig()
1160
 
        warn_load_problems = not c.suppress_warning('plugin_load_failure')
1161
 
        load_plugins(warn_load_problems=warn_load_problems)
 
1130
        load_plugins()
1162
1131
    else:
1163
1132
        disable_plugins()
1164
1133
 
1191
1160
        saved_verbosity_level = option._verbosity_level
1192
1161
        option._verbosity_level = 0
1193
1162
        if opt_lsprof:
1194
 
            if opt_coverage:
 
1163
            if opt_coverage_dir:
1195
1164
                trace.warning(
1196
1165
                    '--coverage ignored, because --lsprof is in use.')
1197
1166
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1198
1167
        elif opt_profile:
1199
 
            if opt_coverage:
 
1168
            if opt_coverage_dir:
1200
1169
                trace.warning(
1201
1170
                    '--coverage ignored, because --profile is in use.')
1202
1171
            ret = apply_profiled(run, *run_argv)
1203
 
        elif opt_coverage:
1204
 
            ret = apply_coveraged(run, *run_argv)
 
1172
        elif opt_coverage_dir:
 
1173
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1205
1174
        else:
1206
1175
            ret = run(*run_argv)
1207
1176
        return ret or 0
1212
1181
        if 'memory' in debug.debug_flags:
1213
1182
            trace.debug_memory('Process status after command:', short=False)
1214
1183
        option._verbosity_level = saved_verbosity_level
1215
 
        # Reset the overrides
 
1184
        # Reset the overrides 
1216
1185
        cmdline_overrides._reset()
1217
1186
 
1218
1187
 
1241
1210
    if _list_bzr_commands in Command.hooks["list_commands"]:
1242
1211
        return
1243
1212
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1244
 
                                     "bzr commands")
 
1213
        "bzr commands")
1245
1214
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1246
 
                                     "bzr commands")
 
1215
        "bzr commands")
1247
1216
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1248
 
                                     "bzr plugin commands")
 
1217
        "bzr plugin commands")
1249
1218
    Command.hooks.install_named_hook("get_command", _get_external_command,
1250
 
                                     "bzr external command lookup")
 
1219
        "bzr external command lookup")
1251
1220
    Command.hooks.install_named_hook("get_missing_command",
1252
1221
                                     _try_plugin_provider,
1253
1222
                                     "bzr plugin-provider-db check")
1254
1223
 
1255
1224
 
 
1225
 
1256
1226
def _specified_or_unicode_argv(argv):
1257
1227
    # For internal or testing use, argv can be passed.  Otherwise, get it from
1258
 
    # the process arguments.
 
1228
    # the process arguments in a unicode-safe way.
1259
1229
    if argv is None:
1260
 
        return sys.argv[1:]
1261
 
    new_argv = []
1262
 
    try:
1263
 
        # ensure all arguments are unicode strings
1264
 
        for a in argv:
1265
 
            if not isinstance(a, str):
1266
 
                raise ValueError('not native str or unicode: %r' % (a,))
1267
 
            new_argv.append(a)
1268
 
    except (ValueError, UnicodeDecodeError):
1269
 
        raise errors.BzrError("argv should be list of unicode strings.")
1270
 
    return new_argv
 
1230
        return osutils.get_unicode_argv()
 
1231
    else:
 
1232
        new_argv = []
 
1233
        try:
 
1234
            # ensure all arguments are unicode strings
 
1235
            for a in argv:
 
1236
                if isinstance(a, unicode):
 
1237
                    new_argv.append(a)
 
1238
                else:
 
1239
                    new_argv.append(a.decode('ascii'))
 
1240
        except UnicodeDecodeError:
 
1241
            raise errors.BzrError("argv should be list of unicode strings.")
 
1242
        return new_argv
1271
1243
 
1272
1244
 
1273
1245
def main(argv=None):
1294
1266
    """Run a bzr command with parameters as described by argv.
1295
1267
 
1296
1268
    This function assumed that that UI layer is setup, that symbol deprecations
1297
 
    are already applied, and that unicode decoding has already been performed
1298
 
    on argv.
 
1269
    are already applied, and that unicode decoding has already been performed on argv.
1299
1270
    """
1300
1271
    # done here so that they're covered for every test run
1301
1272
    install_bzr_command_hooks()
1314
1285
        return run_bzr(argv)
1315
1286
    except Exception as e:
1316
1287
        if (isinstance(e, (OSError, IOError))
1317
 
                or not getattr(e, 'internal_error', True)):
 
1288
            or not getattr(e, 'internal_error', True)):
1318
1289
            trace.report_exception(sys.exc_info(), sys.stderr)
1319
1290
            return 3
1320
1291
        else:
1363
1334
        for key, provider in self.items():
1364
1335
            yield provider
1365
1336
 
1366
 
 
1367
1337
command_providers_registry = ProvidersRegistry()