28
28
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
29
29
# the profile output behind so it can be interactively examined?
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
34
38
from warnings import warn
38
import bzrlib.errors as errors
39
from bzrlib.errors import (BzrError,
43
from bzrlib import option
50
from bzrlib.symbol_versioning import (
44
57
from bzrlib.option import Option
46
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
48
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
67
77
if k_unsquished not in plugin_cmds:
68
78
plugin_cmds[k_unsquished] = cmd
69
mutter('registered plugin command %s', k_unsquished)
79
## trace.mutter('registered plugin command %s', k_unsquished)
70
80
if decorate and k_unsquished in builtin_command_names():
71
81
return _builtin_commands()[k_unsquished]
74
84
plugin_cmds[k_unsquished] = cmd
77
log_error('Two plugins defined the same command: %r' % k)
78
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
87
trace.log_error('Two plugins defined the same command: %r' % k)
88
trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
81
91
def _squish_command_name(cmd):
128
138
If true, plugin commands can override builtins.
141
return _get_cmd_object(cmd_name, plugins_override)
143
raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
146
def _get_cmd_object(cmd_name, plugins_override=True):
147
"""Worker for get_cmd_object which raises KeyError rather than BzrCommandError."""
130
148
from bzrlib.externalcommand import ExternalCommand
132
150
# We want only 'ascii' command names, but the user may have typed
204
221
replace - put in a bogus character (typically '?')
205
222
exact - do not encode sys.stdout
224
NOTE: by default on Windows, sys.stdout is opened as a text
225
stream, therefore LF line-endings are converted to CRLF.
226
When a command uses encoding_type = 'exact', then
227
sys.stdout is forced to be a binary stream, and line-endings
217
240
if self.__doc__ == Command.__doc__:
218
241
warn("No help message set for %r" % self)
244
"""Return single-line grammar for this command.
246
Only describes arguments, not options.
248
s = 'bzr ' + self.name() + ' '
249
for aname in self.takes_args:
250
aname = aname.upper()
251
if aname[-1] in ['$', '+']:
252
aname = aname[:-1] + '...'
253
elif aname[-1] == '?':
254
aname = '[' + aname[:-1] + ']'
255
elif aname[-1] == '*':
256
aname = '[' + aname[:-1] + '...]'
263
def get_help_text(self, additional_see_also=None):
264
"""Return a text string with help for this command.
266
:param additional_see_also: Additional help topics to be
271
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
274
result += 'usage: %s\n' % self._usage()
277
result += 'aliases: '
278
result += ', '.join(self.aliases) + '\n'
282
plugin_name = self.plugin_name()
283
if plugin_name is not None:
284
result += '(From plugin "%s")' % plugin_name
288
if result[-1] != '\n':
291
result += option.get_optparser(self.options()).format_option_help()
292
see_also = self.get_see_also(additional_see_also)
294
result += '\nSee also: '
295
result += ', '.join(see_also)
299
def get_help_topic(self):
300
"""Return the commands help topic - its name."""
303
def get_see_also(self, additional_terms=None):
304
"""Return a list of help topics that are related to this ommand.
306
The list is derived from the content of the _see_also attribute. Any
307
duplicates are removed and the result is in lexical order.
308
:param additional_terms: Additional help topics to cross-reference.
309
:return: A list of help topics.
311
see_also = set(getattr(self, '_see_also', []))
313
see_also.update(additional_terms)
314
return sorted(see_also)
220
316
def options(self):
221
317
"""Return dict of valid options for this command.
223
319
Maps from long option name to option object."""
225
r['help'] = Option.OPTIONS['help']
321
r['help'] = option.Option.OPTIONS['help']
226
322
for o in self.takes_options:
227
323
if isinstance(o, basestring):
228
o = Option.OPTIONS[o]
324
o = option.Option.OPTIONS[o]
236
332
# Originally I was using self.stdout, but that looks
237
333
# *way* too much like sys.stdout
238
334
if self.encoding_type == 'exact':
335
# force sys.stdout to be binary stream on win32
336
if sys.platform == 'win32':
337
fileno = getattr(sys.stdout, 'fileno', None)
340
msvcrt.setmode(fileno(), os.O_BINARY)
239
341
self.outf = sys.stdout
242
output_encoding = bzrlib.osutils.get_terminal_encoding()
344
output_encoding = osutils.get_terminal_encoding()
244
346
# use 'replace' so that we don't abort if trying to write out
245
347
# in e.g. the default C locale.
249
351
# bogus. So set the attribute, so we can find the correct encoding later.
250
352
self.outf.encoding = output_encoding
252
@deprecated_method(zero_eight)
253
def run_argv(self, argv):
254
"""Parse command line and run.
256
See run_argv_aliases for the 0.8 and beyond api.
258
return self.run_argv_aliases(argv)
260
354
def run_argv_aliases(self, argv, alias_argv=None):
261
355
"""Parse the command line and run with extra aliases in alias_argv."""
263
warn("Passing None for [] is deprecated from bzrlib 0.10",
357
warn("Passing None for [] is deprecated from bzrlib 0.10",
264
358
DeprecationWarning, stacklevel=2)
266
360
args, opts = parse_args(self, argv, alias_argv)
267
361
if 'help' in opts: # e.g. bzr add --help
268
from bzrlib.help import help_on_command
269
help_on_command(self.name())
362
sys.stdout.write(self.get_help_text())
271
364
# mix arguments and options into one dictionary
272
365
cmdargs = _match_argform(self.name(), self.takes_args, args)
365
461
options, args = parser.parse_args(args)
366
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
462
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
367
463
v is not option.OptionParser.DEFAULT_VALUE])
368
464
return args, opts
385
481
argdict[argname + '_list'] = None
386
482
elif ap[-1] == '+':
388
raise BzrCommandError("command %r needs one or more %s"
389
% (cmd, argname.upper()))
484
raise errors.BzrCommandError("command %r needs one or more %s"
485
% (cmd, argname.upper()))
391
487
argdict[argname + '_list'] = args[:]
393
489
elif ap[-1] == '$': # all but one
394
490
if len(args) < 2:
395
raise BzrCommandError("command %r needs one or more %s"
396
% (cmd, argname.upper()))
491
raise errors.BzrCommandError("command %r needs one or more %s"
492
% (cmd, argname.upper()))
397
493
argdict[argname + '_list'] = args[:-1]
400
496
# just a plain arg
403
raise BzrCommandError("command %r requires argument %s"
404
% (cmd, argname.upper()))
499
raise errors.BzrCommandError("command %r requires argument %s"
500
% (cmd, argname.upper()))
406
502
argdict[argname] = args.pop(0)
409
raise BzrCommandError("extra argument to command %s: %s"
505
raise errors.BzrCommandError("extra argument to command %s: %s"
439
535
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
440
536
from bzrlib.lsprof import profile
442
537
ret, stats = profile(the_callable, *args, **kwargs)
444
539
if filename is None:
448
cPickle.dump(stats, open(filename, 'w'), 2)
449
print 'Profile data written to %r.' % filename
543
trace.note('Profile data written to "%s".', filename)
454
"""Return an expanded alias, or None if no alias exists"""
456
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
547
def get_alias(cmd, config=None):
548
"""Return an expanded alias, or None if no alias exists.
551
Command to be checked for an alias.
553
Used to specify an alternative config to use,
554
which is especially useful for testing.
555
If it is unspecified, the global config will be used.
559
config = bzrlib.config.GlobalConfig()
560
alias = config.get_alias(cmd)
458
return alias.split(' ')
563
return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
557
665
# 'command not found' error later.
559
667
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
560
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
561
run = cmd_obj.run_argv
564
run = cmd_obj.run_argv_aliases
565
run_argv = [argv, alias_argv]
668
run = cmd_obj.run_argv_aliases
669
run_argv = [argv, alias_argv]
589
693
if e.errno != errno.EPIPE:
590
694
# Win32 raises IOError with errno=0 on a broken pipe
591
if sys.platform != 'win32' or e.errno != 0:
695
if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
594
698
except KeyboardInterrupt:
602
706
bzrlib.ui.ui_factory = TextUIFactory()
603
707
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
604
708
ret = run_bzr_catch_errors(argv)
605
mutter("return code %d", ret)
709
trace.mutter("return code %d", ret)
609
713
def run_bzr_catch_errors(argv):
611
715
return run_bzr(argv)
612
# do this here inside the exception wrappers to catch EPIPE
614
716
except (KeyboardInterrupt, Exception), e:
615
717
# used to handle AssertionError and KeyboardInterrupt
616
718
# specially here, but hopefully they're handled ok by the logger now
617
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
719
trace.report_exception(sys.exc_info(), sys.stderr)
618
720
if os.environ.get('BZR_PDB'):
619
721
print '**** entering debugger'
621
723
pdb.post_mortem(sys.exc_traceback)
727
class HelpCommandIndex(object):
728
"""A index for bzr help that returns commands."""
731
self.prefix = 'commands/'
733
def get_topics(self, topic):
734
"""Search for topic amongst commands.
736
:param topic: A topic to search for.
737
:return: A list which is either empty or contains a single
740
if topic and topic.startswith(self.prefix):
741
topic = topic[len(self.prefix):]
743
cmd = _get_cmd_object(topic)
624
750
if __name__ == '__main__':
625
751
sys.exit(main(sys.argv))