/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: 2018-06-14 17:59:16 UTC
  • mto: This revision was merged to the branch mainline in revision 7065.
  • Revision ID: jelmer@jelmer.uk-20180614175916-a2e2xh5k533guq1x
Move breezy.plugins.git to breezy.git.

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
    text_type,
 
57
    )
 
58
 
 
59
 
 
60
class BzrOptionError(errors.BzrCommandError):
56
61
 
57
62
    _fmt = "Error in command line options"
58
63
 
93
98
 
94
99
class CommandRegistry(registry.Registry):
95
100
    """Special registry mapping command names to command classes.
96
 
 
 
101
    
97
102
    :ivar overridden_registry: Look in this registry for commands being
98
103
        overridden by this registry.  This can be used to tell plugin commands
99
104
        about the builtin they're decorating.
142
147
        except KeyError:
143
148
            trace.warning('Two plugins defined the same command: %r' % k)
144
149
            trace.warning('Not loading the one in %r' %
145
 
                          sys.modules[cmd.__module__])
 
150
                sys.modules[cmd.__module__])
146
151
            trace.warning('Previously this command was registered from %r' %
147
 
                          sys.modules[previous.__module__])
 
152
                sys.modules[previous.__module__])
148
153
        for a in cmd.aliases:
149
154
            self._alias_dict[a] = k_unsquished
150
155
        return previous
171
176
def register_command(cmd, decorate=False):
172
177
    """Register a plugin command.
173
178
 
174
 
    Should generally be avoided in favor of lazy registration.
 
179
    Should generally be avoided in favor of lazy registration. 
175
180
    """
176
181
    global plugin_cmds
177
182
    return plugin_cmds.register(cmd, decorate)
204
209
 
205
210
def _list_bzr_commands(names):
206
211
    """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.
 
212
    
 
213
    This is not the public interface, just the default hook called by all_command_names.
210
214
    """
211
215
    # to eliminate duplicates
212
216
    names.update(builtin_command_names())
227
231
 
228
232
def builtin_command_names():
229
233
    """Return list of builtin command names.
230
 
 
 
234
    
231
235
    Use of all_command_names() is encouraged rather than builtin_command_names
232
236
    and/or plugin_command_names.
233
237
    """
242
246
 
243
247
# Overrides for common mispellings that heuristics get wrong
244
248
_GUESS_OVERRIDES = {
245
 
    'ic': {'ci': 0},  # heuristic finds nick
 
249
    'ic': {'ci': 0}, # heuristic finds nick
246
250
    }
247
251
 
248
252
 
259
263
        names.update(cmd.aliases)
260
264
    # candidate: modified levenshtein distance against cmd_name.
261
265
    costs = {}
262
 
    import patiencediff
 
266
    from . import patiencediff
263
267
    for name in sorted(names):
264
268
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
265
269
        distance = 0.0
266
270
        opcodes = matcher.get_opcodes()
267
271
        for opcode, l1, l2, r1, r2 in opcodes:
268
272
            if opcode == 'delete':
269
 
                distance += l2 - l1
 
273
                distance += l2-l1
270
274
            elif opcode == 'replace':
271
 
                distance += max(l2 - l1, r2 - l1)
 
275
                distance += max(l2-l1, r2-l1)
272
276
            elif opcode == 'insert':
273
 
                distance += r2 - r1
 
277
                distance += r2-r1
274
278
            elif opcode == 'equal':
275
279
                # Score equal ranges lower, making similar commands of equal
276
280
                # length closer than arbitrary same length commands.
277
 
                distance -= 0.1 * (l2 - l1)
 
281
                distance -= 0.1 * (l2-l1)
278
282
        costs[name] = distance
279
283
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
280
284
    costs = sorted((costs[key], key) for key in costs)
298
302
        # No command found, see if this was a typo
299
303
        candidate = guess_command(cmd_name)
300
304
        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)
 
305
            raise errors.BzrCommandError(
 
306
                    gettext('unknown command "%s". Perhaps you meant "%s"')
 
307
                    % (cmd_name, candidate))
 
308
        raise errors.BzrCommandError(gettext('unknown command "%s"')
 
309
                % cmd_name)
306
310
 
307
311
 
308
312
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
491
495
 
492
496
        Functions will be called in LIFO order.
493
497
        """
494
 
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
 
498
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
495
499
 
496
500
    def cleanup_now(self):
497
501
        """Execute and empty pending cleanup functions immediately.
506
510
        as it releases all resources, this may release locks that the command
507
511
        wants to hold, so use should be done with care.
508
512
        """
509
 
        self._exit_stack.close()
510
 
 
511
 
    def enter_context(self, cm):
512
 
        return self._exit_stack.enter_context(cm)
 
513
        self._operation.cleanup_now()
513
514
 
514
515
    def _usage(self):
515
516
        """Return single-line grammar for this command.
580
581
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
581
582
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
582
583
        # 20090319
583
 
        parser = option.get_optparser(
584
 
            [v for k, v in sorted(self.options().items())])
 
584
        parser = option.get_optparser(self.options())
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
679
 
                    not line[0].isspace()):
 
678
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
680
679
                save_section(sections, order, label, section)
681
680
                label, section = None, line
682
681
            else:
711
710
        r = Option.STD_OPTIONS.copy()
712
711
        std_names = set(r)
713
712
        for o in self.takes_options:
714
 
            if isinstance(o, str):
 
713
            if isinstance(o, string_types):
715
714
                o = option.Option.OPTIONS[o]
716
715
            r[o.name] = o
717
716
            if o.name in std_names:
773
772
        you can override this method.
774
773
        """
775
774
        class_run = self.run
776
 
 
777
775
        def run(*args, **kwargs):
778
776
            for hook in Command.hooks['pre_command']:
779
777
                hook(self)
 
778
            self._operation = cleanup.OperationWithCleanups(class_run)
780
779
            try:
781
 
                with contextlib.ExitStack() as self._exit_stack:
782
 
                    return class_run(*args, **kwargs)
 
780
                return self._operation.run_simple(*args, **kwargs)
783
781
            finally:
 
782
                del self._operation
784
783
                for hook in Command.hooks['post_command']:
785
784
                    hook(self)
786
785
        self.run = run
795
794
        shell error code if not.  It's OK for this method to allow
796
795
        an exception to raise up.
797
796
 
798
 
        This method is automatically wrapped by Command.__init__ with a
799
 
        ExitStack, stored as self._exit_stack. This can be used
 
797
        This method is automatically wrapped by Command.__init__ with a 
 
798
        cleanup operation, stored as self._operation. This can be used
800
799
        via self.add_cleanup to perform automatic cleanups at the end of
801
800
        run().
802
801
 
827
826
    def name(self):
828
827
        """Return the canonical name for this command.
829
828
 
830
 
        The name under which it was actually invoked is available in invoked_as
 
829
        The name under which it was actually invoked is available in invoked_as.
831
830
        """
832
831
        return _unsquish_command_name(self.__class__.__name__)
833
832
 
849
848
        notified.
850
849
        """
851
850
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
852
 
        self.add_hook(
853
 
            'extend_command',
 
851
        self.add_hook('extend_command',
854
852
            "Called after creating a command object to allow modifications "
855
853
            "such as adding or removing options, docs etc. Called with the "
856
854
            "new breezy.commands.Command object.", (1, 13))
857
 
        self.add_hook(
858
 
            'get_command',
 
855
        self.add_hook('get_command',
859
856
            "Called when creating a single command. Called with "
860
857
            "(cmd_or_None, command_name). get_command should either return "
861
858
            "the cmd_or_None parameter, or a replacement Command object that "
863
860
            "hooks are core infrastructure. Many users will prefer to use "
864
861
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
865
862
            (1, 17))
866
 
        self.add_hook(
867
 
            'get_missing_command',
 
863
        self.add_hook('get_missing_command',
868
864
            "Called when creating a single command if no command could be "
869
865
            "found. Called with (command_name). get_missing_command should "
870
866
            "either return None, or a Command object to be used for the "
871
867
            "command.", (1, 17))
872
 
        self.add_hook(
873
 
            'list_commands',
 
868
        self.add_hook('list_commands',
874
869
            "Called when enumerating commands. Called with a set of "
875
870
            "cmd_name strings for all the commands found so far. This set "
876
871
            " is safe to mutate - e.g. to remove a command. "
877
872
            "list_commands should return the updated set of command names.",
878
873
            (1, 17))
879
 
        self.add_hook(
880
 
            'pre_command',
 
874
        self.add_hook('pre_command',
881
875
            "Called prior to executing a command. Called with the command "
882
876
            "object.", (2, 6))
883
 
        self.add_hook(
884
 
            'post_command',
 
877
        self.add_hook('post_command',
885
878
            "Called after executing a command. Called with the command "
886
879
            "object.", (2, 6))
887
880
 
888
 
 
889
881
Command.hooks = CommandHooks()
890
882
 
891
883
 
898
890
    they take, and which commands will accept them.
899
891
    """
900
892
    # TODO: make it a method of the Command?
901
 
    parser = option.get_optparser(
902
 
        [v for k, v in sorted(command.options().items())])
 
893
    parser = option.get_optparser(command.options())
903
894
    if alias_argv is not None:
904
895
        args = alias_argv + argv
905
896
    else:
906
897
        args = argv
907
898
 
908
 
    # python 2's optparse raises this exception if a non-ascii
 
899
    # for python 2.5 and later, optparse raises this exception if a non-ascii
909
900
    # option name is given.  See http://bugs.python.org/issue2931
910
901
    try:
911
902
        options, args = parser.parse_args(args)
912
 
    except UnicodeEncodeError:
913
 
        raise errors.CommandError(
 
903
    except UnicodeEncodeError as e:
 
904
        raise errors.BzrCommandError(
914
905
            gettext('Only ASCII permitted in option names'))
915
906
 
916
907
    opts = dict((k, v) for k, v in options.__dict__.items() if
927
918
        if ap[-1] == '?':
928
919
            if args:
929
920
                argdict[argname] = args.pop(0)
930
 
        elif ap[-1] == '*':  # all remaining arguments
 
921
        elif ap[-1] == '*': # all remaining arguments
931
922
            if args:
932
923
                argdict[argname + '_list'] = args[:]
933
924
                args = []
935
926
                argdict[argname + '_list'] = None
936
927
        elif ap[-1] == '+':
937
928
            if not args:
938
 
                raise errors.CommandError(gettext(
939
 
                    "command {0!r} needs one or more {1}").format(
940
 
                    cmd, argname.upper()))
 
929
                raise errors.BzrCommandError(gettext(
 
930
                      "command {0!r} needs one or more {1}").format(
 
931
                      cmd, argname.upper()))
941
932
            else:
942
933
                argdict[argname + '_list'] = args[:]
943
934
                args = []
944
 
        elif ap[-1] == '$':  # all but one
 
935
        elif ap[-1] == '$': # all but one
945
936
            if len(args) < 2:
946
 
                raise errors.CommandError(
947
 
                    gettext("command {0!r} needs one or more {1}").format(
948
 
                        cmd, argname.upper()))
 
937
                raise errors.BzrCommandError(
 
938
                      gettext("command {0!r} needs one or more {1}").format(
 
939
                                             cmd, argname.upper()))
949
940
            argdict[argname + '_list'] = args[:-1]
950
941
            args[:-1] = []
951
942
        else:
952
943
            # just a plain arg
953
944
            argname = ap
954
945
            if not args:
955
 
                raise errors.CommandError(
956
 
                    gettext("command {0!r} requires argument {1}").format(
957
 
                        cmd, argname.upper()))
 
946
                raise errors.BzrCommandError(
 
947
                     gettext("command {0!r} requires argument {1}").format(
 
948
                               cmd, argname.upper()))
958
949
            else:
959
950
                argdict[argname] = args.pop(0)
960
951
 
961
952
    if args:
962
 
        raise errors.CommandError(gettext(
963
 
            "extra argument to command {0}: {1}").format(
964
 
            cmd, args[0]))
 
953
        raise errors.BzrCommandError( gettext(
 
954
                              "extra argument to command {0}: {1}").format(
 
955
                                       cmd, args[0]) )
965
956
 
966
957
    return argdict
967
958
 
969
960
def apply_coveraged(the_callable, *args, **kwargs):
970
961
    import coverage
971
962
    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
 
963
    os.environ['COVERAGE_PROCESS_START'] = cov.config_file
977
964
    cov.start()
978
965
    try:
979
966
        return exception_to_return_code(the_callable, *args, **kwargs)
991
978
        prof = hotshot.Profile(pfname)
992
979
        try:
993
980
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
994
 
                               **kwargs) or 0
 
981
                **kwargs) or 0
995
982
        finally:
996
983
            prof.close()
997
984
        stats = hotshot.stats.load(pfname)
998
985
        stats.strip_dirs()
999
986
        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
 
987
        ## XXX: Might like to write to stderr or the trace file instead but
 
988
        ## print_stats seems hardcoded to stdout
1002
989
        stats.print_stats(20)
1003
990
        return ret
1004
991
    finally:
1015
1002
    """
1016
1003
    try:
1017
1004
        return the_callable(*args, **kwargs)
1018
 
    except (KeyboardInterrupt, Exception):
 
1005
    except (KeyboardInterrupt, Exception) as e:
1019
1006
        # used to handle AssertionError and KeyboardInterrupt
1020
1007
        # specially here, but hopefully they're handled ok by the logger now
1021
1008
        exc_info = sys.exc_info()
1098
1085
        Generate code coverage report
1099
1086
 
1100
1087
    --concurrency
1101
 
        Specify the number of processes that can be run concurrently
1102
 
        (selftest).
 
1088
        Specify the number of processes that can be run concurrently (selftest).
1103
1089
    """
1104
1090
    trace.mutter("breezy version: " + breezy.__version__)
1105
1091
    argv = _specified_or_unicode_argv(argv)
1106
1092
    trace.mutter("brz arguments: %r", argv)
1107
1093
 
1108
1094
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1109
 
        opt_coverage = opt_no_l10n = opt_no_aliases = False
 
1095
            opt_coverage = opt_no_l10n = opt_no_aliases = False
1110
1096
    opt_lsprof_file = None
1111
1097
 
1112
1098
    # --no-plugins is handled specially at a very early stage. We need
1140
1126
        elif a == '--coverage':
1141
1127
            opt_coverage = True
1142
1128
        elif a == '--profile-imports':
1143
 
            pass  # already handled in startup script Bug #588277
 
1129
            pass # already handled in startup script Bug #588277
1144
1130
        elif a.startswith('-D'):
1145
1131
            debug.debug_flags.add(a[2:])
1146
1132
        elif a.startswith('-O'):
1155
1141
    debug.set_debug_flags_from_config()
1156
1142
 
1157
1143
    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)
 
1144
        load_plugins()
1162
1145
    else:
1163
1146
        disable_plugins()
1164
1147
 
1212
1195
        if 'memory' in debug.debug_flags:
1213
1196
            trace.debug_memory('Process status after command:', short=False)
1214
1197
        option._verbosity_level = saved_verbosity_level
1215
 
        # Reset the overrides
 
1198
        # Reset the overrides 
1216
1199
        cmdline_overrides._reset()
1217
1200
 
1218
1201
 
1241
1224
    if _list_bzr_commands in Command.hooks["list_commands"]:
1242
1225
        return
1243
1226
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1244
 
                                     "bzr commands")
 
1227
        "bzr commands")
1245
1228
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1246
 
                                     "bzr commands")
 
1229
        "bzr commands")
1247
1230
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1248
 
                                     "bzr plugin commands")
 
1231
        "bzr plugin commands")
1249
1232
    Command.hooks.install_named_hook("get_command", _get_external_command,
1250
 
                                     "bzr external command lookup")
 
1233
        "bzr external command lookup")
1251
1234
    Command.hooks.install_named_hook("get_missing_command",
1252
1235
                                     _try_plugin_provider,
1253
1236
                                     "bzr plugin-provider-db check")
1254
1237
 
1255
1238
 
 
1239
 
1256
1240
def _specified_or_unicode_argv(argv):
1257
1241
    # For internal or testing use, argv can be passed.  Otherwise, get it from
1258
 
    # the process arguments.
 
1242
    # the process arguments in a unicode-safe way.
1259
1243
    if argv is None:
1260
 
        return sys.argv[1:]
 
1244
        return osutils.get_unicode_argv()
1261
1245
    new_argv = []
1262
1246
    try:
1263
1247
        # ensure all arguments are unicode strings
1264
1248
        for a in argv:
1265
 
            if not isinstance(a, str):
 
1249
            if not isinstance(a, string_types):
1266
1250
                raise ValueError('not native str or unicode: %r' % (a,))
 
1251
            if isinstance(a, bytes):
 
1252
                # For Python 2 only allow ascii native strings
 
1253
                a = a.decode('ascii')
1267
1254
            new_argv.append(a)
1268
1255
    except (ValueError, UnicodeDecodeError):
1269
1256
        raise errors.BzrError("argv should be list of unicode strings.")
1294
1281
    """Run a bzr command with parameters as described by argv.
1295
1282
 
1296
1283
    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.
 
1284
    are already applied, and that unicode decoding has already been performed on argv.
1299
1285
    """
1300
1286
    # done here so that they're covered for every test run
1301
1287
    install_bzr_command_hooks()
1314
1300
        return run_bzr(argv)
1315
1301
    except Exception as e:
1316
1302
        if (isinstance(e, (OSError, IOError))
1317
 
                or not getattr(e, 'internal_error', True)):
 
1303
            or not getattr(e, 'internal_error', True)):
1318
1304
            trace.report_exception(sys.exc_info(), sys.stderr)
1319
1305
            return 3
1320
1306
        else:
1363
1349
        for key, provider in self.items():
1364
1350
            yield provider
1365
1351
 
1366
 
 
1367
1352
command_providers_registry = ProvidersRegistry()