18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
21
# TODO: Help messages for options.
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
# would help with validation and shell completion.
23
# would help with validation and shell completion. They could also provide
24
# help/explanation for that argument in a structured way.
26
# TODO: Specific "examples" property on commands for consistent formatting.
27
28
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
28
29
# the profile output behind so it can be interactively examined?
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
32
38
from warnings import warn
33
from inspect import getdoc
38
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
39
from bzrlib.errors import (BzrError,
44
from bzrlib.revisionspec import RevisionSpec
45
from bzrlib import BZRDIR
51
from bzrlib.symbol_versioning import (
46
56
from bzrlib.option import Option
51
62
def register_command(cmd, decorate=False):
52
"Utility function to help register a command"
63
"""Utility function to help register a command
65
:param cmd: Command subclass to register
66
:param decorate: If true, allow overriding an existing command
67
of the same name; the old command is returned by this function.
68
Otherwise it is an error to try to override an existing command.
55
72
if k.startswith("cmd_"):
56
73
k_unsquished = _unsquish_command_name(k)
59
if not plugin_cmds.has_key(k_unsquished):
76
if k_unsquished not in plugin_cmds:
60
77
plugin_cmds[k_unsquished] = cmd
61
mutter('registered plugin command %s', k_unsquished)
78
## trace.mutter('registered plugin command %s', k_unsquished)
62
79
if decorate and k_unsquished in builtin_command_names():
63
80
return _builtin_commands()[k_unsquished]
196
239
if self.__doc__ == Command.__doc__:
197
240
warn("No help message set for %r" % self)
242
def _maybe_expand_globs(self, file_list):
243
"""Glob expand file_list if the platform does not do that itself.
245
:return: A possibly empty list of unicode paths.
247
Introduced in bzrlib 0.18.
251
if sys.platform == 'win32':
252
file_list = win32utils.glob_expand(file_list)
253
return list(file_list)
256
"""Return single-line grammar for this command.
258
Only describes arguments, not options.
260
s = 'bzr ' + self.name() + ' '
261
for aname in self.takes_args:
262
aname = aname.upper()
263
if aname[-1] in ['$', '+']:
264
aname = aname[:-1] + '...'
265
elif aname[-1] == '?':
266
aname = '[' + aname[:-1] + ']'
267
elif aname[-1] == '*':
268
aname = '[' + aname[:-1] + '...]'
275
def get_help_text(self, additional_see_also=None, plain=True,
276
see_also_as_links=False):
277
"""Return a text string with help for this command.
279
:param additional_see_also: Additional help topics to be
281
:param plain: if False, raw help (reStructuredText) is
282
returned instead of plain text.
283
:param see_also_as_links: if True, convert items in 'See also'
284
list to internal links (used by bzr_man rstx generator)
288
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
290
# Extract the summary (purpose) and sections out from the text
291
purpose,sections = self._get_help_parts(doc)
293
# If a custom usage section was provided, use it
294
if sections.has_key('Usage'):
295
usage = sections.pop('Usage')
297
usage = self._usage()
299
# The header is the purpose and usage
301
result += ':Purpose: %s\n' % purpose
302
if usage.find('\n') >= 0:
303
result += ':Usage:\n%s\n' % usage
305
result += ':Usage: %s\n' % usage
309
options = option.get_optparser(self.options()).format_option_help()
310
if options.startswith('Options:'):
311
result += ':' + options
312
elif options.startswith('options:'):
313
# Python 2.4 version of optparse
314
result += ':Options:' + options[len('options:'):]
319
# Add the description, indenting it 2 spaces
320
# to match the indentation of the options
321
if sections.has_key(None):
322
text = sections.pop(None)
323
text = '\n '.join(text.splitlines())
324
result += ':%s:\n %s\n\n' % ('Description',text)
326
# Add the custom sections (e.g. Examples). Note that there's no need
327
# to indent these as they must be indented already in the source.
329
labels = sorted(sections.keys())
331
result += ':%s:\n%s\n\n' % (label,sections[label])
333
# Add the aliases, source (plug-in) and see also links, if any
335
result += ':Aliases: '
336
result += ', '.join(self.aliases) + '\n'
337
plugin_name = self.plugin_name()
338
if plugin_name is not None:
339
result += ':From: plugin "%s"\n' % plugin_name
340
see_also = self.get_see_also(additional_see_also)
342
if not plain and see_also_as_links:
344
for item in see_also:
346
# topics doesn't have an independent section
347
# so don't create a real link
348
see_also_links.append(item)
350
# Use a reST link for this entry
351
see_also_links.append("`%s`_" % (item,))
352
see_also = see_also_links
353
result += ':See also: '
354
result += ', '.join(see_also) + '\n'
356
# If this will be rendered as plan text, convert it
358
import bzrlib.help_topics
359
result = bzrlib.help_topics.help_as_plain_text(result)
363
def _get_help_parts(text):
364
"""Split help text into a summary and named sections.
366
:return: (summary,sections) where summary is the top line and
367
sections is a dictionary of the rest indexed by section name.
368
A section starts with a heading line of the form ":xxx:".
369
Indented text on following lines is the section value.
370
All text found outside a named section is assigned to the
371
default section which is given the key of None.
373
def save_section(sections, label, section):
375
if sections.has_key(label):
376
sections[label] += '\n' + section
378
sections[label] = section
380
lines = text.rstrip().splitlines()
381
summary = lines.pop(0)
383
label,section = None,''
385
if line.startswith(':') and line.endswith(':') and len(line) > 2:
386
save_section(sections, label, section)
387
label,section = line[1:-1],''
388
elif label != None and len(line) > 1 and not line[0].isspace():
389
save_section(sections, label, section)
390
label,section = None,line
393
section += '\n' + line
396
save_section(sections, label, section)
397
return summary, sections
399
def get_help_topic(self):
400
"""Return the commands help topic - its name."""
403
def get_see_also(self, additional_terms=None):
404
"""Return a list of help topics that are related to this command.
406
The list is derived from the content of the _see_also attribute. Any
407
duplicates are removed and the result is in lexical order.
408
:param additional_terms: Additional help topics to cross-reference.
409
:return: A list of help topics.
411
see_also = set(getattr(self, '_see_also', []))
413
see_also.update(additional_terms)
414
return sorted(see_also)
199
416
def options(self):
200
417
"""Return dict of valid options for this command.
202
419
Maps from long option name to option object."""
204
r['help'] = Option.OPTIONS['help']
421
r['help'] = option._help_option
205
422
for o in self.takes_options:
206
if not isinstance(o, Option):
207
o = Option.OPTIONS[o]
423
if isinstance(o, basestring):
424
o = option.Option.OPTIONS[o]
211
def run_argv(self, argv):
212
"""Parse command line and run."""
213
args, opts = parse_args(self, argv)
428
def _setup_outf(self):
429
"""Return a file linked to stdout, which has proper encoding."""
430
assert self.encoding_type in ['strict', 'exact', 'replace']
432
# Originally I was using self.stdout, but that looks
433
# *way* too much like sys.stdout
434
if self.encoding_type == 'exact':
435
# force sys.stdout to be binary stream on win32
436
if sys.platform == 'win32':
437
fileno = getattr(sys.stdout, 'fileno', None)
440
msvcrt.setmode(fileno(), os.O_BINARY)
441
self.outf = sys.stdout
444
output_encoding = osutils.get_terminal_encoding()
446
# use 'replace' so that we don't abort if trying to write out
447
# in e.g. the default C locale.
448
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
449
# For whatever reason codecs.getwriter() does not advertise its encoding
450
# it just returns the encoding of the wrapped file, which is completely
451
# bogus. So set the attribute, so we can find the correct encoding later.
452
self.outf.encoding = output_encoding
454
def run_argv_aliases(self, argv, alias_argv=None):
455
"""Parse the command line and run with extra aliases in alias_argv."""
457
warn("Passing None for [] is deprecated from bzrlib 0.10",
458
DeprecationWarning, stacklevel=2)
460
args, opts = parse_args(self, argv, alias_argv)
214
461
if 'help' in opts: # e.g. bzr add --help
215
from bzrlib.help import help_on_command
216
help_on_command(self.name())
462
sys.stdout.write(self.get_help_text())
218
# XXX: This should be handled by the parser
219
allowed_names = self.options().keys()
221
if oname not in allowed_names:
222
raise BzrCommandError("option '--%s' is not allowed for command %r"
223
% (oname, self.name()))
224
464
# mix arguments and options into one dictionary
225
465
cmdargs = _match_argform(self.name(), self.takes_args, args)
294
517
lookup table, something about the available options, what optargs
295
518
they take, and which commands will accept them.
297
# TODO: chop up this beast; make it a method of the Command
301
cmd_options = command.options()
309
# We've received a standalone -- No more flags
313
# option names must not be unicode
317
mutter(" got option %r", a)
319
optname, optarg = a[2:].split('=', 1)
322
if optname not in cmd_options:
323
raise BzrOptionError('unknown long option %r for command %s'
324
% (a, command.name()))
327
if shortopt in Option.SHORT_OPTIONS:
328
# Multi-character options must have a space to delimit
330
# ^^^ what does this mean? mbp 20051014
331
optname = Option.SHORT_OPTIONS[shortopt].name
333
# Single character short options, can be chained,
334
# and have their value appended to their name
336
if shortopt not in Option.SHORT_OPTIONS:
337
# We didn't find the multi-character name, and we
338
# didn't find the single char name
339
raise BzrError('unknown short option %r' % a)
340
optname = Option.SHORT_OPTIONS[shortopt].name
343
# There are extra things on this option
344
# see if it is the value, or if it is another
346
optargfn = Option.OPTIONS[optname].type
348
# This option does not take an argument, so the
349
# next entry is another short option, pack it back
351
argv.insert(0, '-' + a[2:])
353
# This option takes an argument, so pack it
357
if optname not in cmd_options:
358
raise BzrOptionError('unknown short option %r for command'
359
' %s' % (shortopt, command.name()))
361
# XXX: Do we ever want to support this, e.g. for -r?
362
raise BzrError('repeated option %r' % a)
364
option_obj = cmd_options[optname]
365
optargfn = option_obj.type
369
raise BzrError('option %r needs an argument' % a)
372
opts[optname] = optargfn(optarg)
375
raise BzrError('option %r takes no argument' % optname)
520
# TODO: make it a method of the Command?
521
parser = option.get_optparser(command.options())
522
if alias_argv is not None:
523
args = alias_argv + argv
527
options, args = parser.parse_args(args)
528
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
529
v is not option.OptionParser.DEFAULT_VALUE])
379
530
return args, opts
482
663
Run under the Python lsprof profiler.
484
argv = [a.decode(bzrlib.user_encoding) for a in argv]
666
trace.mutter("bzr arguments: %r", argv)
486
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = False
668
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
669
opt_no_aliases = False
670
opt_lsprof_file = None
488
672
# --no-plugins is handled specially at a very early stage. We need
489
673
# to load plugins before doing other command parsing so that they
490
674
# can override commands, but this needs to happen first.
493
680
if a == '--profile':
494
681
opt_profile = True
495
682
elif a == '--lsprof':
496
683
opt_lsprof = True
684
elif a == '--lsprof-file':
686
opt_lsprof_file = argv[i + 1]
497
688
elif a == '--no-plugins':
498
689
opt_no_plugins = True
690
elif a == '--no-aliases':
691
opt_no_aliases = True
499
692
elif a == '--builtin':
500
693
opt_builtin = True
501
694
elif a in ('--quiet', '-q'):
696
elif a.startswith('-D'):
697
debug.debug_flags.add(a[2:])
507
if (not argv) or (argv[0] == '--help'):
508
from bzrlib.help import help
704
from bzrlib.builtins import cmd_help
705
cmd_help().run_argv_aliases([])
515
708
if argv[0] == '--version':
516
from bzrlib.builtins import show_version
709
from bzrlib.version import show_version
520
713
if not opt_no_plugins:
521
714
from bzrlib.plugin import load_plugins
524
cmd = str(argv.pop(0))
717
from bzrlib.plugin import disable_plugins
722
if not opt_no_aliases:
723
alias_argv = get_alias(argv[0])
725
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
726
argv[0] = alias_argv.pop(0)
729
# We want only 'ascii' command names, but the user may have typed
730
# in a Unicode name. In that case, they should just get a
731
# 'command not found' error later.
526
733
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
734
run = cmd_obj.run_argv_aliases
735
run_argv = [argv, alias_argv]
530
ret = apply_lsprofiled(cmd_obj.run_argv, argv)
739
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
531
740
elif opt_profile:
532
ret = apply_profiled(cmd_obj.run_argv, argv)
741
ret = apply_profiled(run, *run_argv)
534
ret = cmd_obj.run_argv(argv)
537
746
# reset, in case we may do other commands later within the same process
747
trace.be_quiet(False)
540
749
def display_command(func):
541
750
"""Decorator that suppresses pipe/interrupt errors."""
560
771
from bzrlib.ui.text import TextUIFactory
561
## bzrlib.trace.enable_default_logging()
562
bzrlib.trace.log_startup(argv)
563
772
bzrlib.ui.ui_factory = TextUIFactory()
564
ret = run_bzr_catch_errors(argv[1:])
565
mutter("return code %d", ret)
773
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
774
ret = run_bzr_catch_errors(argv)
775
trace.mutter("return code %d", ret)
569
779
def run_bzr_catch_errors(argv):
574
# do this here inside the exception wrappers to catch EPIPE
782
except (KeyboardInterrupt, Exception), e:
577
783
# used to handle AssertionError and KeyboardInterrupt
578
784
# specially here, but hopefully they're handled ok by the logger now
580
if (isinstance(e, IOError)
581
and hasattr(e, 'errno')
582
and e.errno == errno.EPIPE):
583
bzrlib.trace.note('broken pipe')
785
trace.report_exception(sys.exc_info(), sys.stderr)
786
if os.environ.get('BZR_PDB'):
787
print '**** entering debugger'
789
pdb.post_mortem(sys.exc_traceback)
793
class HelpCommandIndex(object):
794
"""A index for bzr help that returns commands."""
797
self.prefix = 'commands/'
799
def get_topics(self, topic):
800
"""Search for topic amongst commands.
802
:param topic: A topic to search for.
803
:return: A list which is either empty or contains a single
806
if topic and topic.startswith(self.prefix):
807
topic = topic[len(self.prefix):]
809
cmd = _get_cmd_object(topic)
586
bzrlib.trace.log_exception()
587
if os.environ.get('BZR_PDB'):
588
print '**** entering debugger'
590
pdb.post_mortem(sys.exc_traceback)
593
816
if __name__ == '__main__':
594
817
sys.exit(main(sys.argv))