/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

Merge test-run support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import os
27
27
import sys
28
28
 
29
 
from . import (
30
 
    i18n,
31
 
    option,
32
 
    osutils,
33
 
    )
34
 
 
35
29
from .lazy_import import lazy_import
36
30
lazy_import(globals(), """
37
31
import errno
 
32
import threading
38
33
 
39
34
import breezy
40
35
from breezy import (
 
36
    config,
41
37
    cleanup,
42
38
    cmdline,
43
39
    debug,
 
40
    i18n,
 
41
    option,
 
42
    osutils,
44
43
    trace,
45
44
    ui,
46
45
    )
54
53
from . import errors, registry
55
54
from .sixish import (
56
55
    string_types,
 
56
    text_type,
57
57
    )
58
58
 
59
59
 
98
98
 
99
99
class CommandRegistry(registry.Registry):
100
100
    """Special registry mapping command names to command classes.
101
 
 
 
101
    
102
102
    :ivar overridden_registry: Look in this registry for commands being
103
103
        overridden by this registry.  This can be used to tell plugin commands
104
104
        about the builtin they're decorating.
147
147
        except KeyError:
148
148
            trace.warning('Two plugins defined the same command: %r' % k)
149
149
            trace.warning('Not loading the one in %r' %
150
 
                          sys.modules[cmd.__module__])
 
150
                sys.modules[cmd.__module__])
151
151
            trace.warning('Previously this command was registered from %r' %
152
 
                          sys.modules[previous.__module__])
 
152
                sys.modules[previous.__module__])
153
153
        for a in cmd.aliases:
154
154
            self._alias_dict[a] = k_unsquished
155
155
        return previous
176
176
def register_command(cmd, decorate=False):
177
177
    """Register a plugin command.
178
178
 
179
 
    Should generally be avoided in favor of lazy registration.
 
179
    Should generally be avoided in favor of lazy registration. 
180
180
    """
181
181
    global plugin_cmds
182
182
    return plugin_cmds.register(cmd, decorate)
209
209
 
210
210
def _list_bzr_commands(names):
211
211
    """Find commands from bzr's core and plugins.
212
 
 
213
 
    This is not the public interface, just the default hook called by
214
 
    all_command_names.
 
212
    
 
213
    This is not the public interface, just the default hook called by all_command_names.
215
214
    """
216
215
    # to eliminate duplicates
217
216
    names.update(builtin_command_names())
232
231
 
233
232
def builtin_command_names():
234
233
    """Return list of builtin command names.
235
 
 
 
234
    
236
235
    Use of all_command_names() is encouraged rather than builtin_command_names
237
236
    and/or plugin_command_names.
238
237
    """
247
246
 
248
247
# Overrides for common mispellings that heuristics get wrong
249
248
_GUESS_OVERRIDES = {
250
 
    'ic': {'ci': 0},  # heuristic finds nick
 
249
    'ic': {'ci': 0}, # heuristic finds nick
251
250
    }
252
251
 
253
252
 
264
263
        names.update(cmd.aliases)
265
264
    # candidate: modified levenshtein distance against cmd_name.
266
265
    costs = {}
267
 
    import patiencediff
 
266
    from . import patiencediff
268
267
    for name in sorted(names):
269
268
        matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
270
269
        distance = 0.0
271
270
        opcodes = matcher.get_opcodes()
272
271
        for opcode, l1, l2, r1, r2 in opcodes:
273
272
            if opcode == 'delete':
274
 
                distance += l2 - l1
 
273
                distance += l2-l1
275
274
            elif opcode == 'replace':
276
 
                distance += max(l2 - l1, r2 - l1)
 
275
                distance += max(l2-l1, r2-l1)
277
276
            elif opcode == 'insert':
278
 
                distance += r2 - r1
 
277
                distance += r2-r1
279
278
            elif opcode == 'equal':
280
279
                # Score equal ranges lower, making similar commands of equal
281
280
                # length closer than arbitrary same length commands.
282
 
                distance -= 0.1 * (l2 - l1)
 
281
                distance -= 0.1 * (l2-l1)
283
282
        costs[name] = distance
284
283
    costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
285
284
    costs = sorted((costs[key], key) for key in costs)
304
303
        candidate = guess_command(cmd_name)
305
304
        if candidate is not None:
306
305
            raise errors.BzrCommandError(
307
 
                gettext('unknown command "%s". Perhaps you meant "%s"')
308
 
                % (cmd_name, candidate))
 
306
                    gettext('unknown command "%s". Perhaps you meant "%s"')
 
307
                    % (cmd_name, candidate))
309
308
        raise errors.BzrCommandError(gettext('unknown command "%s"')
310
 
                                     % cmd_name)
 
309
                % cmd_name)
311
310
 
312
311
 
313
312
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
496
495
 
497
496
        Functions will be called in LIFO order.
498
497
        """
499
 
        self._exit_stack.callback(cleanup_func, *args, **kwargs)
 
498
        self._operation.add_cleanup(cleanup_func, *args, **kwargs)
500
499
 
501
500
    def cleanup_now(self):
502
501
        """Execute and empty pending cleanup functions immediately.
511
510
        as it releases all resources, this may release locks that the command
512
511
        wants to hold, so use should be done with care.
513
512
        """
514
 
        self._exit_stack.close()
515
 
 
516
 
    def enter_context(self, cm):
517
 
        return self._exit_stack.enter_context(cm)
 
513
        self._operation.cleanup_now()
518
514
 
519
515
    def _usage(self):
520
516
        """Return single-line grammar for this command.
585
581
        # XXX: optparse implicitly rewraps the help, and not always perfectly,
586
582
        # so we get <https://bugs.launchpad.net/bzr/+bug/249908>.  -- mbp
587
583
        # 20090319
588
 
        parser = option.get_optparser(
589
 
            [v for k, v in sorted(self.options().items())])
 
584
        parser = option.get_optparser(self.options())
590
585
        options = parser.format_option_help()
591
586
        # FIXME: According to the spec, ReST option lists actually don't
592
587
        # support options like --1.14 so that causes syntax errors (in Sphinx
619
614
                result += '\n'
620
615
        else:
621
616
            result += (gettext("See brz help %s for more details and examples.\n\n")
622
 
                       % self.name())
 
617
                % self.name())
623
618
 
624
619
        # Add the aliases, source (plug-in) and see also links, if any
625
620
        if self.aliases:
640
635
                    else:
641
636
                        # Use a Sphinx link for this entry
642
637
                        link_text = gettext(":doc:`{0} <{1}-help>`").format(
643
 
                            item, item)
 
638
                                                                    item, item)
644
639
                        see_also_links.append(link_text)
645
640
                see_also = see_also_links
646
641
            result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
680
675
            if line.startswith(':') and line.endswith(':') and len(line) > 2:
681
676
                save_section(sections, order, label, section)
682
677
                label, section = line[1:-1], ''
683
 
            elif (label is not None and len(line) > 1 and
684
 
                    not line[0].isspace()):
 
678
            elif (label is not None) and len(line) > 1 and not line[0].isspace():
685
679
                save_section(sections, order, label, section)
686
680
                label, section = None, line
687
681
            else:
778
772
        you can override this method.
779
773
        """
780
774
        class_run = self.run
781
 
 
782
775
        def run(*args, **kwargs):
783
776
            for hook in Command.hooks['pre_command']:
784
777
                hook(self)
 
778
            self._operation = cleanup.OperationWithCleanups(class_run)
785
779
            try:
786
 
                with cleanup.ExitStack() as self._exit_stack:
787
 
                    return class_run(*args, **kwargs)
 
780
                return self._operation.run_simple(*args, **kwargs)
788
781
            finally:
 
782
                del self._operation
789
783
                for hook in Command.hooks['post_command']:
790
784
                    hook(self)
791
785
        self.run = run
800
794
        shell error code if not.  It's OK for this method to allow
801
795
        an exception to raise up.
802
796
 
803
 
        This method is automatically wrapped by Command.__init__ with a
804
 
        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
805
799
        via self.add_cleanup to perform automatic cleanups at the end of
806
800
        run().
807
801
 
832
826
    def name(self):
833
827
        """Return the canonical name for this command.
834
828
 
835
 
        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.
836
830
        """
837
831
        return _unsquish_command_name(self.__class__.__name__)
838
832
 
854
848
        notified.
855
849
        """
856
850
        Hooks.__init__(self, "breezy.commands", "Command.hooks")
857
 
        self.add_hook(
858
 
            'extend_command',
 
851
        self.add_hook('extend_command',
859
852
            "Called after creating a command object to allow modifications "
860
853
            "such as adding or removing options, docs etc. Called with the "
861
854
            "new breezy.commands.Command object.", (1, 13))
862
 
        self.add_hook(
863
 
            'get_command',
 
855
        self.add_hook('get_command',
864
856
            "Called when creating a single command. Called with "
865
857
            "(cmd_or_None, command_name). get_command should either return "
866
858
            "the cmd_or_None parameter, or a replacement Command object that "
868
860
            "hooks are core infrastructure. Many users will prefer to use "
869
861
            "breezy.commands.register_command or plugin_cmds.register_lazy.",
870
862
            (1, 17))
871
 
        self.add_hook(
872
 
            'get_missing_command',
 
863
        self.add_hook('get_missing_command',
873
864
            "Called when creating a single command if no command could be "
874
865
            "found. Called with (command_name). get_missing_command should "
875
866
            "either return None, or a Command object to be used for the "
876
867
            "command.", (1, 17))
877
 
        self.add_hook(
878
 
            'list_commands',
 
868
        self.add_hook('list_commands',
879
869
            "Called when enumerating commands. Called with a set of "
880
870
            "cmd_name strings for all the commands found so far. This set "
881
871
            " is safe to mutate - e.g. to remove a command. "
882
872
            "list_commands should return the updated set of command names.",
883
873
            (1, 17))
884
 
        self.add_hook(
885
 
            'pre_command',
 
874
        self.add_hook('pre_command',
886
875
            "Called prior to executing a command. Called with the command "
887
876
            "object.", (2, 6))
888
 
        self.add_hook(
889
 
            'post_command',
 
877
        self.add_hook('post_command',
890
878
            "Called after executing a command. Called with the command "
891
879
            "object.", (2, 6))
892
880
 
893
 
 
894
881
Command.hooks = CommandHooks()
895
882
 
896
883
 
903
890
    they take, and which commands will accept them.
904
891
    """
905
892
    # TODO: make it a method of the Command?
906
 
    parser = option.get_optparser(
907
 
        [v for k, v in sorted(command.options().items())])
 
893
    parser = option.get_optparser(command.options())
908
894
    if alias_argv is not None:
909
895
        args = alias_argv + argv
910
896
    else:
911
897
        args = argv
912
898
 
913
 
    # 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
914
900
    # option name is given.  See http://bugs.python.org/issue2931
915
901
    try:
916
902
        options, args = parser.parse_args(args)
917
 
    except UnicodeEncodeError:
 
903
    except UnicodeEncodeError as e:
918
904
        raise errors.BzrCommandError(
919
905
            gettext('Only ASCII permitted in option names'))
920
906
 
932
918
        if ap[-1] == '?':
933
919
            if args:
934
920
                argdict[argname] = args.pop(0)
935
 
        elif ap[-1] == '*':  # all remaining arguments
 
921
        elif ap[-1] == '*': # all remaining arguments
936
922
            if args:
937
923
                argdict[argname + '_list'] = args[:]
938
924
                args = []
941
927
        elif ap[-1] == '+':
942
928
            if not args:
943
929
                raise errors.BzrCommandError(gettext(
944
 
                    "command {0!r} needs one or more {1}").format(
945
 
                    cmd, argname.upper()))
 
930
                      "command {0!r} needs one or more {1}").format(
 
931
                      cmd, argname.upper()))
946
932
            else:
947
933
                argdict[argname + '_list'] = args[:]
948
934
                args = []
949
 
        elif ap[-1] == '$':  # all but one
 
935
        elif ap[-1] == '$': # all but one
950
936
            if len(args) < 2:
951
937
                raise errors.BzrCommandError(
952
 
                    gettext("command {0!r} needs one or more {1}").format(
953
 
                        cmd, argname.upper()))
 
938
                      gettext("command {0!r} needs one or more {1}").format(
 
939
                                             cmd, argname.upper()))
954
940
            argdict[argname + '_list'] = args[:-1]
955
941
            args[:-1] = []
956
942
        else:
958
944
            argname = ap
959
945
            if not args:
960
946
                raise errors.BzrCommandError(
961
 
                    gettext("command {0!r} requires argument {1}").format(
962
 
                        cmd, argname.upper()))
 
947
                     gettext("command {0!r} requires argument {1}").format(
 
948
                               cmd, argname.upper()))
963
949
            else:
964
950
                argdict[argname] = args.pop(0)
965
951
 
966
952
    if args:
967
 
        raise errors.BzrCommandError(gettext(
968
 
            "extra argument to command {0}: {1}").format(
969
 
            cmd, args[0]))
 
953
        raise errors.BzrCommandError( gettext(
 
954
                              "extra argument to command {0}: {1}").format(
 
955
                                       cmd, args[0]) )
970
956
 
971
957
    return argdict
972
958
 
974
960
def apply_coveraged(the_callable, *args, **kwargs):
975
961
    import coverage
976
962
    cov = coverage.Coverage()
977
 
    try:
978
 
        config_file = cov.config.config_file
979
 
    except AttributeError:  # older versions of coverage
980
 
        config_file = cov.config_file
981
 
    os.environ['COVERAGE_PROCESS_START'] = config_file
 
963
    os.environ['COVERAGE_PROCESS_START'] = cov.config_file
982
964
    cov.start()
983
965
    try:
984
966
        return exception_to_return_code(the_callable, *args, **kwargs)
996
978
        prof = hotshot.Profile(pfname)
997
979
        try:
998
980
            ret = prof.runcall(exception_to_return_code, the_callable, *args,
999
 
                               **kwargs) or 0
 
981
                **kwargs) or 0
1000
982
        finally:
1001
983
            prof.close()
1002
984
        stats = hotshot.stats.load(pfname)
1003
985
        stats.strip_dirs()
1004
986
        stats.sort_stats('cum')   # 'time'
1005
 
        # XXX: Might like to write to stderr or the trace file instead but
1006
 
        # 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
1007
989
        stats.print_stats(20)
1008
990
        return ret
1009
991
    finally:
1020
1002
    """
1021
1003
    try:
1022
1004
        return the_callable(*args, **kwargs)
1023
 
    except (KeyboardInterrupt, Exception):
 
1005
    except (KeyboardInterrupt, Exception) as e:
1024
1006
        # used to handle AssertionError and KeyboardInterrupt
1025
1007
        # specially here, but hopefully they're handled ok by the logger now
1026
1008
        exc_info = sys.exc_info()
1103
1085
        Generate code coverage report
1104
1086
 
1105
1087
    --concurrency
1106
 
        Specify the number of processes that can be run concurrently
1107
 
        (selftest).
 
1088
        Specify the number of processes that can be run concurrently (selftest).
1108
1089
    """
1109
1090
    trace.mutter("breezy version: " + breezy.__version__)
1110
1091
    argv = _specified_or_unicode_argv(argv)
1111
1092
    trace.mutter("brz arguments: %r", argv)
1112
1093
 
1113
1094
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1114
 
        opt_coverage = opt_no_l10n = opt_no_aliases = False
 
1095
            opt_coverage = opt_no_l10n = opt_no_aliases = False
1115
1096
    opt_lsprof_file = None
1116
1097
 
1117
1098
    # --no-plugins is handled specially at a very early stage. We need
1145
1126
        elif a == '--coverage':
1146
1127
            opt_coverage = True
1147
1128
        elif a == '--profile-imports':
1148
 
            pass  # already handled in startup script Bug #588277
 
1129
            pass # already handled in startup script Bug #588277
1149
1130
        elif a.startswith('-D'):
1150
1131
            debug.debug_flags.add(a[2:])
1151
1132
        elif a.startswith('-O'):
1214
1195
        if 'memory' in debug.debug_flags:
1215
1196
            trace.debug_memory('Process status after command:', short=False)
1216
1197
        option._verbosity_level = saved_verbosity_level
1217
 
        # Reset the overrides
 
1198
        # Reset the overrides 
1218
1199
        cmdline_overrides._reset()
1219
1200
 
1220
1201
 
1243
1224
    if _list_bzr_commands in Command.hooks["list_commands"]:
1244
1225
        return
1245
1226
    Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1246
 
                                     "bzr commands")
 
1227
        "bzr commands")
1247
1228
    Command.hooks.install_named_hook("get_command", _get_bzr_command,
1248
 
                                     "bzr commands")
 
1229
        "bzr commands")
1249
1230
    Command.hooks.install_named_hook("get_command", _get_plugin_command,
1250
 
                                     "bzr plugin commands")
 
1231
        "bzr plugin commands")
1251
1232
    Command.hooks.install_named_hook("get_command", _get_external_command,
1252
 
                                     "bzr external command lookup")
 
1233
        "bzr external command lookup")
1253
1234
    Command.hooks.install_named_hook("get_missing_command",
1254
1235
                                     _try_plugin_provider,
1255
1236
                                     "bzr plugin-provider-db check")
1256
1237
 
1257
1238
 
 
1239
 
1258
1240
def _specified_or_unicode_argv(argv):
1259
1241
    # For internal or testing use, argv can be passed.  Otherwise, get it from
1260
1242
    # the process arguments in a unicode-safe way.
1299
1281
    """Run a bzr command with parameters as described by argv.
1300
1282
 
1301
1283
    This function assumed that that UI layer is setup, that symbol deprecations
1302
 
    are already applied, and that unicode decoding has already been performed
1303
 
    on argv.
 
1284
    are already applied, and that unicode decoding has already been performed on argv.
1304
1285
    """
1305
1286
    # done here so that they're covered for every test run
1306
1287
    install_bzr_command_hooks()
1319
1300
        return run_bzr(argv)
1320
1301
    except Exception as e:
1321
1302
        if (isinstance(e, (OSError, IOError))
1322
 
                or not getattr(e, 'internal_error', True)):
 
1303
            or not getattr(e, 'internal_error', True)):
1323
1304
            trace.report_exception(sys.exc_info(), sys.stderr)
1324
1305
            return 3
1325
1306
        else:
1368
1349
        for key, provider in self.items():
1369
1350
            yield provider
1370
1351
 
1371
 
 
1372
1352
command_providers_registry = ProvidersRegistry()