52
from bzrlib import registry
50
from bzrlib.symbol_versioning import (
54
from bzrlib.hooks import HookPoint, Hooks
55
57
from bzrlib.option import Option
58
class CommandInfo(object):
59
"""Information about a command."""
61
def __init__(self, aliases):
62
"""The list of aliases for the command."""
63
self.aliases = aliases
66
def from_command(klass, command):
67
"""Factory to construct a CommandInfo from a command."""
68
return klass(command.aliases)
71
class CommandRegistry(registry.Registry):
74
def _get_name(command_name):
75
if command_name.startswith("cmd_"):
76
return _unsquish_command_name(command_name)
80
def register(self, cmd, decorate=False):
81
"""Utility function to help register a command
83
:param cmd: Command subclass to register
84
:param decorate: If true, allow overriding an existing command
85
of the same name; the old command is returned by this function.
86
Otherwise it is an error to try to override an existing command.
89
k_unsquished = self._get_name(k)
91
previous = self.get(k_unsquished)
93
previous = _builtin_commands().get(k_unsquished)
94
info = CommandInfo.from_command(cmd)
96
registry.Registry.register(self, k_unsquished, cmd,
97
override_existing=decorate, info=info)
99
trace.log_error('Two plugins defined the same command: %r' % k)
100
trace.log_error('Not loading the one in %r' %
101
sys.modules[cmd.__module__])
102
trace.log_error('Previously this command was registered from %r' %
103
sys.modules[previous.__module__])
106
def register_lazy(self, command_name, aliases, module_name):
107
"""Register a command without loading its module.
109
:param command_name: The primary name of the command.
110
:param aliases: A list of aliases for the command.
111
:module_name: The module that the command lives in.
113
key = self._get_name(command_name)
114
registry.Registry.register_lazy(self, key, module_name, command_name,
115
info=CommandInfo(aliases))
118
plugin_cmds = CommandRegistry()
121
63
def register_command(cmd, decorate=False):
64
"""Utility function to help register a command
66
:param cmd: Command subclass to register
67
:param decorate: If true, allow overriding an existing command
68
of the same name; the old command is returned by this function.
69
Otherwise it is an error to try to override an existing command.
122
71
global plugin_cmds
123
return plugin_cmds.register(cmd, decorate)
73
if k.startswith("cmd_"):
74
k_unsquished = _unsquish_command_name(k)
77
if k_unsquished not in plugin_cmds:
78
plugin_cmds[k_unsquished] = cmd
79
## trace.mutter('registered plugin command %s', k_unsquished)
80
if decorate and k_unsquished in builtin_command_names():
81
return _builtin_commands()[k_unsquished]
83
result = plugin_cmds[k_unsquished]
84
plugin_cmds[k_unsquished] = cmd
87
trace.log_error('Two plugins defined the same command: %r' % k)
88
trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
126
91
def _squish_command_name(cmd):
292
227
encoding_type = 'strict'
296
231
def __init__(self):
297
232
"""Construct an instance of this command."""
298
233
if self.__doc__ == Command.__doc__:
299
234
warn("No help message set for %r" % self)
300
# List of standard options directly supported
301
self.supported_std_options = []
303
def _maybe_expand_globs(self, file_list):
304
"""Glob expand file_list if the platform does not do that itself.
306
:return: A possibly empty list of unicode paths.
308
Introduced in bzrlib 0.18.
312
if sys.platform == 'win32':
313
file_list = win32utils.glob_expand(file_list)
314
return list(file_list)
317
"""Return single-line grammar for this command.
319
Only describes arguments, not options.
321
s = 'bzr ' + self.name() + ' '
322
for aname in self.takes_args:
323
aname = aname.upper()
324
if aname[-1] in ['$', '+']:
325
aname = aname[:-1] + '...'
326
elif aname[-1] == '?':
327
aname = '[' + aname[:-1] + ']'
328
elif aname[-1] == '*':
329
aname = '[' + aname[:-1] + '...]'
331
s = s[:-1] # remove last space
334
def get_help_text(self, additional_see_also=None, plain=True,
335
see_also_as_links=False, verbose=True):
336
"""Return a text string with help for this command.
338
:param additional_see_also: Additional help topics to be
340
:param plain: if False, raw help (reStructuredText) is
341
returned instead of plain text.
342
:param see_also_as_links: if True, convert items in 'See also'
343
list to internal links (used by bzr_man rstx generator)
344
:param verbose: if True, display the full help, otherwise
345
leave out the descriptive sections and just display
346
usage help (e.g. Purpose, Usage, Options) with a
347
message explaining how to obtain full help.
351
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
353
# Extract the summary (purpose) and sections out from the text
354
purpose,sections,order = self._get_help_parts(doc)
356
# If a custom usage section was provided, use it
357
if sections.has_key('Usage'):
358
usage = sections.pop('Usage')
360
usage = self._usage()
362
# The header is the purpose and usage
364
result += ':Purpose: %s\n' % purpose
365
if usage.find('\n') >= 0:
366
result += ':Usage:\n%s\n' % usage
368
result += ':Usage: %s\n' % usage
372
options = option.get_optparser(self.options()).format_option_help()
373
if options.startswith('Options:'):
374
result += ':' + options
375
elif options.startswith('options:'):
376
# Python 2.4 version of optparse
377
result += ':Options:' + options[len('options:'):]
383
# Add the description, indenting it 2 spaces
384
# to match the indentation of the options
385
if sections.has_key(None):
386
text = sections.pop(None)
387
text = '\n '.join(text.splitlines())
388
result += ':%s:\n %s\n\n' % ('Description',text)
390
# Add the custom sections (e.g. Examples). Note that there's no need
391
# to indent these as they must be indented already in the source.
394
if sections.has_key(label):
395
result += ':%s:\n%s\n' % (label,sections[label])
398
result += ("See bzr help %s for more details and examples.\n\n"
401
# Add the aliases, source (plug-in) and see also links, if any
403
result += ':Aliases: '
404
result += ', '.join(self.aliases) + '\n'
405
plugin_name = self.plugin_name()
406
if plugin_name is not None:
407
result += ':From: plugin "%s"\n' % plugin_name
408
see_also = self.get_see_also(additional_see_also)
410
if not plain and see_also_as_links:
412
for item in see_also:
414
# topics doesn't have an independent section
415
# so don't create a real link
416
see_also_links.append(item)
418
# Use a reST link for this entry
419
see_also_links.append("`%s`_" % (item,))
420
see_also = see_also_links
421
result += ':See also: '
422
result += ', '.join(see_also) + '\n'
424
# If this will be rendered as plain text, convert it
426
import bzrlib.help_topics
427
result = bzrlib.help_topics.help_as_plain_text(result)
431
def _get_help_parts(text):
432
"""Split help text into a summary and named sections.
434
:return: (summary,sections,order) where summary is the top line and
435
sections is a dictionary of the rest indexed by section name.
436
order is the order the section appear in the text.
437
A section starts with a heading line of the form ":xxx:".
438
Indented text on following lines is the section value.
439
All text found outside a named section is assigned to the
440
default section which is given the key of None.
442
def save_section(sections, order, label, section):
444
if sections.has_key(label):
445
sections[label] += '\n' + section
448
sections[label] = section
450
lines = text.rstrip().splitlines()
451
summary = lines.pop(0)
454
label,section = None,''
456
if line.startswith(':') and line.endswith(':') and len(line) > 2:
457
save_section(sections, order, label, section)
458
label,section = line[1:-1],''
459
elif (label is not None) and len(line) > 1 and not line[0].isspace():
460
save_section(sections, order, label, section)
461
label,section = None,line
464
section += '\n' + line
467
save_section(sections, order, label, section)
468
return summary, sections, order
470
def get_help_topic(self):
471
"""Return the commands help topic - its name."""
474
def get_see_also(self, additional_terms=None):
475
"""Return a list of help topics that are related to this command.
477
The list is derived from the content of the _see_also attribute. Any
478
duplicates are removed and the result is in lexical order.
479
:param additional_terms: Additional help topics to cross-reference.
480
:return: A list of help topics.
482
see_also = set(getattr(self, '_see_also', []))
484
see_also.update(additional_terms)
485
return sorted(see_also)
487
236
def options(self):
488
237
"""Return dict of valid options for this command.
490
239
Maps from long option name to option object."""
491
r = Option.STD_OPTIONS.copy()
241
r['help'] = option.Option.OPTIONS['help']
493
242
for o in self.takes_options:
494
243
if isinstance(o, basestring):
495
244
o = option.Option.OPTIONS[o]
497
if o.name in std_names:
498
self.supported_std_options.append(o.name)
501
248
def _setup_outf(self):
502
249
"""Return a file linked to stdout, which has proper encoding."""
250
assert self.encoding_type in ['strict', 'exact', 'replace']
503
252
# Originally I was using self.stdout, but that looks
504
253
# *way* too much like sys.stdout
505
254
if self.encoding_type == 'exact':
597
class CommandHooks(Hooks):
598
"""Hooks related to Command object creation/enumeration."""
601
"""Create the default hooks.
603
These are all empty initially, because by default nothing should get
607
self.create_hook(HookPoint('extend_command',
608
"Called after creating a command object to allow modifications "
609
"such as adding or removing options, docs etc. Called with the "
610
"new bzrlib.commands.Command object.", (1, 13), None))
612
Command.hooks = CommandHooks()
333
# Technically, this function hasn't been use in a *really* long time
334
# but we are only deprecating it now.
335
@deprecated_function(zero_eleven)
336
def parse_spec(spec):
342
>>> parse_spec("../@")
344
>>> parse_spec("../f/@35")
346
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
347
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
352
parsed = spec.split('/@')
353
assert len(parsed) == 2
358
parsed[1] = int(parsed[1])
360
pass # We can allow stuff like ./@revid:blahblahblah
364
parsed = [spec, None]
615
367
def parse_args(command, argv, alias_argv=None):
616
368
"""Parse command line.
618
370
Arguments and options are parsed at this level before being passed
619
371
down to specific command handlers. This routine knows, from a
620
372
lookup table, something about the available options, what optargs
718
453
os.remove(pfname)
721
def exception_to_return_code(the_callable, *args, **kwargs):
722
"""UI level helper for profiling and coverage.
724
This transforms exceptions into a return value of 3. As such its only
725
relevant to the UI layer, and should never be called where catching
726
exceptions may be desirable.
729
return the_callable(*args, **kwargs)
730
except (KeyboardInterrupt, Exception), e:
731
# used to handle AssertionError and KeyboardInterrupt
732
# specially here, but hopefully they're handled ok by the logger now
733
exc_info = sys.exc_info()
734
exitcode = trace.report_exception(exc_info, sys.stderr)
735
if os.environ.get('BZR_PDB'):
736
print '**** entering debugger'
739
if sys.version_info[:2] < (2, 6):
741
# pdb.post_mortem(tb)
742
# but because pdb.post_mortem gives bad results for tracebacks
743
# from inside generators, we do it manually.
744
# (http://bugs.python.org/issue4150, fixed in Python 2.6)
746
# Setup pdb on the traceback
749
p.setup(tb.tb_frame, tb)
750
# Point the debugger at the deepest frame of the stack
751
p.curindex = len(p.stack) - 1
752
p.curframe = p.stack[p.curindex][0]
753
# Start the pdb prompt.
754
p.print_stack_entry(p.stack[p.curindex])
762
456
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
763
457
from bzrlib.lsprof import profile
764
ret, stats = profile(exception_to_return_code, the_callable, *args, **kwargs)
459
ret, stats = profile(the_callable, *args, **kwargs)
766
461
if filename is None:
770
trace.note('Profile data written to "%s".', filename)
465
cPickle.dump(stats, open(filename, 'w'), 2)
466
print 'Profile data written to %r.' % filename
774
def shlex_split_unicode(unsplit):
776
return [u.decode('utf-8') for u in shlex.split(unsplit.encode('utf-8'))]
779
470
def get_alias(cmd, config=None):
780
471
"""Return an expanded alias, or None if no alias exists.
952
623
return ignore_pipe
956
"""Main entry point of command-line interface.
958
:param argv: list of unicode command-line arguments similar to sys.argv.
959
argv[0] is script name usually, it will be ignored.
960
Don't pass here sys.argv because this list contains plain strings
961
and not unicode; pass None instead.
963
:return: exit code of bzr command.
966
bzrlib.ui.ui_factory = bzrlib.ui.make_ui_for_terminal(
967
sys.stdin, sys.stdout, sys.stderr)
969
# Is this a final release version? If so, we should suppress warnings
970
if bzrlib.version_info[3] == 'final':
971
from bzrlib import symbol_versioning
972
symbol_versioning.suppress_deprecation_warnings(override=False)
974
argv = osutils.get_unicode_argv()
978
# ensure all arguments are unicode strings
980
if isinstance(a, unicode):
983
new_argv.append(a.decode('ascii'))
984
except UnicodeDecodeError:
985
raise errors.BzrError("argv should be list of unicode strings.")
628
from bzrlib.ui.text import TextUIFactory
629
bzrlib.ui.ui_factory = TextUIFactory()
630
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
987
631
ret = run_bzr_catch_errors(argv)
988
632
trace.mutter("return code %d", ret)
992
636
def run_bzr_catch_errors(argv):
993
"""Run a bzr command with parameters as described by argv.
995
This function assumed that that UI layer is setup, that symbol deprecations
996
are already applied, and that unicode decoding has already been performed on argv.
998
return exception_to_return_code(run_bzr, argv)
1001
def run_bzr_catch_user_errors(argv):
1002
"""Run bzr and report user errors, but let internal errors propagate.
1004
This is used for the test suite, and might be useful for other programs
1005
that want to wrap the commandline interface.
1008
638
return run_bzr(argv)
1009
except Exception, e:
1010
if (isinstance(e, (OSError, IOError))
1011
or not getattr(e, 'internal_error', True)):
1012
trace.report_exception(sys.exc_info(), sys.stderr)
1018
class HelpCommandIndex(object):
1019
"""A index for bzr help that returns commands."""
1022
self.prefix = 'commands/'
1024
def get_topics(self, topic):
1025
"""Search for topic amongst commands.
1027
:param topic: A topic to search for.
1028
:return: A list which is either empty or contains a single
1031
if topic and topic.startswith(self.prefix):
1032
topic = topic[len(self.prefix):]
1034
cmd = _get_cmd_object(topic)
1041
class Provider(object):
1042
'''Generic class to be overriden by plugins'''
1044
def plugin_for_command(self, cmd_name):
1045
'''Takes a command and returns the information for that plugin
1047
:return: A dictionary with all the available information
1048
for the requested plugin
1050
raise NotImplementedError
1053
class ProvidersRegistry(registry.Registry):
1054
'''This registry exists to allow other providers to exist'''
1057
for key, provider in self.iteritems():
1060
command_providers_registry = ProvidersRegistry()
639
# do this here inside the exception wrappers to catch EPIPE
641
except (KeyboardInterrupt, Exception), e:
642
# used to handle AssertionError and KeyboardInterrupt
643
# specially here, but hopefully they're handled ok by the logger now
644
trace.report_exception(sys.exc_info(), sys.stderr)
645
if os.environ.get('BZR_PDB'):
646
print '**** entering debugger'
648
pdb.post_mortem(sys.exc_traceback)
1063
651
if __name__ == '__main__':
1064
652
sys.exit(main(sys.argv))