/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: 2020-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

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
 
 
19
17
# TODO: Define arguments by objects, rather than just using names.
20
18
# Those objects can specify the expected type of the argument, which
21
19
# would help with validation and shell completion.  They could also provide
23
21
 
24
22
# TODO: Specific "examples" property on commands for consistent formatting.
25
23
 
 
24
import contextlib
26
25
import os
27
26
import sys
28
27
 
 
28
from . import (
 
29
    i18n,
 
30
    option,
 
31
    osutils,
 
32
    )
 
33
 
29
34
from .lazy_import import lazy_import
30
35
lazy_import(globals(), """
31
36
import errno
32
 
import threading
33
37
 
34
38
import breezy
35
39
from breezy import (
36
 
    config,
37
 
    cleanup,
38
40
    cmdline,
39
41
    debug,
40
 
    i18n,
41
 
    option,
42
 
    osutils,
43
42
    trace,
44
43
    ui,
45
44
    )
51
50
from .option import Option
52
51
from .plugin import disable_plugins, load_plugins, plugin_name
53
52
from . import errors, registry
54
 
from .sixish import (
55
 
    string_types,
56
 
    text_type,
57
 
    )
58
53
 
59
54
 
60
55
class BzrOptionError(errors.BzrCommandError):
98
93
 
99
94
class CommandRegistry(registry.Registry):
100
95
    """Special registry mapping command names to command classes.
101
 
    
 
96
 
102
97
    :ivar overridden_registry: Look in this registry for commands being
103
98
        overridden by this registry.  This can be used to tell plugin commands
104
99
        about the builtin they're decorating.
147
142
        except KeyError:
148
143
            trace.warning('Two plugins defined the same command: %r' % k)
149
144
            trace.warning('Not loading the one in %r' %
150
 
                sys.modules[cmd.__module__])
 
145
                          sys.modules[cmd.__module__])
151
146
            trace.warning('Previously this command was registered from %r' %
152
 
                sys.modules[previous.__module__])
 
147
                          sys.modules[previous.__module__])
153
148
        for a in cmd.aliases:
154
149
            self._alias_dict[a] = k_unsquished
155
150
        return previous
176
171
def register_command(cmd, decorate=False):
177
172
    """Register a plugin command.
178
173
 
179
 
    Should generally be avoided in favor of lazy registration. 
 
174
    Should generally be avoided in favor of lazy registration.
180
175
    """
181
176
    global plugin_cmds
182
177
    return plugin_cmds.register(cmd, decorate)
209
204
 
210
205
def _list_bzr_commands(names):
211
206
    """Find commands from bzr's core and plugins.
212
 
    
213
 
    This is not the public interface, just the default hook called by all_command_names.
 
207
 
 
208
    This is not the public interface, just the default hook called by
 
209
    all_command_names.
214
210
    """
215
211
    # to eliminate duplicates
216
212
    names.update(builtin_command_names())
231
227
 
232
228
def builtin_command_names():
233
229
    """Return list of builtin command names.
234
 
    
 
230
 
235
231
    Use of all_command_names() is encouraged rather than builtin_command_names
236
232
    and/or plugin_command_names.
237
233
    """
246
242
 
247
243
# Overrides for common mispellings that heuristics get wrong
248
244
_GUESS_OVERRIDES = {
249
 
    'ic': {'ci': 0}, # heuristic finds nick
 
245
    'ic': {'ci': 0},  # heuristic finds nick
250
246
    }
251
247
 
252
248
 
263
259
        names.update(cmd.aliases)
264
260
    # candidate: modified levenshtein distance against cmd_name.
265
261
    costs = {}
266
 
    from . import patiencediff
 
262
    import patiencediff
267
263
    for name in sorted(names):
268
264
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
269
265
        distance = 0.0
270
266
        opcodes = matcher.get_opcodes()
271
267
        for opcode, l1, l2, r1, r2 in opcodes:
272
268
            if opcode == 'delete':
273
 
                distance += l2-l1
 
269
                distance += l2 - l1
274
270
            elif opcode == 'replace':
275
 
                distance += max(l2-l1, r2-l1)
 
271
                distance += max(l2 - l1, r2 - l1)
276
272
            elif opcode == 'insert':
277
 
                distance += r2-r1
 
273
                distance += r2 - r1
278
274
            elif opcode == 'equal':
279
275
                # Score equal ranges lower, making similar commands of equal
280
276
                # length closer than arbitrary same length commands.
281
 
                distance -= 0.1 * (l2-l1)
 
277
                distance -= 0.1 * (l2 - l1)
282
278
        costs[name] = distance
283
279
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
284
280
    costs = sorted((costs[key], key) for key in costs)
303
299
        candidate = guess_command(cmd_name)
304
300
        if candidate is not None:
305
301
            raise errors.BzrCommandError(
306
 
                    gettext('unknown command "%s". Perhaps you meant "%s"')
307
 
                    % (cmd_name, candidate))
 
302
                gettext('unknown command "%s". Perhaps you meant "%s"')
 
303
                % (cmd_name, candidate))
308
304
        raise errors.BzrCommandError(gettext('unknown command "%s"')
309
 
                % cmd_name)
 
305
                                     % cmd_name)
310
306
 
311
307
 
312
308
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
495
491
 
496
492
        Functions will be called in LIFO order.
497
493
        """
498
 
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
 
494
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
499
495
 
500
496
    def cleanup_now(self):
501
497
        """Execute and empty pending cleanup functions immediately.
510
506
        as it releases all resources, this may release locks that the command
511
507
        wants to hold, so use should be done with care.
512
508
        """
513
 
        self._operation.cleanup_now()
 
509
        self._exit_stack.close()
 
510
 
 
511
    def enter_context(self, cm):
 
512
        return self._exit_stack.enter_context(cm)
514
513
 
515
514
    def _usage(self):
516
515
        """Return single-line grammar for this command.
581
580
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
582
581
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
583
582
        # 20090319
584
 
        parser = option.get_optparser(self.options())
 
583
        parser = option.get_optparser(
 
584
            [v for k, v in sorted(self.options().items())])
585
585
        options = parser.format_option_help()
586
586
        # FIXME: According to the spec, ReST option lists actually don't
587
587
        # support options like --1.14 so that causes syntax errors (in Sphinx
614
614
                result += '\n'
615
615
        else:
616
616
            result += (gettext("See brz help %s for more details and examples.\n\n")
617
 
                % self.name())
 
617
                       % self.name())
618
618
 
619
619
        # Add the aliases, source (plug-in) and see also links, if any
620
620
        if self.aliases:
635
635
                    else:
636
636
                        # Use a Sphinx link for this entry
637
637
                        link_text = gettext(":doc:`{0} <{1}-help>`").format(
638
 
                                                                    item, item)
 
638
                            item, item)
639
639
                        see_also_links.append(link_text)
640
640
                see_also = see_also_links
641
641
            result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
675
675
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
676
676
                save_section(sections, order, label, section)
677
677
                label, section = line[1:-1], ''
678
 
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
 
678
            elif (label is not None and len(line) > 1 and
 
679
                    not line[0].isspace()):
679
680
                save_section(sections, order, label, section)
680
681
                label, section = None, line
681
682
            else:
710
711
        r = Option.STD_OPTIONS.copy()
711
712
        std_names = set(r)
712
713
        for o in self.takes_options:
713
 
            if isinstance(o, string_types):
 
714
            if isinstance(o, str):
714
715
                o = option.Option.OPTIONS[o]
715
716
            r[o.name] = o
716
717
            if o.name in std_names:
772
773
        you can override this method.
773
774
        """
774
775
        class_run = self.run
 
776
 
775
777
        def run(*args, **kwargs):
776
778
            for hook in Command.hooks['pre_command']:
777
779
                hook(self)
778
 
            self._operation = cleanup.OperationWithCleanups(class_run)
779
780
            try:
780
 
                return self._operation.run_simple(*args, **kwargs)
 
781
                with contextlib.ExitStack() as self._exit_stack:
 
782
                    return class_run(*args, **kwargs)
781
783
            finally:
782
 
                del self._operation
783
784
                for hook in Command.hooks['post_command']:
784
785
                    hook(self)
785
786
        self.run = run
794
795
        shell error code if not.  It's OK for this method to allow
795
796
        an exception to raise up.
796
797
 
797
 
        This method is automatically wrapped by Command.__init__ with a 
798
 
        cleanup operation, stored as self._operation. This can be used
 
798
        This method is automatically wrapped by Command.__init__ with a
 
799
        ExitStack, stored as self._exit_stack. This can be used
799
800
        via self.add_cleanup to perform automatic cleanups at the end of
800
801
        run().
801
802
 
826
827
    def name(self):
827
828
        """Return the canonical name for this command.
828
829
 
829
 
        The name under which it was actually invoked is available in invoked_as.
 
830
        The name under which it was actually invoked is available in invoked_as
830
831
        """
831
832
        return _unsquish_command_name(self.__class__.__name__)
832
833
 
848
849
        notified.
849
850
        """
850
851
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
851
 
        self.add_hook('extend_command',
 
852
        self.add_hook(
 
853
            'extend_command',
852
854
            "Called after creating a command object to allow modifications "
853
855
            "such as adding or removing options, docs etc. Called with the "
854
856
            "new breezy.commands.Command object.", (1, 13))
855
 
        self.add_hook('get_command',
 
857
        self.add_hook(
 
858
            'get_command',
856
859
            "Called when creating a single command. Called with "
857
860
            "(cmd_or_None, command_name). get_command should either return "
858
861
            "the cmd_or_None parameter, or a replacement Command object that "
860
863
            "hooks are core infrastructure. Many users will prefer to use "
861
864
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
862
865
            (1, 17))
863
 
        self.add_hook('get_missing_command',
 
866
        self.add_hook(
 
867
            'get_missing_command',
864
868
            "Called when creating a single command if no command could be "
865
869
            "found. Called with (command_name). get_missing_command should "
866
870
            "either return None, or a Command object to be used for the "
867
871
            "command.", (1, 17))
868
 
        self.add_hook('list_commands',
 
872
        self.add_hook(
 
873
            'list_commands',
869
874
            "Called when enumerating commands. Called with a set of "
870
875
            "cmd_name strings for all the commands found so far. This set "
871
876
            " is safe to mutate - e.g. to remove a command. "
872
877
            "list_commands should return the updated set of command names.",
873
878
            (1, 17))
874
 
        self.add_hook('pre_command',
 
879
        self.add_hook(
 
880
            'pre_command',
875
881
            "Called prior to executing a command. Called with the command "
876
882
            "object.", (2, 6))
877
 
        self.add_hook('post_command',
 
883
        self.add_hook(
 
884
            'post_command',
878
885
            "Called after executing a command. Called with the command "
879
886
            "object.", (2, 6))
880
887
 
 
888
 
881
889
Command.hooks = CommandHooks()
882
890
 
883
891
 
890
898
    they take, and which commands will accept them.
891
899
    """
892
900
    # TODO: make it a method of the Command?
893
 
    parser = option.get_optparser(command.options())
 
901
    parser = option.get_optparser(
 
902
        [v for k, v in sorted(command.options().items())])
894
903
    if alias_argv is not None:
895
904
        args = alias_argv + argv
896
905
    else:
897
906
        args = argv
898
907
 
899
 
    # for python 2.5 and later, optparse raises this exception if a non-ascii
 
908
    # python 2's optparse raises this exception if a non-ascii
900
909
    # option name is given.  See http://bugs.python.org/issue2931
901
910
    try:
902
911
        options, args = parser.parse_args(args)
903
 
    except UnicodeEncodeError as e:
 
912
    except UnicodeEncodeError:
904
913
        raise errors.BzrCommandError(
905
914
            gettext('Only ASCII permitted in option names'))
906
915
 
918
927
        if ap[-1] == '?':
919
928
            if args:
920
929
                argdict[argname] = args.pop(0)
921
 
        elif ap[-1] == '*': # all remaining arguments
 
930
        elif ap[-1] == '*':  # all remaining arguments
922
931
            if args:
923
932
                argdict[argname + '_list'] = args[:]
924
933
                args = []
927
936
        elif ap[-1] == '+':
928
937
            if not args:
929
938
                raise errors.BzrCommandError(gettext(
930
 
                      "command {0!r} needs one or more {1}").format(
931
 
                      cmd, argname.upper()))
 
939
                    "command {0!r} needs one or more {1}").format(
 
940
                    cmd, argname.upper()))
932
941
            else:
933
942
                argdict[argname + '_list'] = args[:]
934
943
                args = []
935
 
        elif ap[-1] == '$': # all but one
 
944
        elif ap[-1] == '$':  # all but one
936
945
            if len(args) < 2:
937
946
                raise errors.BzrCommandError(
938
 
                      gettext("command {0!r} needs one or more {1}").format(
939
 
                                             cmd, argname.upper()))
 
947
                    gettext("command {0!r} needs one or more {1}").format(
 
948
                        cmd, argname.upper()))
940
949
            argdict[argname + '_list'] = args[:-1]
941
950
            args[:-1] = []
942
951
        else:
944
953
            argname = ap
945
954
            if not args:
946
955
                raise errors.BzrCommandError(
947
 
                     gettext("command {0!r} requires argument {1}").format(
948
 
                               cmd, argname.upper()))
 
956
                    gettext("command {0!r} requires argument {1}").format(
 
957
                        cmd, argname.upper()))
949
958
            else:
950
959
                argdict[argname] = args.pop(0)
951
960
 
952
961
    if args:
953
 
        raise errors.BzrCommandError( gettext(
954
 
                              "extra argument to command {0}: {1}").format(
955
 
                                       cmd, args[0]) )
 
962
        raise errors.BzrCommandError(gettext(
 
963
            "extra argument to command {0}: {1}").format(
 
964
            cmd, args[0]))
956
965
 
957
966
    return argdict
958
967
 
959
968
 
960
 
def apply_coveraged(dirname, the_callable, *args, **kwargs):
961
 
    import trace
962
 
    tracer = trace.Trace(count=1, trace=0)
963
 
    sys.settrace(tracer.globaltrace)
964
 
    threading.settrace(tracer.globaltrace)
965
 
 
 
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()
966
978
    try:
967
979
        return exception_to_return_code(the_callable, *args, **kwargs)
968
980
    finally:
969
 
        sys.settrace(None)
970
 
        results = tracer.results()
971
 
        results.write_results(show_missing=1, summary=False,
972
 
                              coverdir=dirname)
 
981
        cov.stop()
 
982
        cov.save()
973
983
 
974
984
 
975
985
def apply_profiled(the_callable, *args, **kwargs):
981
991
        prof = hotshot.Profile(pfname)
982
992
        try:
983
993
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
984
 
                **kwargs) or 0
 
994
                               **kwargs) or 0
985
995
        finally:
986
996
            prof.close()
987
997
        stats = hotshot.stats.load(pfname)
988
998
        stats.strip_dirs()
989
999
        stats.sort_stats('cum')   # 'time'
990
 
        ## XXX: Might like to write to stderr or the trace file instead but
991
 
        ## print_stats seems hardcoded to stdout
 
1000
        # XXX: Might like to write to stderr or the trace file instead but
 
1001
        # print_stats seems hardcoded to stdout
992
1002
        stats.print_stats(20)
993
1003
        return ret
994
1004
    finally:
1005
1015
    """
1006
1016
    try:
1007
1017
        return the_callable(*args, **kwargs)
1008
 
    except (KeyboardInterrupt, Exception) as e:
 
1018
    except (KeyboardInterrupt, Exception):
1009
1019
        # used to handle AssertionError and KeyboardInterrupt
1010
1020
        # specially here, but hopefully they're handled ok by the logger now
1011
1021
        exc_info = sys.exc_info()
1085
1095
        Run under the Python lsprof profiler.
1086
1096
 
1087
1097
    --coverage
1088
 
        Generate line coverage report in the specified directory.
 
1098
        Generate code coverage report
1089
1099
 
1090
1100
    --concurrency
1091
 
        Specify the number of processes that can be run concurrently (selftest).
 
1101
        Specify the number of processes that can be run concurrently
 
1102
        (selftest).
1092
1103
    """
1093
1104
    trace.mutter("breezy version: " + breezy.__version__)
1094
1105
    argv = _specified_or_unicode_argv(argv)
1095
1106
    trace.mutter("brz arguments: %r", argv)
1096
1107
 
1097
1108
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1098
 
            opt_no_l10n = opt_no_aliases = False
1099
 
    opt_lsprof_file = opt_coverage_dir = None
 
1109
        opt_coverage = opt_no_l10n = opt_no_aliases = False
 
1110
    opt_lsprof_file = None
1100
1111
 
1101
1112
    # --no-plugins is handled specially at a very early stage. We need
1102
1113
    # to load plugins before doing other command parsing so that they
1127
1138
            os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1128
1139
            i += 1
1129
1140
        elif a == '--coverage':
1130
 
            opt_coverage_dir = argv[i + 1]
1131
 
            i += 1
 
1141
            opt_coverage = True
1132
1142
        elif a == '--profile-imports':
1133
 
            pass # already handled in startup script Bug #588277
 
1143
            pass  # already handled in startup script Bug #588277
1134
1144
        elif a.startswith('-D'):
1135
1145
            debug.debug_flags.add(a[2:])
1136
1146
        elif a.startswith('-O'):
1178
1188
        saved_verbosity_level = option._verbosity_level
1179
1189
        option._verbosity_level = 0
1180
1190
        if opt_lsprof:
1181
 
            if opt_coverage_dir:
 
1191
            if opt_coverage:
1182
1192
                trace.warning(
1183
1193
                    '--coverage ignored, because --lsprof is in use.')
1184
1194
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1185
1195
        elif opt_profile:
1186
 
            if opt_coverage_dir:
 
1196
            if opt_coverage:
1187
1197
                trace.warning(
1188
1198
                    '--coverage ignored, because --profile is in use.')
1189
1199
            ret = apply_profiled(run, *run_argv)
1190
 
        elif opt_coverage_dir:
1191
 
            ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
 
1200
        elif opt_coverage:
 
1201
            ret = apply_coveraged(run, *run_argv)
1192
1202
        else:
1193
1203
            ret = run(*run_argv)
1194
1204
        return ret or 0
1199
1209
        if 'memory' in debug.debug_flags:
1200
1210
            trace.debug_memory('Process status after command:', short=False)
1201
1211
        option._verbosity_level = saved_verbosity_level
1202
 
        # Reset the overrides 
 
1212
        # Reset the overrides
1203
1213
        cmdline_overrides._reset()
1204
1214
 
1205
1215
 
1228
1238
    if _list_bzr_commands in Command.hooks["list_commands"]:
1229
1239
        return
1230
1240
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1231
 
        "bzr commands")
 
1241
                                     "bzr commands")
1232
1242
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1233
 
        "bzr commands")
 
1243
                                     "bzr commands")
1234
1244
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1235
 
        "bzr plugin commands")
 
1245
                                     "bzr plugin commands")
1236
1246
    Command.hooks.install_named_hook("get_command", _get_external_command,
1237
 
        "bzr external command lookup")
 
1247
                                     "bzr external command lookup")
1238
1248
    Command.hooks.install_named_hook("get_missing_command",
1239
1249
                                     _try_plugin_provider,
1240
1250
                                     "bzr plugin-provider-db check")
1241
1251
 
1242
1252
 
1243
 
 
1244
1253
def _specified_or_unicode_argv(argv):
1245
1254
    # For internal or testing use, argv can be passed.  Otherwise, get it from
1246
1255
    # the process arguments in a unicode-safe way.
1250
1259
    try:
1251
1260
        # ensure all arguments are unicode strings
1252
1261
        for a in argv:
1253
 
            if not isinstance(a, string_types):
 
1262
            if not isinstance(a, str):
1254
1263
                raise ValueError('not native str or unicode: %r' % (a,))
1255
 
            if isinstance(a, bytes):
1256
 
                # For Python 2 only allow ascii native strings
1257
 
                a = a.decode('ascii')
1258
1264
            new_argv.append(a)
1259
1265
    except (ValueError, UnicodeDecodeError):
1260
1266
        raise errors.BzrError("argv should be list of unicode strings.")
1285
1291
    """Run a bzr command with parameters as described by argv.
1286
1292
 
1287
1293
    This function assumed that that UI layer is setup, that symbol deprecations
1288
 
    are already applied, and that unicode decoding has already been performed on argv.
 
1294
    are already applied, and that unicode decoding has already been performed
 
1295
    on argv.
1289
1296
    """
1290
1297
    # done here so that they're covered for every test run
1291
1298
    install_bzr_command_hooks()
1304
1311
        return run_bzr(argv)
1305
1312
    except Exception as e:
1306
1313
        if (isinstance(e, (OSError, IOError))
1307
 
            or not getattr(e, 'internal_error', True)):
 
1314
                or not getattr(e, 'internal_error', True)):
1308
1315
            trace.report_exception(sys.exc_info(), sys.stderr)
1309
1316
            return 3
1310
1317
        else:
1353
1360
        for key, provider in self.items():
1354
1361
            yield provider
1355
1362
 
 
1363
 
1356
1364
command_providers_registry = ProvidersRegistry()