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
17
from __future__ import absolute_import
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
50
51
from .option import Option
51
52
from .plugin import disable_plugins, load_plugins, plugin_name
52
53
from . import errors, registry
55
class BzrOptionError(errors.CommandError):
60
class BzrOptionError(errors.BzrCommandError):
57
62
_fmt = "Error in command line options"
94
99
class CommandRegistry(registry.Registry):
95
100
"""Special registry mapping command names to command classes.
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.
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
171
176
def register_command(cmd, decorate=False):
172
177
"""Register a plugin command.
174
Should generally be avoided in favor of lazy registration.
179
Should generally be avoided in favor of lazy registration.
176
181
global plugin_cmds
177
182
return plugin_cmds.register(cmd, decorate)
205
210
def _list_bzr_commands(names):
206
211
"""Find commands from bzr's core and plugins.
208
This is not the public interface, just the default hook called by
213
This is not the public interface, just the default hook called by all_command_names.
211
215
# to eliminate duplicates
212
216
names.update(builtin_command_names())
259
263
names.update(cmd.aliases)
260
264
# candidate: modified levenshtein distance against cmd_name.
266
from . import patiencediff
263
267
for name in sorted(names):
264
268
matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
266
270
opcodes = matcher.get_opcodes()
267
271
for opcode, l1, l2, r1, r2 in opcodes:
268
272
if opcode == 'delete':
270
274
elif opcode == 'replace':
271
distance += max(l2 - l1, r2 - l1)
275
distance += max(l2-l1, r2-l1)
272
276
elif opcode == 'insert':
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
raise errors.BzrCommandError(
306
gettext('unknown command "%s". Perhaps you meant "%s"')
307
% (cmd_name, candidate))
308
raise errors.BzrCommandError(gettext('unknown command "%s"')
308
312
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
492
496
Functions will be called in LIFO order.
494
self._exit_stack.callback(cleanup_func, *args, **kwargs)
498
self._operation.add_cleanup(cleanup_func, *args, **kwargs)
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.
509
self._exit_stack.close()
511
def enter_context(self, cm):
512
return self._exit_stack.enter_context(cm)
513
self._operation.cleanup_now()
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
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
636
636
# Use a Sphinx link for this entry
637
637
link_text = gettext(":doc:`{0} <{1}-help>`").format(
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
773
772
you can override this method.
775
774
class_run = self.run
777
775
def run(*args, **kwargs):
778
776
for hook in Command.hooks['pre_command']:
778
self._operation = cleanup.OperationWithCleanups(class_run)
781
with contextlib.ExitStack() as self._exit_stack:
782
return class_run(*args, **kwargs)
780
return self._operation.run_simple(*args, **kwargs)
784
783
for hook in Command.hooks['post_command']:
795
794
shell error code if not. It's OK for this method to allow
796
795
an exception to raise up.
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
851
850
Hooks.__init__(self, "breezy.commands", "Command.hooks")
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))
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.",
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))
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.",
874
self.add_hook('pre_command',
881
875
"Called prior to executing a command. Called with the command "
882
876
"object.", (2, 6))
877
self.add_hook('post_command',
885
878
"Called after executing a command. Called with the command "
886
879
"object.", (2, 6))
889
881
Command.hooks = CommandHooks()
898
890
they take, and which commands will accept them.
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
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
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'))
916
907
opts = dict((k, v) for k, v in options.__dict__.items() if
935
926
argdict[argname + '_list'] = None
936
927
elif ap[-1] == '+':
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()))
942
933
argdict[argname + '_list'] = 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]
952
943
# just a plain arg
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()))
959
950
argdict[argname] = args.pop(0)
962
raise errors.CommandError(gettext(
963
"extra argument to command {0}: {1}").format(
953
raise errors.BzrCommandError( gettext(
954
"extra argument to command {0}: {1}").format(
969
def apply_coveraged(the_callable, *args, **kwargs):
971
cov = coverage.Coverage()
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
960
def apply_coveraged(dirname, the_callable, *args, **kwargs):
962
tracer = trace.Trace(count=1, trace=0)
963
sys.settrace(tracer.globaltrace)
964
threading.settrace(tracer.globaltrace)
979
967
return exception_to_return_code(the_callable, *args, **kwargs)
970
results = tracer.results()
971
results.write_results(show_missing=1, summary=False,
985
975
def apply_profiled(the_callable, *args, **kwargs):
991
981
prof = hotshot.Profile(pfname)
993
983
ret = prof.runcall(exception_to_return_code, the_callable, *args,
997
987
stats = hotshot.stats.load(pfname)
998
988
stats.strip_dirs()
999
989
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
990
## XXX: Might like to write to stderr or the trace file instead but
991
## print_stats seems hardcoded to stdout
1002
992
stats.print_stats(20)
1017
1007
return the_callable(*args, **kwargs)
1018
except (KeyboardInterrupt, Exception):
1008
except (KeyboardInterrupt, Exception) as e:
1019
1009
# used to handle AssertionError and KeyboardInterrupt
1020
1010
# specially here, but hopefully they're handled ok by the logger now
1021
1011
exc_info = sys.exc_info()
1095
1085
Run under the Python lsprof profiler.
1098
Generate code coverage report
1088
Generate line coverage report in the specified directory.
1101
Specify the number of processes that can be run concurrently
1091
Specify the number of processes that can be run concurrently (selftest).
1104
1093
trace.mutter("breezy version: " + breezy.__version__)
1105
1094
argv = _specified_or_unicode_argv(argv)
1106
1095
trace.mutter("brz arguments: %r", argv)
1108
1097
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1109
opt_coverage = opt_no_l10n = opt_no_aliases = False
1110
opt_lsprof_file = None
1098
opt_no_l10n = opt_no_aliases = False
1099
opt_lsprof_file = opt_coverage_dir = None
1112
1101
# --no-plugins is handled specially at a very early stage. We need
1113
1102
# to load plugins before doing other command parsing so that they
1138
1127
os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1140
1129
elif a == '--coverage':
1130
opt_coverage_dir = argv[i + 1]
1142
1132
elif a == '--profile-imports':
1143
pass # already handled in startup script Bug #588277
1133
pass # already handled in startup script Bug #588277
1144
1134
elif a.startswith('-D'):
1145
1135
debug.debug_flags.add(a[2:])
1146
1136
elif a.startswith('-O'):
1155
1145
debug.set_debug_flags_from_config()
1157
1147
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)
1163
1150
disable_plugins()
1191
1178
saved_verbosity_level = option._verbosity_level
1192
1179
option._verbosity_level = 0
1181
if opt_coverage_dir:
1196
1183
'--coverage ignored, because --lsprof is in use.')
1197
1184
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1198
1185
elif opt_profile:
1186
if opt_coverage_dir:
1201
1188
'--coverage ignored, because --profile is in use.')
1202
1189
ret = apply_profiled(run, *run_argv)
1204
ret = apply_coveraged(run, *run_argv)
1190
elif opt_coverage_dir:
1191
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1206
1193
ret = run(*run_argv)
1207
1194
return ret or 0
1241
1228
if _list_bzr_commands in Command.hooks["list_commands"]:
1243
1230
Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1245
1232
Command.hooks.install_named_hook("get_command", _get_bzr_command,
1247
1234
Command.hooks.install_named_hook("get_command", _get_plugin_command,
1248
"bzr plugin commands")
1235
"bzr plugin commands")
1249
1236
Command.hooks.install_named_hook("get_command", _get_external_command,
1250
"bzr external command lookup")
1237
"bzr external command lookup")
1251
1238
Command.hooks.install_named_hook("get_missing_command",
1252
1239
_try_plugin_provider,
1253
1240
"bzr plugin-provider-db check")
1256
1244
def _specified_or_unicode_argv(argv):
1257
1245
# For internal or testing use, argv can be passed. Otherwise, get it from
1258
# the process arguments.
1246
# the process arguments in a unicode-safe way.
1259
1247
if argv is None:
1248
return osutils.get_unicode_argv()
1263
1251
# ensure all arguments are unicode strings
1265
if not isinstance(a, str):
1253
if not isinstance(a, string_types):
1266
1254
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')
1267
1258
new_argv.append(a)
1268
1259
except (ValueError, UnicodeDecodeError):
1269
1260
raise errors.BzrError("argv should be list of unicode strings.")
1294
1285
"""Run a bzr command with parameters as described by argv.
1296
1287
This function assumed that that UI layer is setup, that symbol deprecations
1297
are already applied, and that unicode decoding has already been performed
1288
are already applied, and that unicode decoding has already been performed on argv.
1300
1290
# done here so that they're covered for every test run
1301
1291
install_bzr_command_hooks()
1314
1304
return run_bzr(argv)
1315
1305
except Exception as e:
1316
1306
if (isinstance(e, (OSError, IOError))
1317
or not getattr(e, 'internal_error', True)):
1307
or not getattr(e, 'internal_error', True)):
1318
1308
trace.report_exception(sys.exc_info(), sys.stderr)