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
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
55
class BzrOptionError(errors.CommandError):
57
_fmt = "Error in command line options"
60
class CommandAvailableInPlugin(Exception):
62
internal_error = False
64
def __init__(self, cmd_name, plugin_metadata, provider):
66
self.plugin_metadata = plugin_metadata
67
self.cmd_name = cmd_name
68
self.provider = provider
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']))
54
from . import registry
81
60
class CommandInfo(object):
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
171
150
def register_command(cmd, decorate=False):
172
151
"""Register a plugin command.
174
Should generally be avoided in favor of lazy registration.
153
Should generally be avoided in favor of lazy registration.
176
155
global plugin_cmds
177
156
return plugin_cmds.register(cmd, decorate)
205
184
def _list_bzr_commands(names):
206
185
"""Find commands from bzr's core and plugins.
208
This is not the public interface, just the default hook called by
187
This is not the public interface, just the default hook called by all_command_names.
211
189
# to eliminate duplicates
212
190
names.update(builtin_command_names())
240
218
return plugin_cmds.keys()
243
# Overrides for common mispellings that heuristics get wrong
245
'ic': {'ci': 0}, # heuristic finds nick
249
def guess_command(cmd_name):
250
"""Guess what command a user typoed.
252
:param cmd_name: Command to search for
253
:return: None if no command was found, name of a command otherwise
256
for name in all_command_names():
258
cmd = get_cmd_object(name)
259
names.update(cmd.aliases)
260
# candidate: modified levenshtein distance against cmd_name.
263
for name in sorted(names):
264
matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
266
opcodes = matcher.get_opcodes()
267
for opcode, l1, l2, r1, r2 in opcodes:
268
if opcode == 'delete':
270
elif opcode == 'replace':
271
distance += max(l2 - l1, r2 - l1)
272
elif opcode == 'insert':
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)
285
candidate = costs[0][1]
289
221
def get_cmd_object(cmd_name, plugins_override=True):
290
222
"""Return the command object for a command.
296
228
return _get_cmd_object(cmd_name, plugins_override)
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"')
230
raise errors.BzrCommandError(gettext('unknown command "%s"') % cmd_name)
308
233
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
346
class NoPluginAvailable(errors.BzrError):
350
271
def _try_plugin_provider(cmd_name):
351
272
"""Probe for a plugin provider having cmd_name."""
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:
367
289
for provider in command_providers_registry:
369
291
return provider.plugin_for_command(cmd_name), provider
370
except NoPluginAvailable:
292
except errors.NoPluginAvailable:
372
raise NoPluginAvailable(cmd_name)
294
raise errors.NoPluginAvailable(cmd_name)
375
297
def _get_bzr_command(cmd_or_None, cmd_name):
492
414
Functions will be called in LIFO order.
494
self._exit_stack.callback(cleanup_func, *args, **kwargs)
416
self._operation.add_cleanup(cleanup_func, *args, **kwargs)
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.
509
self._exit_stack.close()
511
def enter_context(self, cm):
512
return self._exit_stack.enter_context(cm)
431
self._operation.cleanup_now()
514
433
def _usage(self):
515
434
"""Return single-line grammar for this command.
558
477
doc = gettext("No help for this command.")
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)
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
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
670
588
summary = lines.pop(0)
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
683
600
if len(section) > 0:
684
601
section += '\n' + line
773
690
you can override this method.
775
692
class_run = self.run
777
693
def run(*args, **kwargs):
778
694
for hook in Command.hooks['pre_command']:
696
self._operation = cleanup.OperationWithCleanups(class_run)
781
with contextlib.ExitStack() as self._exit_stack:
782
return class_run(*args, **kwargs)
698
return self._operation.run_simple(*args, **kwargs)
784
701
for hook in Command.hooks['post_command']:
795
712
shell error code if not. It's OK for this method to allow
796
713
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
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
851
768
Hooks.__init__(self, "breezy.commands", "Command.hooks")
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))
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.",
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))
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.",
792
self.add_hook('pre_command',
881
793
"Called prior to executing a command. Called with the command "
882
794
"object.", (2, 6))
795
self.add_hook('post_command',
885
796
"Called after executing a command. Called with the command "
886
797
"object.", (2, 6))
889
799
Command.hooks = CommandHooks()
898
808
they take, and which commands will accept them.
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
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
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'))
916
825
opts = dict((k, v) for k, v in options.__dict__.items() if
935
844
argdict[argname + '_list'] = None
936
845
elif ap[-1] == '+':
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()))
942
851
argdict[argname + '_list'] = 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]
952
861
# just a plain arg
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()))
959
868
argdict[argname] = args.pop(0)
962
raise errors.CommandError(gettext(
963
"extra argument to command {0}: {1}").format(
871
raise errors.BzrCommandError( gettext(
872
"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
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')
882
tracer = trace.Trace(count=1, trace=0)
883
sys.settrace(tracer.globaltrace)
884
threading.settrace(tracer.globaltrace)
979
887
return exception_to_return_code(the_callable, *args, **kwargs)
890
results = tracer.results()
891
results.write_results(show_missing=1, summary=False,
985
895
def apply_profiled(the_callable, *args, **kwargs):
991
901
prof = hotshot.Profile(pfname)
993
903
ret = prof.runcall(exception_to_return_code, the_callable, *args,
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)
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.
1098
Generate code coverage report
1008
Generate line coverage report in the specified directory.
1101
Specify the number of processes that can be run concurrently
1011
Specify the number of processes that can be run concurrently (selftest).
1104
1013
trace.mutter("breezy version: " + breezy.__version__)
1105
1014
argv = _specified_or_unicode_argv(argv)
1106
1015
trace.mutter("brz arguments: %r", argv)
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
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]
1140
1049
elif a == '--coverage':
1050
opt_coverage_dir = argv[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)
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()
1067
cmdline_overrides = breezy.global_state.cmdline_overrides
1153
1068
cmdline_overrides._from_cmdline(override_config)
1155
1070
debug.set_debug_flags_from_config()
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)
1163
1075
disable_plugins()
1191
1103
saved_verbosity_level = option._verbosity_level
1192
1104
option._verbosity_level = 0
1106
if opt_coverage_dir:
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:
1111
if opt_coverage_dir:
1201
1113
'--coverage ignored, because --profile is in use.')
1202
1114
ret = apply_profiled(run, *run_argv)
1204
ret = apply_coveraged(run, *run_argv)
1115
elif opt_coverage_dir:
1116
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1206
1118
ret = run(*run_argv)
1207
1119
return ret or 0
1241
1153
if _list_bzr_commands in Command.hooks["list_commands"]:
1243
1155
Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1245
1157
Command.hooks.install_named_hook("get_command", _get_bzr_command,
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")
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:
1263
# ensure all arguments are unicode strings
1265
if not isinstance(a, str):
1266
raise ValueError('not native str or unicode: %r' % (a,))
1268
except (ValueError, UnicodeDecodeError):
1269
raise errors.BzrError("argv should be list of unicode strings.")
1173
return osutils.get_unicode_argv()
1177
# ensure all arguments are unicode strings
1179
if isinstance(a, unicode):
1182
new_argv.append(a.decode('ascii'))
1183
except UnicodeDecodeError:
1184
raise errors.BzrError("argv should be list of unicode strings.")
1273
1188
def main(argv=None):
1294
1209
"""Run a bzr command with parameters as described by argv.
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
1212
are already applied, and that unicode decoding has already been performed on argv.
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)