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(), """
33
38
from warnings import warn
38
from bzrlib.errors import (BzrError,
51
from bzrlib import registry
43
53
from bzrlib.option import Option
44
from bzrlib.revisionspec import RevisionSpec
45
from bzrlib.symbol_versioning import *
47
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
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()
52
119
def register_command(cmd, decorate=False):
53
"""Utility function to help register a command
55
:param cmd: Command subclass to register
56
:param decorate: If true, allow overriding an existing command
57
of the same name; the old command is returned by this function.
58
Otherwise it is an error to try to override an existing command.
60
120
global plugin_cmds
62
if k.startswith("cmd_"):
63
k_unsquished = _unsquish_command_name(k)
66
if not plugin_cmds.has_key(k_unsquished):
67
plugin_cmds[k_unsquished] = cmd
68
mutter('registered plugin command %s', k_unsquished)
69
if decorate and k_unsquished in builtin_command_names():
70
return _builtin_commands()[k_unsquished]
72
result = plugin_cmds[k_unsquished]
73
plugin_cmds[k_unsquished] = cmd
76
log_error('Two plugins defined the same command: %r' % k)
77
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
121
return plugin_cmds.register(cmd, decorate)
80
124
def _squish_command_name(cmd):
212
291
"""Construct an instance of this command."""
213
292
if self.__doc__ == Command.__doc__:
214
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)
216
469
def options(self):
217
470
"""Return dict of valid options for this command.
219
472
Maps from long option name to option object."""
221
r['help'] = Option.OPTIONS['help']
473
r = Option.STD_OPTIONS.copy()
222
475
for o in self.takes_options:
223
if not isinstance(o, Option):
224
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)
228
483
def _setup_outf(self):
229
484
"""Return a file linked to stdout, which has proper encoding."""
230
assert self.encoding_type in ['strict', 'exact', 'replace']
232
485
# Originally I was using self.stdout, but that looks
233
486
# *way* too much like sys.stdout
234
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)
235
494
self.outf = sys.stdout
238
output_encoding = getattr(sys.stdout, 'encoding', None)
239
if not output_encoding:
240
input_encoding = getattr(sys.stdin, 'encoding', None)
241
if not input_encoding:
242
output_encoding = bzrlib.user_encoding
243
mutter('encoding stdout as bzrlib.user_encoding %r', output_encoding)
245
output_encoding = input_encoding
246
mutter('encoding stdout as sys.stdin encoding %r', output_encoding)
248
mutter('encoding stdout as sys.stdout encoding %r', output_encoding)
497
output_encoding = osutils.get_terminal_encoding()
250
# use 'replace' so that we don't abort if trying to write out
251
# in e.g. the default C locale.
252
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
499
self.outf = codecs.getwriter(output_encoding)(sys.stdout,
500
errors=self.encoding_type)
253
501
# For whatever reason codecs.getwriter() does not advertise its encoding
254
502
# it just returns the encoding of the wrapped file, which is completely
255
503
# bogus. So set the attribute, so we can find the correct encoding later.
256
504
self.outf.encoding = output_encoding
258
@deprecated_method(zero_eight)
259
def run_argv(self, argv):
260
"""Parse command line and run.
262
See run_argv_aliases for the 0.8 and beyond api.
264
return self.run_argv_aliases(argv)
266
506
def run_argv_aliases(self, argv, alias_argv=None):
267
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)
268
512
args, opts = parse_args(self, argv, alias_argv)
514
# Process the standard options
269
515
if 'help' in opts: # e.g. bzr add --help
270
from bzrlib.help import help_on_command
271
help_on_command(self.name())
516
sys.stdout.write(self.get_help_text())
273
# XXX: This should be handled by the parser
274
allowed_names = self.options().keys()
276
if oname not in allowed_names:
277
raise BzrCommandError("option '--%s' is not allowed for"
278
" command %r" % (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'):
279
528
# mix arguments and options into one dictionary
280
529
cmdargs = _match_argform(self.name(), self.takes_args, args)
352
581
lookup table, something about the available options, what optargs
353
582
they take, and which commands will accept them.
355
# TODO: chop up this beast; make it a method of the Command
360
cmd_options = command.options()
362
proc_aliasarg = True # Are we processing alias_argv now?
363
for proc_argv in alias_argv, argv:
370
# We've received a standalone -- No more flags
374
# option names must not be unicode
378
mutter(" got option %r", a)
380
optname, optarg = a[2:].split('=', 1)
383
if optname not in cmd_options:
384
raise BzrOptionError('unknown long option %r for'
389
if shortopt in Option.SHORT_OPTIONS:
390
# Multi-character options must have a space to delimit
392
# ^^^ what does this mean? mbp 20051014
393
optname = Option.SHORT_OPTIONS[shortopt].name
395
# Single character short options, can be chained,
396
# and have their value appended to their name
398
if shortopt not in Option.SHORT_OPTIONS:
399
# We didn't find the multi-character name, and we
400
# didn't find the single char name
401
raise BzrError('unknown short option %r' % a)
402
optname = Option.SHORT_OPTIONS[shortopt].name
405
# There are extra things on this option
406
# see if it is the value, or if it is another
408
optargfn = Option.OPTIONS[optname].type
410
# This option does not take an argument, so the
411
# next entry is another short option, pack it
413
proc_argv.insert(0, '-' + a[2:])
415
# This option takes an argument, so pack it
419
if optname not in cmd_options:
420
raise BzrOptionError('unknown short option %r for'
422
(shortopt, command.name()))
424
# XXX: Do we ever want to support this, e.g. for -r?
426
raise BzrError('repeated option %r' % a)
427
elif optname in alias_opts:
428
# Replace what's in the alias with what's in the real
430
del alias_opts[optname]
432
proc_argv.insert(0, a)
435
raise BzrError('repeated option %r' % a)
437
option_obj = cmd_options[optname]
438
optargfn = option_obj.type
442
raise BzrError('option %r needs an argument' % a)
444
optarg = proc_argv.pop(0)
445
opts[optname] = optargfn(optarg)
447
alias_opts[optname] = optargfn(optarg)
450
raise BzrError('option %r takes no argument' % optname)
453
alias_opts[optname] = True
456
proc_aliasarg = False # Done with alias argv
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])
457
594
return args, opts
474
611
argdict[argname + '_list'] = None
475
612
elif ap[-1] == '+':
477
raise BzrCommandError("command %r needs one or more %s"
478
% (cmd, argname.upper()))
614
raise errors.BzrCommandError("command %r needs one or more %s"
615
% (cmd, argname.upper()))
480
617
argdict[argname + '_list'] = args[:]
482
619
elif ap[-1] == '$': # all but one
483
620
if len(args) < 2:
484
raise BzrCommandError("command %r needs one or more %s"
485
% (cmd, argname.upper()))
621
raise errors.BzrCommandError("command %r needs one or more %s"
622
% (cmd, argname.upper()))
486
623
argdict[argname + '_list'] = args[:-1]
489
626
# just a plain arg
492
raise BzrCommandError("command %r requires argument %s"
493
% (cmd, argname.upper()))
629
raise errors.BzrCommandError("command %r requires argument %s"
630
% (cmd, argname.upper()))
495
632
argdict[argname] = args.pop(0)
498
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,
505
656
def apply_profiled(the_callable, *args, **kwargs):
636
805
if not opt_no_aliases:
637
806
alias_argv = get_alias(argv[0])
639
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
808
user_encoding = osutils.get_user_encoding()
809
alias_argv = [a.decode(user_encoding) for a in alias_argv]
640
810
argv[0] = alias_argv.pop(0)
642
cmd = str(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.
644
817
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
645
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
646
run = cmd_obj.run_argv
649
run = cmd_obj.run_argv_aliases
650
run_argv = [argv, alias_argv]
818
run = cmd_obj.run_argv_aliases
819
run_argv = [argv, alias_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.')
654
830
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
655
831
elif opt_profile:
834
'--coverage ignored, because --profile is in use.')
656
835
ret = apply_profiled(run, *run_argv)
836
elif opt_coverage_dir:
837
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
658
839
ret = run(*run_argv)
840
if 'memory' in debug.debug_flags:
841
trace.debug_memory('Process status after command:', short=False)
661
# 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
664
849
def display_command(func):
665
850
"""Decorator that suppresses pipe/interrupt errors."""
684
from bzrlib.ui.text import TextUIFactory
685
## bzrlib.trace.enable_default_logging()
686
bzrlib.trace.log_startup(argv)
687
bzrlib.ui.ui_factory = TextUIFactory()
871
bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
872
sys.stdin, sys.stdout, sys.stderr)
689
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
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 "
690
884
ret = run_bzr_catch_errors(argv)
691
mutter("return code %d", ret)
885
trace.mutter("return code %d", ret)
695
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.
700
# do this here inside the exception wrappers to catch EPIPE
894
except (KeyboardInterrupt, Exception), e:
703
895
# used to handle AssertionError and KeyboardInterrupt
704
896
# specially here, but hopefully they're handled ok by the logger now
706
if (isinstance(e, IOError)
707
and hasattr(e, 'errno')
708
and e.errno == errno.EPIPE):
709
bzrlib.trace.note('broken pipe')
712
bzrlib.trace.log_exception()
713
if os.environ.get('BZR_PDB'):
714
print '**** entering debugger'
716
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()
719
967
if __name__ == '__main__':
720
968
sys.exit(main(sys.argv))