/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-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

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
    errors,
 
41
    i18n,
 
42
    option,
 
43
    osutils,
42
44
    trace,
43
45
    ui,
44
46
    )
49
51
# Compatibility - Option used to be in commands.
50
52
from .option import Option
51
53
from .plugin import disable_plugins, load_plugins, plugin_name
52
 
from . import errors, registry
53
 
 
54
 
 
55
 
class BzrOptionError(errors.CommandError):
56
 
 
57
 
    _fmt = "Error in command line options"
58
 
 
59
 
 
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
 
54
from . import registry
 
55
from .sixish import (
 
56
    string_types,
 
57
    )
79
58
 
80
59
 
81
60
class CommandInfo(object):
93
72
 
94
73
class CommandRegistry(registry.Registry):
95
74
    """Special registry mapping command names to command classes.
96
 
 
 
75
    
97
76
    :ivar overridden_registry: Look in this registry for commands being
98
77
        overridden by this registry.  This can be used to tell plugin commands
99
78
        about the builtin they're decorating.
142
121
        except KeyError:
143
122
            trace.warning('Two plugins defined the same command: %r' % k)
144
123
            trace.warning('Not loading the one in %r' %
145
 
                          sys.modules[cmd.__module__])
 
124
                sys.modules[cmd.__module__])
146
125
            trace.warning('Previously this command was registered from %r' %
147
 
                          sys.modules[previous.__module__])
 
126
                sys.modules[previous.__module__])
148
127
        for a in cmd.aliases:
149
128
            self._alias_dict[a] = k_unsquished
150
129
        return previous
171
150
def register_command(cmd, decorate=False):
172
151
    """Register a plugin command.
173
152
 
174
 
    Should generally be avoided in favor of lazy registration.
 
153
    Should generally be avoided in favor of lazy registration. 
175
154
    """
176
155
    global plugin_cmds
177
156
    return plugin_cmds.register(cmd, decorate)
182
161
 
183
162
 
184
163
def _unsquish_command_name(cmd):
185
 
    return cmd[4:].replace('_', '-')
 
164
    return cmd[4:].replace('_','-')
186
165
 
187
166
 
188
167
def _register_builtin_commands():
204
183
 
205
184
def _list_bzr_commands(names):
206
185
    """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.
 
186
    
 
187
    This is not the public interface, just the default hook called by all_command_names.
210
188
    """
211
189
    # to eliminate duplicates
212
190
    names.update(builtin_command_names())
227
205
 
228
206
def builtin_command_names():
229
207
    """Return list of builtin command names.
230
 
 
 
208
    
231
209
    Use of all_command_names() is encouraged rather than builtin_command_names
232
210
    and/or plugin_command_names.
233
211
    """
240
218
    return plugin_cmds.keys()
241
219
 
242
220
 
243
 
# Overrides for common mispellings that heuristics get wrong
244
 
_GUESS_OVERRIDES = {
245
 
    'ic': {'ci': 0},  # heuristic finds nick
246
 
    }
247
 
 
248
 
 
249
 
def guess_command(cmd_name):
250
 
    """Guess what command a user typoed.
251
 
 
252
 
    :param cmd_name: Command to search for
253
 
    :return: None if no command was found, name of a command otherwise
254
 
    """
255
 
    names = set()
256
 
    for name in all_command_names():
257
 
        names.add(name)
258
 
        cmd = get_cmd_object(name)
259
 
        names.update(cmd.aliases)
260
 
    # candidate: modified levenshtein distance against cmd_name.
261
 
    costs = {}
262
 
    import patiencediff
263
 
    for name in sorted(names):
264
 
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
265
 
        distance = 0.0
266
 
        opcodes = matcher.get_opcodes()
267
 
        for opcode, l1, l2, r1, r2 in opcodes:
268
 
            if opcode == 'delete':
269
 
                distance += l2 - l1
270
 
            elif opcode == 'replace':
271
 
                distance += max(l2 - l1, r2 - l1)
272
 
            elif opcode == 'insert':
273
 
                distance += r2 - r1
274
 
            elif opcode == 'equal':
275
 
                # Score equal ranges lower, making similar commands of equal
276
 
                # length closer than arbitrary same length commands.
277
 
                distance -= 0.1 * (l2 - l1)
278
 
        costs[name] = distance
279
 
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
280
 
    costs = sorted((costs[key], key) for key in costs)
281
 
    if not costs:
282
 
        return
283
 
    if costs[0][0] > 4:
284
 
        return
285
 
    candidate = costs[0][1]
286
 
    return candidate
287
 
 
288
 
 
289
221
def get_cmd_object(cmd_name, plugins_override=True):
290
222
    """Return the command object for a command.
291
223
 
295
227
    try:
296
228
        return _get_cmd_object(cmd_name, plugins_override)
297
229
    except KeyError:
298
 
        # No command found, see if this was a typo
299
 
        candidate = guess_command(cmd_name)
300
 
        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)
 
230
        raise errors.BzrCommandError(gettext('unknown command "%s"') % cmd_name)
306
231
 
307
232
 
308
233
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
343
268
    return cmd
344
269
 
345
270
 
346
 
class NoPluginAvailable(errors.BzrError):
347
 
    pass
348
 
 
349
 
 
350
271
def _try_plugin_provider(cmd_name):
351
272
    """Probe for a plugin provider having cmd_name."""
352
273
    try:
353
274
        plugin_metadata, provider = probe_for_provider(cmd_name)
354
 
        raise CommandAvailableInPlugin(cmd_name, plugin_metadata, provider)
355
 
    except NoPluginAvailable:
 
275
        raise errors.CommandAvailableInPlugin(cmd_name,
 
276
            plugin_metadata, provider)
 
277
    except errors.NoPluginAvailable:
356
278
        pass
357
279
 
358
280
 
367
289
    for provider in command_providers_registry:
368
290
        try:
369
291
            return provider.plugin_for_command(cmd_name), provider
370
 
        except NoPluginAvailable:
 
292
        except errors.NoPluginAvailable:
371
293
            pass
372
 
    raise NoPluginAvailable(cmd_name)
 
294
    raise errors.NoPluginAvailable(cmd_name)
373
295
 
374
296
 
375
297
def _get_bzr_command(cmd_or_None, cmd_name):
491
413
 
492
414
        Functions will be called in LIFO order.
493
415
        """
494
 
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
 
416
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
495
417
 
496
418
    def cleanup_now(self):
497
419
        """Execute and empty pending cleanup functions immediately.
506
428
        as it releases all resources, this may release locks that the command
507
429
        wants to hold, so use should be done with care.
508
430
        """
509
 
        self._exit_stack.close()
510
 
 
511
 
    def enter_context(self, cm):
512
 
        return self._exit_stack.enter_context(cm)
 
431
        self._operation.cleanup_now()
513
432
 
514
433
    def _usage(self):
515
434
        """Return single-line grammar for this command.
558
477
            doc = gettext("No help for this command.")
559
478
 
560
479
        # Extract the summary (purpose) and sections out from the text
561
 
        purpose, sections, order = self._get_help_parts(doc)
 
480
        purpose,sections,order = self._get_help_parts(doc)
562
481
 
563
482
        # If a custom usage section was provided, use it
564
483
        if 'Usage' in sections:
580
499
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
581
500
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
582
501
        # 20090319
583
 
        parser = option.get_optparser(
584
 
            [v for k, v in sorted(self.options().items())])
 
502
        parser = option.get_optparser(self.options())
585
503
        options = parser.format_option_help()
586
504
        # FIXME: According to the spec, ReST option lists actually don't
587
505
        # support options like --1.14 so that causes syntax errors (in Sphinx
614
532
                result += '\n'
615
533
        else:
616
534
            result += (gettext("See brz help %s for more details and examples.\n\n")
617
 
                       % self.name())
 
535
                % self.name())
618
536
 
619
537
        # Add the aliases, source (plug-in) and see also links, if any
620
538
        if self.aliases:
635
553
                    else:
636
554
                        # Use a Sphinx link for this entry
637
555
                        link_text = gettext(":doc:`{0} <{1}-help>`").format(
638
 
                            item, item)
 
556
                                                                    item, item)
639
557
                        see_also_links.append(link_text)
640
558
                see_also = see_also_links
641
559
            result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
670
588
        summary = lines.pop(0)
671
589
        sections = {}
672
590
        order = []
673
 
        label, section = None, ''
 
591
        label,section = None,''
674
592
        for line in lines:
675
593
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
676
594
                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()):
 
595
                label,section = line[1:-1],''
 
596
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
680
597
                save_section(sections, order, label, section)
681
 
                label, section = None, line
 
598
                label,section = None,line
682
599
            else:
683
600
                if len(section) > 0:
684
601
                    section += '\n' + line
711
628
        r = Option.STD_OPTIONS.copy()
712
629
        std_names = set(r)
713
630
        for o in self.takes_options:
714
 
            if isinstance(o, str):
 
631
            if isinstance(o, string_types):
715
632
                o = option.Option.OPTIONS[o]
716
633
            r[o.name] = o
717
634
            if o.name in std_names:
773
690
        you can override this method.
774
691
        """
775
692
        class_run = self.run
776
 
 
777
693
        def run(*args, **kwargs):
778
694
            for hook in Command.hooks['pre_command']:
779
695
                hook(self)
 
696
            self._operation = cleanup.OperationWithCleanups(class_run)
780
697
            try:
781
 
                with contextlib.ExitStack() as self._exit_stack:
782
 
                    return class_run(*args, **kwargs)
 
698
                return self._operation.run_simple(*args, **kwargs)
783
699
            finally:
 
700
                del self._operation
784
701
                for hook in Command.hooks['post_command']:
785
702
                    hook(self)
786
703
        self.run = run
795
712
        shell error code if not.  It's OK for this method to allow
796
713
        an exception to raise up.
797
714
 
798
 
        This method is automatically wrapped by Command.__init__ with a
799
 
        ExitStack, stored as self._exit_stack. This can be used
 
715
        This method is automatically wrapped by Command.__init__ with a 
 
716
        cleanup operation, stored as self._operation. This can be used
800
717
        via self.add_cleanup to perform automatic cleanups at the end of
801
718
        run().
802
719
 
827
744
    def name(self):
828
745
        """Return the canonical name for this command.
829
746
 
830
 
        The name under which it was actually invoked is available in invoked_as
 
747
        The name under which it was actually invoked is available in invoked_as.
831
748
        """
832
749
        return _unsquish_command_name(self.__class__.__name__)
833
750
 
849
766
        notified.
850
767
        """
851
768
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
852
 
        self.add_hook(
853
 
            'extend_command',
 
769
        self.add_hook('extend_command',
854
770
            "Called after creating a command object to allow modifications "
855
771
            "such as adding or removing options, docs etc. Called with the "
856
772
            "new breezy.commands.Command object.", (1, 13))
857
 
        self.add_hook(
858
 
            'get_command',
 
773
        self.add_hook('get_command',
859
774
            "Called when creating a single command. Called with "
860
775
            "(cmd_or_None, command_name). get_command should either return "
861
776
            "the cmd_or_None parameter, or a replacement Command object that "
863
778
            "hooks are core infrastructure. Many users will prefer to use "
864
779
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
865
780
            (1, 17))
866
 
        self.add_hook(
867
 
            'get_missing_command',
 
781
        self.add_hook('get_missing_command',
868
782
            "Called when creating a single command if no command could be "
869
783
            "found. Called with (command_name). get_missing_command should "
870
784
            "either return None, or a Command object to be used for the "
871
785
            "command.", (1, 17))
872
 
        self.add_hook(
873
 
            'list_commands',
 
786
        self.add_hook('list_commands',
874
787
            "Called when enumerating commands. Called with a set of "
875
788
            "cmd_name strings for all the commands found so far. This set "
876
789
            " is safe to mutate - e.g. to remove a command. "
877
790
            "list_commands should return the updated set of command names.",
878
791
            (1, 17))
879
 
        self.add_hook(
880
 
            'pre_command',
 
792
        self.add_hook('pre_command',
881
793
            "Called prior to executing a command. Called with the command "
882
794
            "object.", (2, 6))
883
 
        self.add_hook(
884
 
            'post_command',
 
795
        self.add_hook('post_command',
885
796
            "Called after executing a command. Called with the command "
886
797
            "object.", (2, 6))
887
798
 
888
 
 
889
799
Command.hooks = CommandHooks()
890
800
 
891
801
 
898
808
    they take, and which commands will accept them.
899
809
    """
900
810
    # TODO: make it a method of the Command?
901
 
    parser = option.get_optparser(
902
 
        [v for k, v in sorted(command.options().items())])
 
811
    parser = option.get_optparser(command.options())
903
812
    if alias_argv is not None:
904
813
        args = alias_argv + argv
905
814
    else:
906
815
        args = argv
907
816
 
908
 
    # python 2's optparse raises this exception if a non-ascii
 
817
    # for python 2.5 and later, optparse raises this exception if a non-ascii
909
818
    # option name is given.  See http://bugs.python.org/issue2931
910
819
    try:
911
820
        options, args = parser.parse_args(args)
912
 
    except UnicodeEncodeError:
913
 
        raise errors.CommandError(
 
821
    except UnicodeEncodeError as e:
 
822
        raise errors.BzrCommandError(
914
823
            gettext('Only ASCII permitted in option names'))
915
824
 
916
825
    opts = dict((k, v) for k, v in options.__dict__.items() if
927
836
        if ap[-1] == '?':
928
837
            if args:
929
838
                argdict[argname] = args.pop(0)
930
 
        elif ap[-1] == '*':  # all remaining arguments
 
839
        elif ap[-1] == '*': # all remaining arguments
931
840
            if args:
932
841
                argdict[argname + '_list'] = args[:]
933
842
                args = []
935
844
                argdict[argname + '_list'] = None
936
845
        elif ap[-1] == '+':
937
846
            if not args:
938
 
                raise errors.CommandError(gettext(
939
 
                    "command {0!r} needs one or more {1}").format(
940
 
                    cmd, argname.upper()))
 
847
                raise errors.BzrCommandError(gettext(
 
848
                      "command {0!r} needs one or more {1}").format(
 
849
                      cmd, argname.upper()))
941
850
            else:
942
851
                argdict[argname + '_list'] = args[:]
943
852
                args = []
944
 
        elif ap[-1] == '$':  # all but one
 
853
        elif ap[-1] == '$': # all but one
945
854
            if len(args) < 2:
946
 
                raise errors.CommandError(
947
 
                    gettext("command {0!r} needs one or more {1}").format(
948
 
                        cmd, argname.upper()))
 
855
                raise errors.BzrCommandError(
 
856
                      gettext("command {0!r} needs one or more {1}").format(
 
857
                                             cmd, argname.upper()))
949
858
            argdict[argname + '_list'] = args[:-1]
950
859
            args[:-1] = []
951
860
        else:
952
861
            # just a plain arg
953
862
            argname = ap
954
863
            if not args:
955
 
                raise errors.CommandError(
956
 
                    gettext("command {0!r} requires argument {1}").format(
957
 
                        cmd, argname.upper()))
 
864
                raise errors.BzrCommandError(
 
865
                     gettext("command {0!r} requires argument {1}").format(
 
866
                               cmd, argname.upper()))
958
867
            else:
959
868
                argdict[argname] = args.pop(0)
960
869
 
961
870
    if args:
962
 
        raise errors.CommandError(gettext(
963
 
            "extra argument to command {0}: {1}").format(
964
 
            cmd, args[0]))
 
871
        raise errors.BzrCommandError( gettext(
 
872
                              "extra argument to command {0}: {1}").format(
 
873
                                       cmd, args[0]) )
965
874
 
966
875
    return argdict
967
876
 
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()
 
877
def apply_coveraged(dirname, the_callable, *args, **kwargs):
 
878
    # Cannot use "import trace", as that would import breezy.trace instead of
 
879
    # the standard library's trace.
 
880
    trace = __import__('trace')
 
881
 
 
882
    tracer = trace.Trace(count=1, trace=0)
 
883
    sys.settrace(tracer.globaltrace)
 
884
    threading.settrace(tracer.globaltrace)
 
885
 
978
886
    try:
979
887
        return exception_to_return_code(the_callable, *args, **kwargs)
980
888
    finally:
981
 
        cov.stop()
982
 
        cov.save()
 
889
        sys.settrace(None)
 
890
        results = tracer.results()
 
891
        results.write_results(show_missing=1, summary=False,
 
892
                              coverdir=dirname)
983
893
 
984
894
 
985
895
def apply_profiled(the_callable, *args, **kwargs):
991
901
        prof = hotshot.Profile(pfname)
992
902
        try:
993
903
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
994
 
                               **kwargs) or 0
 
904
                **kwargs) or 0
995
905
        finally:
996
906
            prof.close()
997
907
        stats = hotshot.stats.load(pfname)
998
908
        stats.strip_dirs()
999
909
        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
 
910
        ## XXX: Might like to write to stderr or the trace file instead but
 
911
        ## print_stats seems hardcoded to stdout
1002
912
        stats.print_stats(20)
1003
913
        return ret
1004
914
    finally:
1015
925
    """
1016
926
    try:
1017
927
        return the_callable(*args, **kwargs)
1018
 
    except (KeyboardInterrupt, Exception):
 
928
    except (KeyboardInterrupt, Exception) as e:
1019
929
        # used to handle AssertionError and KeyboardInterrupt
1020
930
        # specially here, but hopefully they're handled ok by the logger now
1021
931
        exc_info = sys.exc_info()
1095
1005
        Run under the Python lsprof profiler.
1096
1006
 
1097
1007
    --coverage
1098
 
        Generate code coverage report
 
1008
        Generate line coverage report in the specified directory.
1099
1009
 
1100
1010
    --concurrency
1101
 
        Specify the number of processes that can be run concurrently
1102
 
        (selftest).
 
1011
        Specify the number of processes that can be run concurrently (selftest).
1103
1012
    """
1104
1013
    trace.mutter("breezy version: " + breezy.__version__)
1105
1014
    argv = _specified_or_unicode_argv(argv)
1106
1015
    trace.mutter("brz arguments: %r", argv)
1107
1016
 
1108
1017
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1109
 
        opt_coverage = opt_no_l10n = opt_no_aliases = False
1110
 
    opt_lsprof_file = None
 
1018
            opt_no_l10n = opt_no_aliases = False
 
1019
    opt_lsprof_file = opt_coverage_dir = None
1111
1020
 
1112
1021
    # --no-plugins is handled specially at a very early stage. We need
1113
1022
    # to load plugins before doing other command parsing so that they
1138
1047
            os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1139
1048
            i += 1
1140
1049
        elif a == '--coverage':
1141
 
            opt_coverage = True
 
1050
            opt_coverage_dir = argv[i + 1]
 
1051
            i += 1
1142
1052
        elif a == '--profile-imports':
1143
 
            pass  # already handled in startup script Bug #588277
 
1053
            pass # already handled in startup script Bug #588277
1144
1054
        elif a.startswith('-D'):
1145
1055
            debug.debug_flags.add(a[2:])
1146
1056
        elif a.startswith('-O'):
1149
1059
            argv_copy.append(a)
1150
1060
        i += 1
1151
1061
 
1152
 
    cmdline_overrides = breezy.get_global_state().cmdline_overrides
 
1062
    if breezy.global_state is None:
 
1063
        # FIXME: Workaround for users that imported breezy but didn't call
 
1064
        # breezy.initialize -- vila 2012-01-19
 
1065
        cmdline_overrides = config.CommandLineStore()
 
1066
    else:
 
1067
        cmdline_overrides = breezy.global_state.cmdline_overrides
1153
1068
    cmdline_overrides._from_cmdline(override_config)
1154
1069
 
1155
1070
    debug.set_debug_flags_from_config()
1156
1071
 
1157
1072
    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)
 
1073
        load_plugins()
1162
1074
    else:
1163
1075
        disable_plugins()
1164
1076
 
1191
1103
        saved_verbosity_level = option._verbosity_level
1192
1104
        option._verbosity_level = 0
1193
1105
        if opt_lsprof:
1194
 
            if opt_coverage:
 
1106
            if opt_coverage_dir:
1195
1107
                trace.warning(
1196
1108
                    '--coverage ignored, because --lsprof is in use.')
1197
1109
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1198
1110
        elif opt_profile:
1199
 
            if opt_coverage:
 
1111
            if opt_coverage_dir:
1200
1112
                trace.warning(
1201
1113
                    '--coverage ignored, because --profile is in use.')
1202
1114
            ret = apply_profiled(run, *run_argv)
1203
 
        elif opt_coverage:
1204
 
            ret = apply_coveraged(run, *run_argv)
 
1115
        elif opt_coverage_dir:
 
1116
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1205
1117
        else:
1206
1118
            ret = run(*run_argv)
1207
1119
        return ret or 0
1212
1124
        if 'memory' in debug.debug_flags:
1213
1125
            trace.debug_memory('Process status after command:', short=False)
1214
1126
        option._verbosity_level = saved_verbosity_level
1215
 
        # Reset the overrides
 
1127
        # Reset the overrides 
1216
1128
        cmdline_overrides._reset()
1217
1129
 
1218
1130
 
1241
1153
    if _list_bzr_commands in Command.hooks["list_commands"]:
1242
1154
        return
1243
1155
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1244
 
                                     "bzr commands")
 
1156
        "bzr commands")
1245
1157
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1246
 
                                     "bzr commands")
 
1158
        "bzr commands")
1247
1159
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1248
 
                                     "bzr plugin commands")
 
1160
        "bzr plugin commands")
1249
1161
    Command.hooks.install_named_hook("get_command", _get_external_command,
1250
 
                                     "bzr external command lookup")
 
1162
        "bzr external command lookup")
1251
1163
    Command.hooks.install_named_hook("get_missing_command",
1252
1164
                                     _try_plugin_provider,
1253
1165
                                     "bzr plugin-provider-db check")
1254
1166
 
1255
1167
 
 
1168
 
1256
1169
def _specified_or_unicode_argv(argv):
1257
1170
    # For internal or testing use, argv can be passed.  Otherwise, get it from
1258
 
    # the process arguments.
 
1171
    # the process arguments in a unicode-safe way.
1259
1172
    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
 
1173
        return osutils.get_unicode_argv()
 
1174
    else:
 
1175
        new_argv = []
 
1176
        try:
 
1177
            # ensure all arguments are unicode strings
 
1178
            for a in argv:
 
1179
                if isinstance(a, unicode):
 
1180
                    new_argv.append(a)
 
1181
                else:
 
1182
                    new_argv.append(a.decode('ascii'))
 
1183
        except UnicodeDecodeError:
 
1184
            raise errors.BzrError("argv should be list of unicode strings.")
 
1185
        return new_argv
1271
1186
 
1272
1187
 
1273
1188
def main(argv=None):
1294
1209
    """Run a bzr command with parameters as described by argv.
1295
1210
 
1296
1211
    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.
 
1212
    are already applied, and that unicode decoding has already been performed on argv.
1299
1213
    """
1300
1214
    # done here so that they're covered for every test run
1301
1215
    install_bzr_command_hooks()
1314
1228
        return run_bzr(argv)
1315
1229
    except Exception as e:
1316
1230
        if (isinstance(e, (OSError, IOError))
1317
 
                or not getattr(e, 'internal_error', True)):
 
1231
            or not getattr(e, 'internal_error', True)):
1318
1232
            trace.report_exception(sys.exc_info(), sys.stderr)
1319
1233
            return 3
1320
1234
        else:
1363
1277
        for key, provider in self.items():
1364
1278
            yield provider
1365
1279
 
1366
 
 
1367
1280
command_providers_registry = ProvidersRegistry()