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 brzlib.lazy_import import lazy_import
35
30
lazy_import(globals(), """
47
from .hooks import Hooks
48
from .i18n import gettext
49
from brzlib.hooks import Hooks
50
from brzlib.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.BzrCommandError):
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 brzlib.option import Option
53
from brzlib.plugin import disable_plugins, load_plugins
54
from brzlib import registry
81
57
class CommandInfo(object):
94
70
class CommandRegistry(registry.Registry):
95
71
"""Special registry mapping command names to command classes.
97
73
:ivar overridden_registry: Look in this registry for commands being
98
74
overridden by this registry. This can be used to tell plugin commands
99
75
about the builtin they're decorating.
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 brzlib.builtins
169
for cmd_class in _scan_module_for_commands(brzlib.builtins).values():
194
170
builtin_command_registry.register(cmd_class)
195
breezy.builtins._register_lazy_builtins()
171
brzlib.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.BzrCommandError(
302
gettext('unknown command "%s". Perhaps you meant "%s"')
303
% (cmd_name, candidate))
304
raise errors.BzrCommandError(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 brzlib.externalcommand import ExternalCommand
392
313
cmd_obj = ExternalCommand.find_command(cmd_name)
413
334
Commands are the heart of the command-line brz 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 brzlib 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.
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 brzlib.help_topics
563
result = brzlib.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
710
626
Maps from long option name to option object."""
711
627
r = Option.STD_OPTIONS.copy()
713
629
for o in self.takes_options:
714
if isinstance(o, str):
630
if isinstance(o, basestring):
715
631
o = option.Option.OPTIONS[o]
717
633
if o.name in std_names:
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
837
753
:return: The name of the plugin or None if the command is builtin.
839
return plugin_name(self.__module__)
755
mod_parts = self.__module__.split('.')
756
if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
842
762
class CommandHooks(Hooks):
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, "brzlib.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 brzlib.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
"brzlib.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:
824
except UnicodeEncodeError,e:
913
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
936
848
elif ap[-1] == '+':
938
850
raise errors.BzrCommandError(gettext(
939
"command {0!r} needs one or more {1}").format(
940
cmd, argname.upper()))
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
858
raise errors.BzrCommandError(
947
gettext("command {0!r} needs one or more {1}").format(
948
cmd, argname.upper()))
859
gettext("command {0!r} needs one or more {1}").format(
860
cmd, argname.upper()))
949
861
argdict[argname + '_list'] = args[:-1]
955
867
raise errors.BzrCommandError(
956
gettext("command {0!r} requires argument {1}").format(
957
cmd, argname.upper()))
868
gettext("command {0!r} requires argument {1}").format(
869
cmd, argname.upper()))
959
871
argdict[argname] = args.pop(0)
962
raise errors.BzrCommandError(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 brzlib.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 brzlib.lsprof import profile
1032
945
ret, stats = profile(exception_to_return_code, the_callable,
1033
946
*args, **kwargs)
1051
964
If it is unspecified, the global config will be used.
1053
966
if config is None:
1054
import breezy.config
1055
config = breezy.config.GlobalConfig()
968
config = brzlib.config.GlobalConfig()
1056
969
alias = config.get_alias(cmd)
1058
971
return cmdline.split(alias)
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("breezy version: " + brzlib.__version__)
1105
1017
argv = _specified_or_unicode_argv(argv)
1106
1018
trace.mutter("brz 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 brzlib.global_state is None:
1066
# FIXME: Workaround for users that imported brzlib but didn't call
1067
# brzlib.initialize -- vila 2012-01-19
1068
cmdline_overrides = config.CommandLineStore()
1070
cmdline_overrides = brzlib.global_state.cmdline_overrides
1153
1071
cmdline_overrides._from_cmdline(override_config)
1155
1073
debug.set_debug_flags_from_config()
1188
1106
saved_verbosity_level = option._verbosity_level
1189
1107
option._verbosity_level = 0
1109
if opt_coverage_dir:
1193
1111
'--coverage ignored, because --lsprof is in use.')
1194
1112
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1195
1113
elif opt_profile:
1114
if opt_coverage_dir:
1198
1116
'--coverage ignored, because --profile is in use.')
1199
1117
ret = apply_profiled(run, *run_argv)
1201
ret = apply_coveraged(run, *run_argv)
1118
elif opt_coverage_dir:
1119
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1203
1121
ret = run(*run_argv)
1204
1122
return ret or 0
1238
1156
if _list_bzr_commands in Command.hooks["list_commands"]:
1240
1158
Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1242
1160
Command.hooks.install_named_hook("get_command", _get_bzr_command,
1244
1162
Command.hooks.install_named_hook("get_command", _get_plugin_command,
1245
"bzr plugin commands")
1163
"bzr plugin commands")
1246
1164
Command.hooks.install_named_hook("get_command", _get_external_command,
1247
"bzr external command lookup")
1165
"bzr external command lookup")
1248
1166
Command.hooks.install_named_hook("get_missing_command",
1249
1167
_try_plugin_provider,
1250
1168
"bzr plugin-provider-db check")
1253
1172
def _specified_or_unicode_argv(argv):
1254
1173
# For internal or testing use, argv can be passed. Otherwise, get it from
1255
1174
# the process arguments in a unicode-safe way.
1256
1175
if argv is None:
1257
1176
return osutils.get_unicode_argv()
1260
# ensure all arguments are unicode strings
1262
if not isinstance(a, str):
1263
raise ValueError('not native str or unicode: %r' % (a,))
1265
except (ValueError, UnicodeDecodeError):
1266
raise errors.BzrError("argv should be list of unicode strings.")
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.")
1270
1191
def main(argv=None):
1271
1192
"""Main entry point of command-line interface.
1273
Typically `breezy.initialize` should be called first.
1194
Typically `brzlib.initialize` should be called first.
1275
1196
:param argv: list of unicode command-line arguments similar to sys.argv.
1276
1197
argv[0] is script name usually, it will be ignored.
1291
1212
"""Run a bzr command with parameters as described by argv.
1293
1214
This function assumed that that UI layer is setup, that symbol deprecations
1294
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.
1297
1217
# done here so that they're covered for every test run
1298
1218
install_bzr_command_hooks()
1302
1222
def run_bzr_catch_user_errors(argv):
1303
"""Run brz and report user errors, but let internal errors propagate.
1223
"""Run bzr and report user errors, but let internal errors propagate.
1305
1225
This is used for the test suite, and might be useful for other programs
1306
1226
that want to wrap the commandline interface.
1309
1229
install_bzr_command_hooks()
1311
1231
return run_bzr(argv)
1312
except Exception as e:
1232
except Exception, e:
1313
1233
if (isinstance(e, (OSError, IOError))
1314
or not getattr(e, 'internal_error', True)):
1234
or not getattr(e, 'internal_error', True)):
1315
1235
trace.report_exception(sys.exc_info(), sys.stderr)
1357
1277
"""This registry exists to allow other providers to exist"""
1359
1279
def __iter__(self):
1360
for key, provider in self.items():
1280
for key, provider in self.iteritems():
1364
1283
command_providers_registry = ProvidersRegistry()