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 import registry
46
53
from bzrlib.option import Option
56
class CommandInfo(object):
57
"""Information about a command."""
59
def __init__(self, aliases):
60
"""The list of aliases for the command."""
61
self.aliases = aliases
64
def from_command(klass, command):
65
"""Factory to construct a CommandInfo from a command."""
66
return klass(command.aliases)
69
class CommandRegistry(registry.Registry):
72
def _get_name(command_name):
73
if command_name.startswith("cmd_"):
74
return _unsquish_command_name(command_name)
78
def register(self, cmd, decorate=False):
79
"""Utility function to help register a command
81
:param cmd: Command subclass to register
82
:param decorate: If true, allow overriding an existing command
83
of the same name; the old command is returned by this function.
84
Otherwise it is an error to try to override an existing command.
87
k_unsquished = self._get_name(k)
89
previous = self.get(k_unsquished)
91
previous = _builtin_commands().get(k_unsquished)
92
info = CommandInfo.from_command(cmd)
94
registry.Registry.register(self, k_unsquished, cmd,
95
override_existing=decorate, info=info)
97
trace.log_error('Two plugins defined the same command: %r' % k)
98
trace.log_error('Not loading the one in %r' %
99
sys.modules[cmd.__module__])
100
trace.log_error('Previously this command was registered from %r' %
101
sys.modules[previous.__module__])
104
def register_lazy(self, command_name, aliases, module_name):
105
"""Register a command without loading its module.
107
:param command_name: The primary name of the command.
108
:param aliases: A list of aliases for the command.
109
:module_name: The module that the command lives in.
111
key = self._get_name(command_name)
112
registry.Registry.register_lazy(self, key, module_name, command_name,
113
info=CommandInfo(aliases))
116
plugin_cmds = CommandRegistry()
51
119
def register_command(cmd, decorate=False):
52
"Utility function to help register a command"
53
120
global plugin_cmds
55
if k.startswith("cmd_"):
56
k_unsquished = _unsquish_command_name(k)
59
if not plugin_cmds.has_key(k_unsquished):
60
plugin_cmds[k_unsquished] = cmd
61
mutter('registered plugin command %s', k_unsquished)
62
if decorate and k_unsquished in builtin_command_names():
63
return _builtin_commands()[k_unsquished]
65
result = plugin_cmds[k_unsquished]
66
plugin_cmds[k_unsquished] = cmd
69
log_error('Two plugins defined the same command: %r' % k)
70
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
121
return plugin_cmds.register(cmd, decorate)
73
124
def _squish_command_name(cmd):
195
291
"""Construct an instance of this command."""
196
292
if self.__doc__ == Command.__doc__:
197
293
warn("No help message set for %r" % self)
294
# List of standard options directly supported
295
self.supported_std_options = []
297
def _maybe_expand_globs(self, file_list):
298
"""Glob expand file_list if the platform does not do that itself.
300
:return: A possibly empty list of unicode paths.
302
Introduced in bzrlib 0.18.
306
if sys.platform == 'win32':
307
file_list = win32utils.glob_expand(file_list)
308
return list(file_list)
311
"""Return single-line grammar for this command.
313
Only describes arguments, not options.
315
s = 'bzr ' + self.name() + ' '
316
for aname in self.takes_args:
317
aname = aname.upper()
318
if aname[-1] in ['$', '+']:
319
aname = aname[:-1] + '...'
320
elif aname[-1] == '?':
321
aname = '[' + aname[:-1] + ']'
322
elif aname[-1] == '*':
323
aname = '[' + aname[:-1] + '...]'
325
s = s[:-1] # remove last space
328
def get_help_text(self, additional_see_also=None, plain=True,
329
see_also_as_links=False):
330
"""Return a text string with help for this command.
332
:param additional_see_also: Additional help topics to be
334
:param plain: if False, raw help (reStructuredText) is
335
returned instead of plain text.
336
:param see_also_as_links: if True, convert items in 'See also'
337
list to internal links (used by bzr_man rstx generator)
341
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
343
# Extract the summary (purpose) and sections out from the text
344
purpose,sections = self._get_help_parts(doc)
346
# If a custom usage section was provided, use it
347
if sections.has_key('Usage'):
348
usage = sections.pop('Usage')
350
usage = self._usage()
352
# The header is the purpose and usage
354
result += ':Purpose: %s\n' % purpose
355
if usage.find('\n') >= 0:
356
result += ':Usage:\n%s\n' % usage
358
result += ':Usage: %s\n' % usage
362
options = option.get_optparser(self.options()).format_option_help()
363
if options.startswith('Options:'):
364
result += ':' + options
365
elif options.startswith('options:'):
366
# Python 2.4 version of optparse
367
result += ':Options:' + options[len('options:'):]
372
# Add the description, indenting it 2 spaces
373
# to match the indentation of the options
374
if sections.has_key(None):
375
text = sections.pop(None)
376
text = '\n '.join(text.splitlines())
377
result += ':%s:\n %s\n\n' % ('Description',text)
379
# Add the custom sections (e.g. Examples). Note that there's no need
380
# to indent these as they must be indented already in the source.
382
labels = sorted(sections.keys())
384
result += ':%s:\n%s\n\n' % (label,sections[label])
386
# Add the aliases, source (plug-in) and see also links, if any
388
result += ':Aliases: '
389
result += ', '.join(self.aliases) + '\n'
390
plugin_name = self.plugin_name()
391
if plugin_name is not None:
392
result += ':From: plugin "%s"\n' % plugin_name
393
see_also = self.get_see_also(additional_see_also)
395
if not plain and see_also_as_links:
397
for item in see_also:
399
# topics doesn't have an independent section
400
# so don't create a real link
401
see_also_links.append(item)
403
# Use a reST link for this entry
404
see_also_links.append("`%s`_" % (item,))
405
see_also = see_also_links
406
result += ':See also: '
407
result += ', '.join(see_also) + '\n'
409
# If this will be rendered as plain text, convert it
411
import bzrlib.help_topics
412
result = bzrlib.help_topics.help_as_plain_text(result)
416
def _get_help_parts(text):
417
"""Split help text into a summary and named sections.
419
:return: (summary,sections) where summary is the top line and
420
sections is a dictionary of the rest indexed by section name.
421
A section starts with a heading line of the form ":xxx:".
422
Indented text on following lines is the section value.
423
All text found outside a named section is assigned to the
424
default section which is given the key of None.
426
def save_section(sections, label, section):
428
if sections.has_key(label):
429
sections[label] += '\n' + section
431
sections[label] = section
433
lines = text.rstrip().splitlines()
434
summary = lines.pop(0)
436
label,section = None,''
438
if line.startswith(':') and line.endswith(':') and len(line) > 2:
439
save_section(sections, label, section)
440
label,section = line[1:-1],''
441
elif (label is not None) and len(line) > 1 and not line[0].isspace():
442
save_section(sections, label, section)
443
label,section = None,line
446
section += '\n' + line
449
save_section(sections, label, section)
450
return summary, sections
452
def get_help_topic(self):
453
"""Return the commands help topic - its name."""
456
def get_see_also(self, additional_terms=None):
457
"""Return a list of help topics that are related to this command.
459
The list is derived from the content of the _see_also attribute. Any
460
duplicates are removed and the result is in lexical order.
461
:param additional_terms: Additional help topics to cross-reference.
462
:return: A list of help topics.
464
see_also = set(getattr(self, '_see_also', []))
466
see_also.update(additional_terms)
467
return sorted(see_also)
199
469
def options(self):
200
470
"""Return dict of valid options for this command.
202
472
Maps from long option name to option object."""
204
r['help'] = Option.OPTIONS['help']
473
r = Option.STD_OPTIONS.copy()
205
475
for o in self.takes_options:
206
if not isinstance(o, Option):
207
o = Option.OPTIONS[o]
476
if isinstance(o, basestring):
477
o = option.Option.OPTIONS[o]
479
if o.name in std_names:
480
self.supported_std_options.append(o.name)
211
def run_argv(self, argv):
212
"""Parse command line and run."""
213
args, opts = parse_args(self, argv)
483
def _setup_outf(self):
484
"""Return a file linked to stdout, which has proper encoding."""
485
# Originally I was using self.stdout, but that looks
486
# *way* too much like sys.stdout
487
if self.encoding_type == 'exact':
488
# force sys.stdout to be binary stream on win32
489
if sys.platform == 'win32':
490
fileno = getattr(sys.stdout, 'fileno', None)
493
msvcrt.setmode(fileno(), os.O_BINARY)
494
self.outf = sys.stdout
497
output_encoding = osutils.get_terminal_encoding()
499
self.outf = codecs.getwriter(output_encoding)(sys.stdout,
500
errors=self.encoding_type)
501
# For whatever reason codecs.getwriter() does not advertise its encoding
502
# it just returns the encoding of the wrapped file, which is completely
503
# bogus. So set the attribute, so we can find the correct encoding later.
504
self.outf.encoding = output_encoding
506
def run_argv_aliases(self, argv, alias_argv=None):
507
"""Parse the command line and run with extra aliases in alias_argv."""
509
warn("Passing None for [] is deprecated from bzrlib 0.10",
510
DeprecationWarning, stacklevel=2)
512
args, opts = parse_args(self, argv, alias_argv)
514
# Process the standard options
214
515
if 'help' in opts: # e.g. bzr add --help
215
from bzrlib.help import help_on_command
216
help_on_command(self.name())
516
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()))
518
trace.set_verbosity_level(option._verbosity_level)
519
if 'verbose' in self.supported_std_options:
520
opts['verbose'] = trace.is_verbose()
521
elif opts.has_key('verbose'):
523
if 'quiet' in self.supported_std_options:
524
opts['quiet'] = trace.is_quiet()
525
elif opts.has_key('quiet'):
224
528
# mix arguments and options into one dictionary
225
529
cmdargs = _match_argform(self.name(), self.takes_args, args)
294
581
lookup table, something about the available options, what optargs
295
582
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)
584
# TODO: make it a method of the Command?
585
parser = option.get_optparser(command.options())
586
if alias_argv is not None:
587
args = alias_argv + argv
591
options, args = parser.parse_args(args)
592
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
593
v is not option.OptionParser.DEFAULT_VALUE])
379
594
return args, opts
396
611
argdict[argname + '_list'] = None
397
612
elif ap[-1] == '+':
399
raise BzrCommandError("command %r needs one or more %s"
400
% (cmd, argname.upper()))
614
raise errors.BzrCommandError("command %r needs one or more %s"
615
% (cmd, argname.upper()))
402
617
argdict[argname + '_list'] = args[:]
404
619
elif ap[-1] == '$': # all but one
405
620
if len(args) < 2:
406
raise BzrCommandError("command %r needs one or more %s"
407
% (cmd, argname.upper()))
621
raise errors.BzrCommandError("command %r needs one or more %s"
622
% (cmd, argname.upper()))
408
623
argdict[argname + '_list'] = args[:-1]
411
626
# just a plain arg
414
raise BzrCommandError("command %r requires argument %s"
415
% (cmd, argname.upper()))
629
raise errors.BzrCommandError("command %r requires argument %s"
630
% (cmd, argname.upper()))
417
632
argdict[argname] = args.pop(0)
420
raise BzrCommandError("extra argument to command %s: %s"
635
raise errors.BzrCommandError("extra argument to command %s: %s"
640
def apply_coveraged(dirname, the_callable, *args, **kwargs):
641
# Cannot use "import trace", as that would import bzrlib.trace instead of
642
# the standard library's trace.
643
trace = __import__('trace')
645
tracer = trace.Trace(count=1, trace=0)
646
sys.settrace(tracer.globaltrace)
648
ret = the_callable(*args, **kwargs)
651
results = tracer.results()
652
results.write_results(show_missing=1, summary=False,
427
656
def apply_profiled(the_callable, *args, **kwargs):
465
729
Do not load plugin modules at all
468
735
Only use builtin commands. (Plugins are still allowed to change
469
736
other behaviour.)
472
Run under the Python profiler.
739
Run under the Python hotshot profiler.
742
Run under the Python lsprof profiler.
745
Generate line coverage report in the specified directory.
474
argv = [a.decode(bzrlib.user_encoding) for a in argv]
748
trace.mutter("bzr arguments: %r", argv)
476
opt_profile = opt_no_plugins = opt_builtin = False
750
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
751
opt_no_aliases = False
752
opt_lsprof_file = opt_coverage_dir = None
478
754
# --no-plugins is handled specially at a very early stage. We need
479
755
# to load plugins before doing other command parsing so that they
480
756
# can override commands, but this needs to happen first.
483
762
if a == '--profile':
484
763
opt_profile = True
764
elif a == '--lsprof':
766
elif a == '--lsprof-file':
768
opt_lsprof_file = argv[i + 1]
485
770
elif a == '--no-plugins':
486
771
opt_no_plugins = True
772
elif a == '--no-aliases':
773
opt_no_aliases = True
487
774
elif a == '--builtin':
488
775
opt_builtin = True
489
elif a in ('--quiet', '-q'):
776
elif a == '--coverage':
777
opt_coverage_dir = argv[i + 1]
779
elif a.startswith('-D'):
780
debug.debug_flags.add(a[2:])
495
if (not argv) or (argv[0] == '--help'):
496
from bzrlib.help import help
787
from bzrlib.builtins import cmd_help
788
cmd_help().run_argv_aliases([])
503
791
if argv[0] == '--version':
504
from bzrlib.builtins import show_version
792
from bzrlib.builtins import cmd_version
793
cmd_version().run_argv_aliases([])
508
796
if not opt_no_plugins:
509
797
from bzrlib.plugin import load_plugins
512
cmd = str(argv.pop(0))
800
from bzrlib.plugin import disable_plugins
805
if not opt_no_aliases:
806
alias_argv = get_alias(argv[0])
808
user_encoding = osutils.get_user_encoding()
809
alias_argv = [a.decode(user_encoding) for a in alias_argv]
810
argv[0] = alias_argv.pop(0)
813
# We want only 'ascii' command names, but the user may have typed
814
# in a Unicode name. In that case, they should just get a
815
# 'command not found' error later.
514
817
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
818
run = cmd_obj.run_argv_aliases
819
run_argv = [argv, alias_argv]
518
ret = apply_profiled(cmd_obj.run_argv, argv)
822
# We can be called recursively (tests for example), but we don't want
823
# the verbosity level to propagate.
824
saved_verbosity_level = option._verbosity_level
825
option._verbosity_level = 0
829
'--coverage ignored, because --lsprof is in use.')
830
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
834
'--coverage ignored, because --profile is in use.')
835
ret = apply_profiled(run, *run_argv)
836
elif opt_coverage_dir:
837
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
520
ret = cmd_obj.run_argv(argv)
840
if 'memory' in debug.debug_flags:
841
trace.debug_memory('Process status after command:', short=False)
523
# reset, in case we may do other commands later within the same process
844
# reset, in case we may do other commands later within the same
845
# process. Commands that want to execute sub-commands must propagate
846
# --verbose in their own way.
847
option._verbosity_level = saved_verbosity_level
526
849
def display_command(func):
527
850
"""Decorator that suppresses pipe/interrupt errors."""
546
871
from bzrlib.ui.text import TextUIFactory
547
## bzrlib.trace.enable_default_logging()
548
bzrlib.trace.log_startup(argv)
549
872
bzrlib.ui.ui_factory = TextUIFactory()
550
ret = run_bzr_catch_errors(argv[1:])
551
mutter("return code %d", ret)
874
# Is this a final release version? If so, we should suppress warnings
875
if bzrlib.version_info[3] == 'final':
876
from bzrlib import symbol_versioning
877
symbol_versioning.suppress_deprecation_warnings(override=False)
879
user_encoding = osutils.get_user_encoding()
880
argv = [a.decode(user_encoding) for a in argv[1:]]
881
except UnicodeDecodeError:
882
raise errors.BzrError(("Parameter '%r' is unsupported by the current "
884
ret = run_bzr_catch_errors(argv)
885
trace.mutter("return code %d", ret)
555
889
def run_bzr_catch_errors(argv):
890
# Note: The except clause logic below should be kept in sync with the
891
# profile() routine in lsprof.py.
560
# do this here inside the exception wrappers to catch EPIPE
894
except (KeyboardInterrupt, Exception), e:
563
895
# used to handle AssertionError and KeyboardInterrupt
564
896
# specially here, but hopefully they're handled ok by the logger now
566
if (isinstance(e, IOError)
567
and hasattr(e, 'errno')
568
and e.errno == errno.EPIPE):
569
bzrlib.trace.note('broken pipe')
572
bzrlib.trace.log_exception()
573
if os.environ.get('BZR_PDB'):
574
print '**** entering debugger'
576
pdb.post_mortem(sys.exc_traceback)
897
exitcode = trace.report_exception(sys.exc_info(), sys.stderr)
898
if os.environ.get('BZR_PDB'):
899
print '**** entering debugger'
901
pdb.post_mortem(sys.exc_traceback)
905
def run_bzr_catch_user_errors(argv):
906
"""Run bzr and report user errors, but let internal errors propagate.
908
This is used for the test suite, and might be useful for other programs
909
that want to wrap the commandline interface.
914
if (isinstance(e, (OSError, IOError))
915
or not getattr(e, 'internal_error', True)):
916
trace.report_exception(sys.exc_info(), sys.stderr)
922
class HelpCommandIndex(object):
923
"""A index for bzr help that returns commands."""
926
self.prefix = 'commands/'
928
def get_topics(self, topic):
929
"""Search for topic amongst commands.
931
:param topic: A topic to search for.
932
:return: A list which is either empty or contains a single
935
if topic and topic.startswith(self.prefix):
936
topic = topic[len(self.prefix):]
938
cmd = _get_cmd_object(topic)
945
class Provider(object):
946
'''Generic class to be overriden by plugins'''
948
def plugin_for_command(self, cmd_name):
949
'''Takes a command and returns the information for that plugin
951
:return: A dictionary with all the available information
952
for the requested plugin
954
raise NotImplementedError
957
class ProvidersRegistry(registry.Registry):
958
'''This registry exists to allow other providers to exist'''
961
for key, provider in self.iteritems():
964
command_providers_registry = ProvidersRegistry()
579
967
if __name__ == '__main__':
580
968
sys.exit(main(sys.argv))