41
41
from bzrlib import (
49
from bzrlib.symbol_versioning import (
51
from bzrlib import registry
53
from bzrlib.hooks import HookPoint, Hooks
56
54
from bzrlib.option import Option
57
class CommandInfo(object):
58
"""Information about a command."""
60
def __init__(self, aliases):
61
"""The list of aliases for the command."""
62
self.aliases = aliases
65
def from_command(klass, command):
66
"""Factory to construct a CommandInfo from a command."""
67
return klass(command.aliases)
70
class CommandRegistry(registry.Registry):
73
def _get_name(command_name):
74
if command_name.startswith("cmd_"):
75
return _unsquish_command_name(command_name)
79
def register(self, cmd, decorate=False):
80
"""Utility function to help register a command
82
:param cmd: Command subclass to register
83
:param decorate: If true, allow overriding an existing command
84
of the same name; the old command is returned by this function.
85
Otherwise it is an error to try to override an existing command.
88
k_unsquished = self._get_name(k)
90
previous = self.get(k_unsquished)
92
previous = _builtin_commands().get(k_unsquished)
93
info = CommandInfo.from_command(cmd)
95
registry.Registry.register(self, k_unsquished, cmd,
96
override_existing=decorate, info=info)
98
trace.log_error('Two plugins defined the same command: %r' % k)
99
trace.log_error('Not loading the one in %r' %
100
sys.modules[cmd.__module__])
101
trace.log_error('Previously this command was registered from %r' %
102
sys.modules[previous.__module__])
105
def register_lazy(self, command_name, aliases, module_name):
106
"""Register a command without loading its module.
108
:param command_name: The primary name of the command.
109
:param aliases: A list of aliases for the command.
110
:module_name: The module that the command lives in.
112
key = self._get_name(command_name)
113
registry.Registry.register_lazy(self, key, module_name, command_name,
114
info=CommandInfo(aliases))
117
plugin_cmds = CommandRegistry()
62
120
def register_command(cmd, decorate=False):
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.
70
121
global plugin_cmds
72
if k.startswith("cmd_"):
73
k_unsquished = _unsquish_command_name(k)
76
if k_unsquished not in plugin_cmds:
77
plugin_cmds[k_unsquished] = cmd
78
trace.mutter('registered plugin command %s', k_unsquished)
79
if decorate and k_unsquished in builtin_command_names():
80
return _builtin_commands()[k_unsquished]
82
result = plugin_cmds[k_unsquished]
83
plugin_cmds[k_unsquished] = cmd
86
trace.log_error('Two plugins defined the same command: %r' % k)
87
trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
122
return plugin_cmds.register(cmd, decorate)
90
125
def _squish_command_name(cmd):
220
291
encoding_type = 'strict'
224
295
def __init__(self):
225
296
"""Construct an instance of this command."""
226
297
if self.__doc__ == Command.__doc__:
227
298
warn("No help message set for %r" % self)
299
# List of standard options directly supported
300
self.supported_std_options = []
302
def _maybe_expand_globs(self, file_list):
303
"""Glob expand file_list if the platform does not do that itself.
305
:return: A possibly empty list of unicode paths.
307
Introduced in bzrlib 0.18.
311
if sys.platform == 'win32':
312
file_list = win32utils.glob_expand(file_list)
313
return list(file_list)
316
"""Return single-line grammar for this command.
318
Only describes arguments, not options.
320
s = 'bzr ' + self.name() + ' '
321
for aname in self.takes_args:
322
aname = aname.upper()
323
if aname[-1] in ['$', '+']:
324
aname = aname[:-1] + '...'
325
elif aname[-1] == '?':
326
aname = '[' + aname[:-1] + ']'
327
elif aname[-1] == '*':
328
aname = '[' + aname[:-1] + '...]'
330
s = s[:-1] # remove last space
333
def get_help_text(self, additional_see_also=None, plain=True,
334
see_also_as_links=False):
335
"""Return a text string with help for this command.
337
:param additional_see_also: Additional help topics to be
339
:param plain: if False, raw help (reStructuredText) is
340
returned instead of plain text.
341
:param see_also_as_links: if True, convert items in 'See also'
342
list to internal links (used by bzr_man rstx generator)
346
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
348
# Extract the summary (purpose) and sections out from the text
349
purpose,sections,order = self._get_help_parts(doc)
351
# If a custom usage section was provided, use it
352
if sections.has_key('Usage'):
353
usage = sections.pop('Usage')
355
usage = self._usage()
357
# The header is the purpose and usage
359
result += ':Purpose: %s\n' % purpose
360
if usage.find('\n') >= 0:
361
result += ':Usage:\n%s\n' % usage
363
result += ':Usage: %s\n' % usage
367
options = option.get_optparser(self.options()).format_option_help()
368
if options.startswith('Options:'):
369
result += ':' + options
370
elif options.startswith('options:'):
371
# Python 2.4 version of optparse
372
result += ':Options:' + options[len('options:'):]
377
# Add the description, indenting it 2 spaces
378
# to match the indentation of the options
379
if sections.has_key(None):
380
text = sections.pop(None)
381
text = '\n '.join(text.splitlines())
382
result += ':%s:\n %s\n\n' % ('Description',text)
384
# Add the custom sections (e.g. Examples). Note that there's no need
385
# to indent these as they must be indented already in the source.
388
if sections.has_key(label):
389
result += ':%s:\n%s\n\n' % (label,sections[label])
391
# Add the aliases, source (plug-in) and see also links, if any
393
result += ':Aliases: '
394
result += ', '.join(self.aliases) + '\n'
395
plugin_name = self.plugin_name()
396
if plugin_name is not None:
397
result += ':From: plugin "%s"\n' % plugin_name
398
see_also = self.get_see_also(additional_see_also)
400
if not plain and see_also_as_links:
402
for item in see_also:
404
# topics doesn't have an independent section
405
# so don't create a real link
406
see_also_links.append(item)
408
# Use a reST link for this entry
409
see_also_links.append("`%s`_" % (item,))
410
see_also = see_also_links
411
result += ':See also: '
412
result += ', '.join(see_also) + '\n'
414
# If this will be rendered as plain text, convert it
416
import bzrlib.help_topics
417
result = bzrlib.help_topics.help_as_plain_text(result)
421
def _get_help_parts(text):
422
"""Split help text into a summary and named sections.
424
:return: (summary,sections,order) where summary is the top line and
425
sections is a dictionary of the rest indexed by section name.
426
order is the order the section appear in the text.
427
A section starts with a heading line of the form ":xxx:".
428
Indented text on following lines is the section value.
429
All text found outside a named section is assigned to the
430
default section which is given the key of None.
432
def save_section(sections, order, label, section):
434
if sections.has_key(label):
435
sections[label] += '\n' + section
438
sections[label] = section
440
lines = text.rstrip().splitlines()
441
summary = lines.pop(0)
444
label,section = None,''
446
if line.startswith(':') and line.endswith(':') and len(line) > 2:
447
save_section(sections, order, label, section)
448
label,section = line[1:-1],''
449
elif (label is not None) and len(line) > 1 and not line[0].isspace():
450
save_section(sections, order, label, section)
451
label,section = None,line
454
section += '\n' + line
457
save_section(sections, order, label, section)
458
return summary, sections, order
460
def get_help_topic(self):
461
"""Return the commands help topic - its name."""
464
def get_see_also(self, additional_terms=None):
465
"""Return a list of help topics that are related to this command.
467
The list is derived from the content of the _see_also attribute. Any
468
duplicates are removed and the result is in lexical order.
469
:param additional_terms: Additional help topics to cross-reference.
470
:return: A list of help topics.
472
see_also = set(getattr(self, '_see_also', []))
474
see_also.update(additional_terms)
475
return sorted(see_also)
229
477
def options(self):
230
478
"""Return dict of valid options for this command.
232
480
Maps from long option name to option object."""
234
r['help'] = option.Option.OPTIONS['help']
481
r = Option.STD_OPTIONS.copy()
235
483
for o in self.takes_options:
236
484
if isinstance(o, basestring):
237
485
o = option.Option.OPTIONS[o]
487
if o.name in std_names:
488
self.supported_std_options.append(o.name)
241
491
def _setup_outf(self):
242
492
"""Return a file linked to stdout, which has proper encoding."""
243
assert self.encoding_type in ['strict', 'exact', 'replace']
245
493
# Originally I was using self.stdout, but that looks
246
494
# *way* too much like sys.stdout
247
495
if self.encoding_type == 'exact':
496
# force sys.stdout to be binary stream on win32
497
if sys.platform == 'win32':
498
fileno = getattr(sys.stdout, 'fileno', None)
501
msvcrt.setmode(fileno(), os.O_BINARY)
248
502
self.outf = sys.stdout
251
505
output_encoding = osutils.get_terminal_encoding()
253
# use 'replace' so that we don't abort if trying to write out
254
# in e.g. the default C locale.
255
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
507
self.outf = codecs.getwriter(output_encoding)(sys.stdout,
508
errors=self.encoding_type)
256
509
# For whatever reason codecs.getwriter() does not advertise its encoding
257
510
# it just returns the encoding of the wrapped file, which is completely
258
511
# bogus. So set the attribute, so we can find the correct encoding later.
259
512
self.outf.encoding = output_encoding
261
@deprecated_method(zero_eight)
262
def run_argv(self, argv):
263
"""Parse command line and run.
265
See run_argv_aliases for the 0.8 and beyond api.
267
return self.run_argv_aliases(argv)
269
514
def run_argv_aliases(self, argv, alias_argv=None):
270
515
"""Parse the command line and run with extra aliases in alias_argv."""
272
warn("Passing None for [] is deprecated from bzrlib 0.10",
517
warn("Passing None for [] is deprecated from bzrlib 0.10",
273
518
DeprecationWarning, stacklevel=2)
275
520
args, opts = parse_args(self, argv, alias_argv)
522
# Process the standard options
276
523
if 'help' in opts: # e.g. bzr add --help
277
from bzrlib.help import help_on_command
278
help_on_command(self.name())
524
sys.stdout.write(self.get_help_text())
526
trace.set_verbosity_level(option._verbosity_level)
527
if 'verbose' in self.supported_std_options:
528
opts['verbose'] = trace.is_verbose()
529
elif opts.has_key('verbose'):
531
if 'quiet' in self.supported_std_options:
532
opts['quiet'] = trace.is_quiet()
533
elif opts.has_key('quiet'):
280
536
# mix arguments and options into one dictionary
281
537
cmdargs = _match_argform(self.name(), self.takes_args, args)
448
704
os.remove(pfname)
707
def exception_to_return_code(the_callable, *args, **kwargs):
708
"""UI level helper for profiling and coverage.
710
This transforms exceptions into a return value of 3. As such its only
711
relevant to the UI layer, and should never be called where catching
712
exceptions may be desirable.
715
return the_callable(*args, **kwargs)
716
except (KeyboardInterrupt, Exception), e:
717
# used to handle AssertionError and KeyboardInterrupt
718
# specially here, but hopefully they're handled ok by the logger now
719
exc_info = sys.exc_info()
720
exitcode = trace.report_exception(exc_info, sys.stderr)
721
if os.environ.get('BZR_PDB'):
722
print '**** entering debugger'
725
if sys.version_info[:2] < (2, 6):
727
# pdb.post_mortem(tb)
728
# but because pdb.post_mortem gives bad results for tracebacks
729
# from inside generators, we do it manually.
730
# (http://bugs.python.org/issue4150, fixed in Python 2.6)
732
# Setup pdb on the traceback
735
p.setup(tb.tb_frame, tb)
736
# Point the debugger at the deepest frame of the stack
737
p.curindex = len(p.stack) - 1
738
p.curframe = p.stack[p.curindex][0]
739
# Start the pdb prompt.
740
p.print_stack_entry(p.stack[p.curindex])
451
748
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
452
749
from bzrlib.lsprof import profile
454
ret, stats = profile(the_callable, *args, **kwargs)
750
ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
456
752
if filename is None:
460
cPickle.dump(stats, open(filename, 'w'), 2)
461
print 'Profile data written to %r.' % filename
756
trace.note('Profile data written to "%s".', filename)
466
"""Return an expanded alias, or None if no alias exists"""
468
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
760
def shlex_split_unicode(unsplit):
762
return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
765
def get_alias(cmd, config=None):
766
"""Return an expanded alias, or None if no alias exists.
769
Command to be checked for an alias.
771
Used to specify an alternative config to use,
772
which is especially useful for testing.
773
If it is unspecified, the global config will be used.
777
config = bzrlib.config.GlobalConfig()
778
alias = config.get_alias(cmd)
470
return alias.split(' ')
780
return shlex_split_unicode(alias)
474
784
def run_bzr(argv):
475
785
"""Execute a command.
477
This is similar to main(), but without all the trappings for
478
logging and error handling.
481
788
The command-line arguments, without the program name from argv[0]
482
789
These should already be decoded. All library/test code calling
483
790
run_bzr should be passing valid strings (don't need decoding).
485
792
Returns a command status or raises an exception.
487
794
Special master options: these must come before the command because
613
from bzrlib.ui.text import TextUIFactory
614
bzrlib.ui.ui_factory = TextUIFactory()
615
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
943
bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
944
sys.stdin, sys.stdout, sys.stderr)
946
# Is this a final release version? If so, we should suppress warnings
947
if bzrlib.version_info[3] == 'final':
948
from bzrlib import symbol_versioning
949
symbol_versioning.suppress_deprecation_warnings(override=False)
951
user_encoding = osutils.get_user_encoding()
952
argv = [a.decode(user_encoding) for a in argv[1:]]
953
except UnicodeDecodeError:
954
raise errors.BzrError(("Parameter '%r' is unsupported by the current "
616
956
ret = run_bzr_catch_errors(argv)
617
957
trace.mutter("return code %d", ret)
621
961
def run_bzr_catch_errors(argv):
962
"""Run a bzr command with parameters as described by argv.
964
This function assumed that that UI layer is setup, that symbol deprecations
965
are already applied, and that unicode decoding has already been performed on argv.
967
return exception_to_return_code(run_bzr, argv)
970
def run_bzr_catch_user_errors(argv):
971
"""Run bzr and report user errors, but let internal errors propagate.
973
This is used for the test suite, and might be useful for other programs
974
that want to wrap the commandline interface.
623
977
return run_bzr(argv)
624
# do this here inside the exception wrappers to catch EPIPE
626
978
except Exception, e:
627
# used to handle AssertionError and KeyboardInterrupt
628
# specially here, but hopefully they're handled ok by the logger now
629
trace.report_exception(sys.exc_info(), sys.stderr)
630
if os.environ.get('BZR_PDB'):
631
print '**** entering debugger'
633
pdb.post_mortem(sys.exc_traceback)
979
if (isinstance(e, (OSError, IOError))
980
or not getattr(e, 'internal_error', True)):
981
trace.report_exception(sys.exc_info(), sys.stderr)
987
class HelpCommandIndex(object):
988
"""A index for bzr help that returns commands."""
991
self.prefix = 'commands/'
993
def get_topics(self, topic):
994
"""Search for topic amongst commands.
996
:param topic: A topic to search for.
997
:return: A list which is either empty or contains a single
1000
if topic and topic.startswith(self.prefix):
1001
topic = topic[len(self.prefix):]
1003
cmd = _get_cmd_object(topic)
1010
class Provider(object):
1011
'''Generic class to be overriden by plugins'''
1013
def plugin_for_command(self, cmd_name):
1014
'''Takes a command and returns the information for that plugin
1016
:return: A dictionary with all the available information
1017
for the requested plugin
1019
raise NotImplementedError
1022
class ProvidersRegistry(registry.Registry):
1023
'''This registry exists to allow other providers to exist'''
1026
for key, provider in self.iteritems():
1029
command_providers_registry = ProvidersRegistry()
636
1032
if __name__ == '__main__':
637
1033
sys.exit(main(sys.argv))