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
22
24
# TODO: Specific "examples" property on commands for consistent formatting.
34
from .lazy_import import lazy_import
29
from bzrlib.lazy_import import lazy_import
35
30
lazy_import(globals(), """
47
from .hooks import Hooks
48
from .i18n import gettext
49
from bzrlib.hooks import Hooks
50
from bzrlib.i18n import gettext
49
51
# Compatibility - Option used to be in commands.
50
from .option import Option
51
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']))
52
from bzrlib.option import Option
53
from bzrlib.plugin import disable_plugins, load_plugins
54
from bzrlib import registry
81
57
class CommandInfo(object):
143
119
trace.warning('Two plugins defined the same command: %r' % k)
144
120
trace.warning('Not loading the one in %r' %
145
sys.modules[cmd.__module__])
121
sys.modules[cmd.__module__])
146
122
trace.warning('Previously this command was registered from %r' %
147
sys.modules[previous.__module__])
123
sys.modules[previous.__module__])
148
124
for a in cmd.aliases:
149
125
self._alias_dict[a] = k_unsquished
171
147
def register_command(cmd, decorate=False):
172
148
"""Register a plugin command.
174
Should generally be avoided in favor of lazy registration.
150
Should generally be avoided in favor of lazy registration.
176
152
global plugin_cmds
177
153
return plugin_cmds.register(cmd, decorate)
184
160
def _unsquish_command_name(cmd):
185
return cmd[4:].replace('_', '-')
161
return cmd[4:].replace('_','-')
188
164
def _register_builtin_commands():
189
165
if builtin_command_registry.keys():
192
import breezy.builtins
193
for cmd_class in _scan_module_for_commands(breezy.builtins):
168
import bzrlib.builtins
169
for cmd_class in _scan_module_for_commands(bzrlib.builtins).values():
194
170
builtin_command_registry.register(cmd_class)
195
breezy.builtins._register_lazy_builtins()
171
bzrlib.builtins._register_lazy_builtins()
198
174
def _scan_module_for_commands(module):
199
module_dict = module.__dict__
200
for name in module_dict:
176
for name, obj in module.__dict__.iteritems():
201
177
if name.startswith("cmd_"):
202
yield module_dict[name]
178
real_name = _unsquish_command_name(name)
205
183
def _list_bzr_commands(names):
206
184
"""Find commands from bzr's core and plugins.
208
This is not the public interface, just the default hook called by
186
This is not the public interface, just the default hook called by all_command_names.
211
188
# to eliminate duplicates
212
189
names.update(builtin_command_names())
240
217
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
220
def get_cmd_object(cmd_name, plugins_override=True):
290
221
"""Return the command object for a command.
296
227
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"')
229
raise errors.BzrCommandError(gettext('unknown command "%s"') % cmd_name)
308
232
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
346
class NoPluginAvailable(errors.BzrError):
350
270
def _try_plugin_provider(cmd_name):
351
271
"""Probe for a plugin provider having cmd_name."""
353
273
plugin_metadata, provider = probe_for_provider(cmd_name)
354
raise CommandAvailableInPlugin(cmd_name, plugin_metadata, provider)
355
except NoPluginAvailable:
274
raise errors.CommandAvailableInPlugin(cmd_name,
275
plugin_metadata, provider)
276
except errors.NoPluginAvailable:
367
288
for provider in command_providers_registry:
369
290
return provider.plugin_for_command(cmd_name), provider
370
except NoPluginAvailable:
291
except errors.NoPluginAvailable:
372
raise NoPluginAvailable(cmd_name)
293
raise errors.NoPluginAvailable(cmd_name)
375
296
def _get_bzr_command(cmd_or_None, cmd_name):
388
309
# Only do external command lookups when no command is found so far.
389
310
if cmd_or_None is not None:
390
311
return cmd_or_None
391
from breezy.externalcommand import ExternalCommand
312
from bzrlib.externalcommand import ExternalCommand
392
313
cmd_obj = ExternalCommand.find_command(cmd_name)
397
318
def _get_plugin_command(cmd_or_None, cmd_name):
398
"""Get a command from brz's plugins."""
319
"""Get a command from bzr's plugins."""
400
321
return plugin_cmds.get(cmd_name)()
410
331
class Command(object):
411
332
"""Base class for commands.
413
Commands are the heart of the command-line brz interface.
334
Commands are the heart of the command-line bzr interface.
415
336
The command object mostly handles the mapping of command-line
416
parameters into one or more breezy operations, and of the results
337
parameters into one or more bzrlib operations, and of the results
417
338
into textual output.
419
340
Commands normally don't have any state. All their arguments are
492
413
Functions will be called in LIFO order.
494
self._exit_stack.callback(cleanup_func, *args, **kwargs)
415
self._operation.add_cleanup(cleanup_func, *args, **kwargs)
496
417
def cleanup_now(self):
497
418
"""Execute and empty pending cleanup functions immediately.
506
427
as it releases all resources, this may release locks that the command
507
428
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)
430
self._operation.cleanup_now()
514
432
def _usage(self):
515
433
"""Return single-line grammar for this command.
517
435
Only describes arguments, not options.
519
s = 'brz ' + self.name() + ' '
437
s = 'bzr ' + self.name() + ' '
520
438
for aname in self.takes_args:
521
439
aname = aname.upper()
522
440
if aname[-1] in ['$', '+']:
551
469
# Note: If self.gettext() translates ':Usage:\n', the section will
552
470
# be shown after "Description" section and we don't want to
553
471
# translate the usage string.
554
# Though, brz export-pot don't exports :Usage: section and it must
472
# Though, bzr export-pot don't exports :Usage: section and it must
555
473
# not be translated.
556
474
doc = self.gettext(doc)
558
476
doc = gettext("No help for this command.")
560
478
# Extract the summary (purpose) and sections out from the text
561
purpose, sections, order = self._get_help_parts(doc)
479
purpose,sections,order = self._get_help_parts(doc)
563
481
# If a custom usage section was provided, use it
564
if 'Usage' in sections:
482
if sections.has_key('Usage'):
565
483
usage = sections.pop('Usage')
567
485
usage = self._usage()
580
498
# XXX: optparse implicitly rewraps the help, and not always perfectly,
581
499
# 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())])
501
parser = option.get_optparser(self.options())
585
502
options = parser.format_option_help()
586
503
# FIXME: According to the spec, ReST option lists actually don't
587
504
# support options like --1.14 so that causes syntax errors (in Sphinx
601
518
# Add the description, indenting it 2 spaces
602
519
# to match the indentation of the options
520
if sections.has_key(None):
604
521
text = sections.pop(None)
605
522
text = '\n '.join(text.splitlines())
606
523
result += gettext(':Description:\n %s\n\n') % (text,)
613
530
result += ':%s:\n%s\n' % (label, sections[label])
616
result += (gettext("See brz help %s for more details and examples.\n\n")
533
result += (gettext("See bzr help %s for more details and examples.\n\n")
619
536
# Add the aliases, source (plug-in) and see also links, if any
636
553
# Use a Sphinx link for this entry
637
554
link_text = gettext(":doc:`{0} <{1}-help>`").format(
639
556
see_also_links.append(link_text)
640
557
see_also = see_also_links
641
558
result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
643
560
# If this will be rendered as plain text, convert it
645
import breezy.help_topics
646
result = breezy.help_topics.help_as_plain_text(result)
562
import bzrlib.help_topics
563
result = bzrlib.help_topics.help_as_plain_text(result)
670
587
summary = lines.pop(0)
673
label, section = None, ''
590
label,section = None,''
674
591
for line in lines:
675
592
if line.startswith(':') and line.endswith(':') and len(line) > 2:
676
593
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()):
594
label,section = line[1:-1],''
595
elif (label is not None) and len(line) > 1 and not line[0].isspace():
680
596
save_section(sections, order, label, section)
681
label, section = None, line
597
label,section = None,line
683
599
if len(section) > 0:
684
600
section += '\n' + line
729
645
self._setup_outf()
731
647
# Process the standard options
732
if 'help' in opts: # e.g. brz add --help
648
if 'help' in opts: # e.g. bzr add --help
733
649
self.outf.write(self.get_help_text())
735
if 'usage' in opts: # e.g. brz add --usage
651
if 'usage' in opts: # e.g. bzr add --usage
736
652
self.outf.write(self.get_help_text(verbose=False))
738
654
trace.set_verbosity_level(option._verbosity_level)
739
655
if 'verbose' in self.supported_std_options:
740
656
opts['verbose'] = trace.is_verbose()
741
elif 'verbose' in opts:
657
elif opts.has_key('verbose'):
742
658
del opts['verbose']
743
659
if 'quiet' in self.supported_std_options:
744
660
opts['quiet'] = trace.is_quiet()
745
elif 'quiet' in opts:
661
elif opts.has_key('quiet'):
746
662
del opts['quiet']
747
663
# mix arguments and options into one dictionary
748
664
cmdargs = _match_argform(self.name(), self.takes_args, args)
773
689
you can override this method.
775
691
class_run = self.run
777
692
def run(*args, **kwargs):
778
693
for hook in Command.hooks['pre_command']:
695
self._operation = cleanup.OperationWithCleanups(class_run)
781
with contextlib.ExitStack() as self._exit_stack:
782
return class_run(*args, **kwargs)
697
return self._operation.run_simple(*args, **kwargs)
784
700
for hook in Command.hooks['post_command']:
795
711
shell error code if not. It's OK for this method to allow
796
712
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
714
This method is automatically wrapped by Command.__init__ with a
715
cleanup operation, stored as self._operation. This can be used
800
716
via self.add_cleanup to perform automatic cleanups at the end of
848
768
These are all empty initially, because by default nothing should get
851
Hooks.__init__(self, "breezy.commands", "Command.hooks")
771
Hooks.__init__(self, "bzrlib.commands", "Command.hooks")
772
self.add_hook('extend_command',
854
773
"Called after creating a command object to allow modifications "
855
774
"such as adding or removing options, docs etc. Called with the "
856
"new breezy.commands.Command object.", (1, 13))
775
"new bzrlib.commands.Command object.", (1, 13))
776
self.add_hook('get_command',
859
777
"Called when creating a single command. Called with "
860
778
"(cmd_or_None, command_name). get_command should either return "
861
779
"the cmd_or_None parameter, or a replacement Command object that "
862
780
"should be used for the command. Note that the Command.hooks "
863
781
"hooks are core infrastructure. Many users will prefer to use "
864
"breezy.commands.register_command or plugin_cmds.register_lazy.",
782
"bzrlib.commands.register_command or plugin_cmds.register_lazy.",
867
'get_missing_command',
784
self.add_hook('get_missing_command',
868
785
"Called when creating a single command if no command could be "
869
786
"found. Called with (command_name). get_missing_command should "
870
787
"either return None, or a Command object to be used for the "
871
788
"command.", (1, 17))
789
self.add_hook('list_commands',
874
790
"Called when enumerating commands. Called with a set of "
875
791
"cmd_name strings for all the commands found so far. This set "
876
792
" is safe to mutate - e.g. to remove a command. "
877
793
"list_commands should return the updated set of command names.",
795
self.add_hook('pre_command',
881
796
"Called prior to executing a command. Called with the command "
882
797
"object.", (2, 6))
798
self.add_hook('post_command',
885
799
"Called after executing a command. Called with the command "
886
800
"object.", (2, 6))
889
802
Command.hooks = CommandHooks()
898
811
they take, and which commands will accept them.
900
813
# TODO: make it a method of the Command?
901
parser = option.get_optparser(
902
[v for k, v in sorted(command.options().items())])
814
parser = option.get_optparser(command.options())
903
815
if alias_argv is not None:
904
816
args = alias_argv + argv
908
# python 2's optparse raises this exception if a non-ascii
820
# for python 2.5 and later, optparse raises this exception if a non-ascii
909
821
# option name is given. See http://bugs.python.org/issue2931
911
823
options, args = parser.parse_args(args)
912
except UnicodeEncodeError:
913
raise errors.CommandError(
824
except UnicodeEncodeError,e:
825
raise errors.BzrCommandError(
914
826
gettext('Only ASCII permitted in option names'))
916
opts = dict((k, v) for k, v in options.__dict__.items() if
917
v is not option.OptionParser.DEFAULT_VALUE)
828
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
829
v is not option.OptionParser.DEFAULT_VALUE])
918
830
return args, opts
935
847
argdict[argname + '_list'] = None
936
848
elif ap[-1] == '+':
938
raise errors.CommandError(gettext(
939
"command {0!r} needs one or more {1}").format(
940
cmd, argname.upper()))
850
raise errors.BzrCommandError(gettext(
851
"command {0!r} needs one or more {1}").format(
852
cmd, argname.upper()))
942
854
argdict[argname + '_list'] = args[:]
944
elif ap[-1] == '$': # all but one
856
elif ap[-1] == '$': # all but one
945
857
if len(args) < 2:
946
raise errors.CommandError(
947
gettext("command {0!r} needs one or more {1}").format(
948
cmd, argname.upper()))
858
raise errors.BzrCommandError(
859
gettext("command {0!r} needs one or more {1}").format(
860
cmd, argname.upper()))
949
861
argdict[argname + '_list'] = args[:-1]
952
864
# just a plain arg
955
raise errors.CommandError(
956
gettext("command {0!r} requires argument {1}").format(
957
cmd, argname.upper()))
867
raise errors.BzrCommandError(
868
gettext("command {0!r} requires argument {1}").format(
869
cmd, argname.upper()))
959
871
argdict[argname] = args.pop(0)
962
raise errors.CommandError(gettext(
963
"extra argument to command {0}: {1}").format(
874
raise errors.BzrCommandError( gettext(
875
"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
880
def apply_coveraged(dirname, the_callable, *args, **kwargs):
881
# Cannot use "import trace", as that would import bzrlib.trace instead of
882
# the standard library's trace.
883
trace = __import__('trace')
885
tracer = trace.Trace(count=1, trace=0)
886
sys.settrace(tracer.globaltrace)
887
threading.settrace(tracer.globaltrace)
979
890
return exception_to_return_code(the_callable, *args, **kwargs)
893
results = tracer.results()
894
results.write_results(show_missing=1, summary=False,
985
898
def apply_profiled(the_callable, *args, **kwargs):
991
904
prof = hotshot.Profile(pfname)
993
906
ret = prof.runcall(exception_to_return_code, the_callable, *args,
997
910
stats = hotshot.stats.load(pfname)
998
911
stats.strip_dirs()
999
912
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
913
## XXX: Might like to write to stderr or the trace file instead but
914
## print_stats seems hardcoded to stdout
1002
915
stats.print_stats(20)
1017
930
return the_callable(*args, **kwargs)
1018
except (KeyboardInterrupt, Exception):
931
except (KeyboardInterrupt, Exception), e:
1019
932
# used to handle AssertionError and KeyboardInterrupt
1020
933
# specially here, but hopefully they're handled ok by the logger now
1021
934
exc_info = sys.exc_info()
1022
935
exitcode = trace.report_exception(exc_info, sys.stderr)
1023
if os.environ.get('BRZ_PDB'):
1024
print('**** entering debugger')
936
if os.environ.get('BZR_PDB'):
937
print '**** entering debugger'
1026
939
pdb.post_mortem(exc_info[2])
1030
943
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
1031
from breezy.lsprof import profile
944
from bzrlib.lsprof import profile
1032
945
ret, stats = profile(exception_to_return_code, the_callable,
1033
946
*args, **kwargs)
1095
1008
Run under the Python lsprof profiler.
1098
Generate code coverage report
1011
Generate line coverage report in the specified directory.
1101
Specify the number of processes that can be run concurrently
1014
Specify the number of processes that can be run concurrently (selftest).
1104
trace.mutter("breezy version: " + breezy.__version__)
1016
trace.mutter("bazaar version: " + bzrlib.__version__)
1105
1017
argv = _specified_or_unicode_argv(argv)
1106
trace.mutter("brz arguments: %r", argv)
1018
trace.mutter("bzr arguments: %r", argv)
1108
1020
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1109
opt_coverage = opt_no_l10n = opt_no_aliases = False
1110
opt_lsprof_file = None
1021
opt_no_l10n = opt_no_aliases = False
1022
opt_lsprof_file = opt_coverage_dir = None
1112
1024
# --no-plugins is handled specially at a very early stage. We need
1113
1025
# to load plugins before doing other command parsing so that they
1135
1047
elif a == '--builtin':
1136
1048
opt_builtin = True
1137
1049
elif a == '--concurrency':
1138
os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1050
os.environ['BZR_CONCURRENCY'] = argv[i + 1]
1140
1052
elif a == '--coverage':
1053
opt_coverage_dir = argv[i + 1]
1142
1055
elif a == '--profile-imports':
1143
pass # already handled in startup script Bug #588277
1056
pass # already handled in startup script Bug #588277
1144
1057
elif a.startswith('-D'):
1145
1058
debug.debug_flags.add(a[2:])
1146
1059
elif a.startswith('-O'):
1149
1062
argv_copy.append(a)
1152
cmdline_overrides = breezy.get_global_state().cmdline_overrides
1065
if bzrlib.global_state is None:
1066
# FIXME: Workaround for users that imported bzrlib but didn't call
1067
# bzrlib.initialize -- vila 2012-01-19
1068
cmdline_overrides = config.CommandLineStore()
1070
cmdline_overrides = bzrlib.global_state.cmdline_overrides
1153
1071
cmdline_overrides._from_cmdline(override_config)
1155
1073
debug.set_debug_flags_from_config()
1157
1075
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
1078
disable_plugins()
1191
1106
saved_verbosity_level = option._verbosity_level
1192
1107
option._verbosity_level = 0
1109
if opt_coverage_dir:
1196
1111
'--coverage ignored, because --lsprof is in use.')
1197
1112
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1198
1113
elif opt_profile:
1114
if opt_coverage_dir:
1201
1116
'--coverage ignored, because --profile is in use.')
1202
1117
ret = apply_profiled(run, *run_argv)
1204
ret = apply_coveraged(run, *run_argv)
1118
elif opt_coverage_dir:
1119
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1206
1121
ret = run(*run_argv)
1207
1122
return ret or 0
1241
1156
if _list_bzr_commands in Command.hooks["list_commands"]:
1243
1158
Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1245
1160
Command.hooks.install_named_hook("get_command", _get_bzr_command,
1247
1162
Command.hooks.install_named_hook("get_command", _get_plugin_command,
1248
"bzr plugin commands")
1163
"bzr plugin commands")
1249
1164
Command.hooks.install_named_hook("get_command", _get_external_command,
1250
"bzr external command lookup")
1165
"bzr external command lookup")
1251
1166
Command.hooks.install_named_hook("get_missing_command",
1252
1167
_try_plugin_provider,
1253
1168
"bzr plugin-provider-db check")
1256
1172
def _specified_or_unicode_argv(argv):
1257
1173
# For internal or testing use, argv can be passed. Otherwise, get it from
1258
# the process arguments.
1174
# the process arguments in a unicode-safe way.
1259
1175
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.")
1176
return osutils.get_unicode_argv()
1180
# ensure all arguments are unicode strings
1182
if isinstance(a, unicode):
1185
new_argv.append(a.decode('ascii'))
1186
except UnicodeDecodeError:
1187
raise errors.BzrError("argv should be list of unicode strings.")
1273
1191
def main(argv=None):
1274
1192
"""Main entry point of command-line interface.
1276
Typically `breezy.initialize` should be called first.
1194
Typically `bzrlib.initialize` should be called first.
1278
1196
:param argv: list of unicode command-line arguments similar to sys.argv.
1279
1197
argv[0] is script name usually, it will be ignored.
1280
1198
Don't pass here sys.argv because this list contains plain strings
1281
1199
and not unicode; pass None instead.
1283
:return: exit code of brz command.
1201
:return: exit code of bzr command.
1285
1203
if argv is not None:
1286
1204
argv = argv[1:]
1294
1212
"""Run a bzr command with parameters as described by argv.
1296
1214
This function assumed that that UI layer is setup, that symbol deprecations
1297
are already applied, and that unicode decoding has already been performed
1215
are already applied, and that unicode decoding has already been performed on argv.
1300
1217
# done here so that they're covered for every test run
1301
1218
install_bzr_command_hooks()
1305
1222
def run_bzr_catch_user_errors(argv):
1306
"""Run brz and report user errors, but let internal errors propagate.
1223
"""Run bzr and report user errors, but let internal errors propagate.
1308
1225
This is used for the test suite, and might be useful for other programs
1309
1226
that want to wrap the commandline interface.
1312
1229
install_bzr_command_hooks()
1314
1231
return run_bzr(argv)
1315
except Exception as e:
1232
except Exception, e:
1316
1233
if (isinstance(e, (OSError, IOError))
1317
or not getattr(e, 'internal_error', True)):
1234
or not getattr(e, 'internal_error', True)):
1318
1235
trace.report_exception(sys.exc_info(), sys.stderr)