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.
21
23
# TODO: Define arguments by objects, rather than just using names.
22
24
# Those objects can specify the expected type of the argument, which
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.
25
# would help with validation and shell completion.
28
27
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
29
28
# the profile output behind so it can be interactively examined?
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
32
from warnings import warn
33
from inspect import getdoc
38
from warnings import warn
51
from bzrlib import registry
38
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
39
from bzrlib.errors import (BzrError,
44
from bzrlib.revisionspec import RevisionSpec
53
45
from bzrlib.option import Option
59
50
def register_command(cmd, decorate=False):
60
"""Utility function to help register a command
62
:param cmd: Command subclass to register
63
:param decorate: If true, allow overriding an existing command
64
of the same name; the old command is returned by this function.
65
Otherwise it is an error to try to override an existing command.
51
"Utility function to help register a command"
69
54
if k.startswith("cmd_"):
70
55
k_unsquished = _unsquish_command_name(k)
73
if k_unsquished not in plugin_cmds:
58
if not plugin_cmds.has_key(k_unsquished):
74
59
plugin_cmds[k_unsquished] = cmd
75
## trace.mutter('registered plugin command %s', k_unsquished)
60
mutter('registered plugin command %s', k_unsquished)
76
61
if decorate and k_unsquished in builtin_command_names():
77
62
return _builtin_commands()[k_unsquished]
247
194
"""Construct an instance of this command."""
248
195
if self.__doc__ == Command.__doc__:
249
196
warn("No help message set for %r" % self)
250
# List of standard options directly supported
251
self.supported_std_options = []
253
def _maybe_expand_globs(self, file_list):
254
"""Glob expand file_list if the platform does not do that itself.
256
:return: A possibly empty list of unicode paths.
258
Introduced in bzrlib 0.18.
262
if sys.platform == 'win32':
263
file_list = win32utils.glob_expand(file_list)
264
return list(file_list)
267
"""Return single-line grammar for this command.
269
Only describes arguments, not options.
271
s = 'bzr ' + self.name() + ' '
272
for aname in self.takes_args:
273
aname = aname.upper()
274
if aname[-1] in ['$', '+']:
275
aname = aname[:-1] + '...'
276
elif aname[-1] == '?':
277
aname = '[' + aname[:-1] + ']'
278
elif aname[-1] == '*':
279
aname = '[' + aname[:-1] + '...]'
281
s = s[:-1] # remove last space
284
def get_help_text(self, additional_see_also=None, plain=True,
285
see_also_as_links=False):
286
"""Return a text string with help for this command.
288
:param additional_see_also: Additional help topics to be
290
:param plain: if False, raw help (reStructuredText) is
291
returned instead of plain text.
292
:param see_also_as_links: if True, convert items in 'See also'
293
list to internal links (used by bzr_man rstx generator)
297
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
299
# Extract the summary (purpose) and sections out from the text
300
purpose,sections = self._get_help_parts(doc)
302
# If a custom usage section was provided, use it
303
if sections.has_key('Usage'):
304
usage = sections.pop('Usage')
306
usage = self._usage()
308
# The header is the purpose and usage
310
result += ':Purpose: %s\n' % purpose
311
if usage.find('\n') >= 0:
312
result += ':Usage:\n%s\n' % usage
314
result += ':Usage: %s\n' % usage
318
options = option.get_optparser(self.options()).format_option_help()
319
if options.startswith('Options:'):
320
result += ':' + options
321
elif options.startswith('options:'):
322
# Python 2.4 version of optparse
323
result += ':Options:' + options[len('options:'):]
328
# Add the description, indenting it 2 spaces
329
# to match the indentation of the options
330
if sections.has_key(None):
331
text = sections.pop(None)
332
text = '\n '.join(text.splitlines())
333
result += ':%s:\n %s\n\n' % ('Description',text)
335
# Add the custom sections (e.g. Examples). Note that there's no need
336
# to indent these as they must be indented already in the source.
338
labels = sorted(sections.keys())
340
result += ':%s:\n%s\n\n' % (label,sections[label])
342
# Add the aliases, source (plug-in) and see also links, if any
344
result += ':Aliases: '
345
result += ', '.join(self.aliases) + '\n'
346
plugin_name = self.plugin_name()
347
if plugin_name is not None:
348
result += ':From: plugin "%s"\n' % plugin_name
349
see_also = self.get_see_also(additional_see_also)
351
if not plain and see_also_as_links:
353
for item in see_also:
355
# topics doesn't have an independent section
356
# so don't create a real link
357
see_also_links.append(item)
359
# Use a reST link for this entry
360
see_also_links.append("`%s`_" % (item,))
361
see_also = see_also_links
362
result += ':See also: '
363
result += ', '.join(see_also) + '\n'
365
# If this will be rendered as plain text, convert it
367
import bzrlib.help_topics
368
result = bzrlib.help_topics.help_as_plain_text(result)
372
def _get_help_parts(text):
373
"""Split help text into a summary and named sections.
375
:return: (summary,sections) where summary is the top line and
376
sections is a dictionary of the rest indexed by section name.
377
A section starts with a heading line of the form ":xxx:".
378
Indented text on following lines is the section value.
379
All text found outside a named section is assigned to the
380
default section which is given the key of None.
382
def save_section(sections, label, section):
384
if sections.has_key(label):
385
sections[label] += '\n' + section
387
sections[label] = section
389
lines = text.rstrip().splitlines()
390
summary = lines.pop(0)
392
label,section = None,''
394
if line.startswith(':') and line.endswith(':') and len(line) > 2:
395
save_section(sections, label, section)
396
label,section = line[1:-1],''
397
elif (label is not None) and len(line) > 1 and not line[0].isspace():
398
save_section(sections, label, section)
399
label,section = None,line
402
section += '\n' + line
405
save_section(sections, label, section)
406
return summary, sections
408
def get_help_topic(self):
409
"""Return the commands help topic - its name."""
412
def get_see_also(self, additional_terms=None):
413
"""Return a list of help topics that are related to this command.
415
The list is derived from the content of the _see_also attribute. Any
416
duplicates are removed and the result is in lexical order.
417
:param additional_terms: Additional help topics to cross-reference.
418
:return: A list of help topics.
420
see_also = set(getattr(self, '_see_also', []))
422
see_also.update(additional_terms)
423
return sorted(see_also)
425
198
def options(self):
426
199
"""Return dict of valid options for this command.
428
201
Maps from long option name to option object."""
429
r = Option.STD_OPTIONS.copy()
203
r['help'] = Option.OPTIONS['help']
431
204
for o in self.takes_options:
432
if isinstance(o, basestring):
433
o = option.Option.OPTIONS[o]
205
if not isinstance(o, Option):
206
o = Option.OPTIONS[o]
435
if o.name in std_names:
436
self.supported_std_options.append(o.name)
439
def _setup_outf(self):
440
"""Return a file linked to stdout, which has proper encoding."""
441
# Originally I was using self.stdout, but that looks
442
# *way* too much like sys.stdout
443
if self.encoding_type == 'exact':
444
# force sys.stdout to be binary stream on win32
445
if sys.platform == 'win32':
446
fileno = getattr(sys.stdout, 'fileno', None)
449
msvcrt.setmode(fileno(), os.O_BINARY)
450
self.outf = sys.stdout
453
output_encoding = osutils.get_terminal_encoding()
455
self.outf = codecs.getwriter(output_encoding)(sys.stdout,
456
errors=self.encoding_type)
457
# For whatever reason codecs.getwriter() does not advertise its encoding
458
# it just returns the encoding of the wrapped file, which is completely
459
# bogus. So set the attribute, so we can find the correct encoding later.
460
self.outf.encoding = output_encoding
462
def run_argv_aliases(self, argv, alias_argv=None):
463
"""Parse the command line and run with extra aliases in alias_argv."""
465
warn("Passing None for [] is deprecated from bzrlib 0.10",
466
DeprecationWarning, stacklevel=2)
468
args, opts = parse_args(self, argv, alias_argv)
470
# Process the standard options
210
def run_argv(self, argv):
211
"""Parse command line and run."""
212
args, opts = parse_args(self, argv)
471
213
if 'help' in opts: # e.g. bzr add --help
472
sys.stdout.write(self.get_help_text())
214
from bzrlib.help import help_on_command
215
help_on_command(self.name())
474
trace.set_verbosity_level(option._verbosity_level)
475
if 'verbose' in self.supported_std_options:
476
opts['verbose'] = trace.is_verbose()
477
elif opts.has_key('verbose'):
479
if 'quiet' in self.supported_std_options:
480
opts['quiet'] = trace.is_quiet()
481
elif opts.has_key('quiet'):
217
# XXX: This should be handled by the parser
218
allowed_names = self.options().keys()
220
if oname not in allowed_names:
221
raise BzrCommandError("option '--%s' is not allowed for command %r"
222
% (oname, self.name()))
484
223
# mix arguments and options into one dictionary
485
224
cmdargs = _match_argform(self.name(), self.takes_args, args)
537
293
lookup table, something about the available options, what optargs
538
294
they take, and which commands will accept them.
540
# TODO: make it a method of the Command?
541
parser = option.get_optparser(command.options())
542
if alias_argv is not None:
543
args = alias_argv + argv
547
options, args = parser.parse_args(args)
548
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
549
v is not option.OptionParser.DEFAULT_VALUE])
296
# TODO: chop up this beast; make it a method of the Command
300
cmd_options = command.options()
308
# We've received a standalone -- No more flags
312
# option names must not be unicode
316
mutter(" got option %r", a)
318
optname, optarg = a[2:].split('=', 1)
321
if optname not in cmd_options:
322
raise BzrOptionError('unknown long option %r for command %s'
323
% (a, command.name()))
326
if shortopt in Option.SHORT_OPTIONS:
327
# Multi-character options must have a space to delimit
329
# ^^^ what does this mean? mbp 20051014
330
optname = Option.SHORT_OPTIONS[shortopt].name
332
# Single character short options, can be chained,
333
# and have their value appended to their name
335
if shortopt not in Option.SHORT_OPTIONS:
336
# We didn't find the multi-character name, and we
337
# didn't find the single char name
338
raise BzrError('unknown short option %r' % a)
339
optname = Option.SHORT_OPTIONS[shortopt].name
342
# There are extra things on this option
343
# see if it is the value, or if it is another
345
optargfn = Option.OPTIONS[optname].type
347
# This option does not take an argument, so the
348
# next entry is another short option, pack it back
350
argv.insert(0, '-' + a[2:])
352
# This option takes an argument, so pack it
356
if optname not in cmd_options:
357
raise BzrOptionError('unknown short option %r for command'
358
' %s' % (shortopt, command.name()))
360
# XXX: Do we ever want to support this, e.g. for -r?
361
raise BzrError('repeated option %r' % a)
363
option_obj = cmd_options[optname]
364
optargfn = option_obj.type
368
raise BzrError('option %r needs an argument' % a)
371
opts[optname] = optargfn(optarg)
374
raise BzrError('option %r takes no argument' % optname)
550
378
return args, opts
567
395
argdict[argname + '_list'] = None
568
396
elif ap[-1] == '+':
570
raise errors.BzrCommandError("command %r needs one or more %s"
571
% (cmd, argname.upper()))
398
raise BzrCommandError("command %r needs one or more %s"
399
% (cmd, argname.upper()))
573
401
argdict[argname + '_list'] = args[:]
575
403
elif ap[-1] == '$': # all but one
576
404
if len(args) < 2:
577
raise errors.BzrCommandError("command %r needs one or more %s"
578
% (cmd, argname.upper()))
405
raise BzrCommandError("command %r needs one or more %s"
406
% (cmd, argname.upper()))
579
407
argdict[argname + '_list'] = args[:-1]
582
410
# just a plain arg
585
raise errors.BzrCommandError("command %r requires argument %s"
586
% (cmd, argname.upper()))
413
raise BzrCommandError("command %r requires argument %s"
414
% (cmd, argname.upper()))
588
416
argdict[argname] = args.pop(0)
591
raise errors.BzrCommandError("extra argument to command %s: %s"
419
raise BzrCommandError("extra argument to command %s: %s"
596
def apply_coveraged(dirname, the_callable, *args, **kwargs):
597
# Cannot use "import trace", as that would import bzrlib.trace instead of
598
# the standard library's trace.
599
trace = __import__('trace')
601
tracer = trace.Trace(count=1, trace=0)
602
sys.settrace(tracer.globaltrace)
604
ret = the_callable(*args, **kwargs)
607
results = tracer.results()
608
results.write_results(show_missing=1, summary=False,
612
426
def apply_profiled(the_callable, *args, **kwargs):
756
537
from bzrlib.plugin import disable_plugins
757
538
disable_plugins()
761
if not opt_no_aliases:
762
alias_argv = get_alias(argv[0])
764
user_encoding = osutils.get_user_encoding()
765
alias_argv = [a.decode(user_encoding) for a in alias_argv]
766
argv[0] = alias_argv.pop(0)
769
# We want only 'ascii' command names, but the user may have typed
770
# in a Unicode name. In that case, they should just get a
771
# 'command not found' error later.
540
cmd = str(argv.pop(0))
773
542
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
774
run = cmd_obj.run_argv_aliases
775
run_argv = [argv, alias_argv]
778
# We can be called recursively (tests for example), but we don't want
779
# the verbosity level to propagate.
780
saved_verbosity_level = option._verbosity_level
781
option._verbosity_level = 0
785
'--coverage ignored, because --lsprof is in use.')
786
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
546
ret = apply_lsprofiled(opt_lsprof_file, cmd_obj.run_argv, argv)
787
547
elif opt_profile:
790
'--coverage ignored, because --profile is in use.')
791
ret = apply_profiled(run, *run_argv)
792
elif opt_coverage_dir:
793
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
548
ret = apply_profiled(cmd_obj.run_argv, argv)
796
if 'memory' in debug.debug_flags:
797
trace.debug_memory('Process status after command:', short=False)
550
ret = cmd_obj.run_argv(argv)
800
# reset, in case we may do other commands later within the same
801
# process. Commands that want to execute sub-commands must propagate
802
# --verbose in their own way.
803
option._verbosity_level = saved_verbosity_level
553
# reset, in case we may do other commands later within the same process
805
556
def display_command(func):
806
557
"""Decorator that suppresses pipe/interrupt errors."""
827
576
from bzrlib.ui.text import TextUIFactory
577
## bzrlib.trace.enable_default_logging()
578
bzrlib.trace.log_startup(argv)
828
579
bzrlib.ui.ui_factory = TextUIFactory()
830
# Is this a final release version? If so, we should suppress warnings
831
if bzrlib.version_info[3] == 'final':
832
from bzrlib import symbol_versioning
833
symbol_versioning.suppress_deprecation_warnings(override=False)
835
user_encoding = osutils.get_user_encoding()
836
argv = [a.decode(user_encoding) for a in argv[1:]]
837
except UnicodeDecodeError:
838
raise errors.BzrError(("Parameter '%r' is unsupported by the current "
840
ret = run_bzr_catch_errors(argv)
841
trace.mutter("return code %d", ret)
580
ret = run_bzr_catch_errors(argv[1:])
581
mutter("return code %d", ret)
845
585
def run_bzr_catch_errors(argv):
846
# Note: The except clause logic below should be kept in sync with the
847
# profile() routine in lsprof.py.
850
except (KeyboardInterrupt, Exception), e:
590
# do this here inside the exception wrappers to catch EPIPE
851
593
# used to handle AssertionError and KeyboardInterrupt
852
594
# specially here, but hopefully they're handled ok by the logger now
853
exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
854
if os.environ.get('BZR_PDB'):
855
print '**** entering debugger'
857
pdb.post_mortem(sys.exc_traceback)
861
def run_bzr_catch_user_errors(argv):
862
"""Run bzr and report user errors, but let internal errors propagate.
864
This is used for the test suite, and might be useful for other programs
865
that want to wrap the commandline interface.
870
if (isinstance(e, (OSError, IOError))
871
or not getattr(e, 'internal_error', True)):
872
trace.report_exception(sys.exc_info(), sys.stderr)
878
class HelpCommandIndex(object):
879
"""A index for bzr help that returns commands."""
882
self.prefix = 'commands/'
884
def get_topics(self, topic):
885
"""Search for topic amongst commands.
887
:param topic: A topic to search for.
888
:return: A list which is either empty or contains a single
891
if topic and topic.startswith(self.prefix):
892
topic = topic[len(self.prefix):]
894
cmd = _get_cmd_object(topic)
901
class Provider(object):
902
'''Generic class to be overriden by plugins'''
904
def plugin_for_command(self, cmd_name):
905
'''Takes a command and returns the information for that plugin
907
:return: A dictionary with all the available information
908
for the requested plugin
910
raise NotImplementedError
913
class ProvidersRegistry(registry.Registry):
914
'''This registry exists to allow other providers to exist'''
917
for key, provider in self.iteritems():
920
command_providers_registry = ProvidersRegistry()
596
if (isinstance(e, IOError)
597
and hasattr(e, 'errno')
598
and e.errno == errno.EPIPE):
599
bzrlib.trace.note('broken pipe')
602
bzrlib.trace.log_exception()
603
if os.environ.get('BZR_PDB'):
604
print '**** entering debugger'
606
pdb.post_mortem(sys.exc_traceback)
923
609
if __name__ == '__main__':
924
610
sys.exit(main(sys.argv))