1
# Copyright (C) 2005-2011 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
# TODO: Define arguments by objects, rather than just using names.
20
# Those objects can specify the expected type of the argument, which
21
# would help with validation and shell completion. They could also provide
22
# help/explanation for that argument in a structured way.
24
# TODO: Specific "examples" property on commands for consistent formatting.
29
from .lazy_import import lazy_import
30
lazy_import(globals(), """
48
from .hooks import Hooks
49
from .i18n import gettext
50
# Compatibility - Option used to be in commands.
51
from .option import Option
52
from .plugin import disable_plugins, load_plugins, plugin_name
53
from . import errors, registry
59
class BzrOptionError(errors.BzrCommandError):
61
_fmt = "Error in command line options"
64
class CommandAvailableInPlugin(Exception):
66
internal_error = False
68
def __init__(self, cmd_name, plugin_metadata, provider):
70
self.plugin_metadata = plugin_metadata
71
self.cmd_name = cmd_name
72
self.provider = provider
76
_fmt = ('"%s" is not a standard brz command. \n'
77
'However, the following official plugin provides this command: %s\n'
78
'You can install it by going to: %s'
79
% (self.cmd_name, self.plugin_metadata['name'],
80
self.plugin_metadata['url']))
85
class CommandInfo(object):
86
"""Information about a command."""
88
def __init__(self, aliases):
89
"""The list of aliases for the command."""
90
self.aliases = aliases
93
def from_command(klass, command):
94
"""Factory to construct a CommandInfo from a command."""
95
return klass(command.aliases)
98
class CommandRegistry(registry.Registry):
99
"""Special registry mapping command names to command classes.
101
:ivar overridden_registry: Look in this registry for commands being
102
overridden by this registry. This can be used to tell plugin commands
103
about the builtin they're decorating.
107
registry.Registry.__init__(self)
108
self.overridden_registry = None
109
# map from aliases to the real command that implements the name
110
self._alias_dict = {}
112
def get(self, command_name):
113
real_name = self._alias_dict.get(command_name, command_name)
114
return registry.Registry.get(self, real_name)
117
def _get_name(command_name):
118
if command_name.startswith("cmd_"):
119
return _unsquish_command_name(command_name)
123
def register(self, cmd, decorate=False):
124
"""Utility function to help register a command
126
:param cmd: Command subclass to register
127
:param decorate: If true, allow overriding an existing command
128
of the same name; the old command is returned by this function.
129
Otherwise it is an error to try to override an existing command.
132
k_unsquished = self._get_name(k)
134
previous = self.get(k_unsquished)
137
if self.overridden_registry:
139
previous = self.overridden_registry.get(k_unsquished)
142
info = CommandInfo.from_command(cmd)
144
registry.Registry.register(self, k_unsquished, cmd,
145
override_existing=decorate, info=info)
147
trace.warning('Two plugins defined the same command: %r' % k)
148
trace.warning('Not loading the one in %r' %
149
sys.modules[cmd.__module__])
150
trace.warning('Previously this command was registered from %r' %
151
sys.modules[previous.__module__])
152
for a in cmd.aliases:
153
self._alias_dict[a] = k_unsquished
156
def register_lazy(self, command_name, aliases, module_name):
157
"""Register a command without loading its module.
159
:param command_name: The primary name of the command.
160
:param aliases: A list of aliases for the command.
161
:module_name: The module that the command lives in.
163
key = self._get_name(command_name)
164
registry.Registry.register_lazy(self, key, module_name, command_name,
165
info=CommandInfo(aliases))
167
self._alias_dict[a] = key
170
plugin_cmds = CommandRegistry()
171
builtin_command_registry = CommandRegistry()
172
plugin_cmds.overridden_registry = builtin_command_registry
175
def register_command(cmd, decorate=False):
176
"""Register a plugin command.
178
Should generally be avoided in favor of lazy registration.
181
return plugin_cmds.register(cmd, decorate)
184
def _squish_command_name(cmd):
185
return 'cmd_' + cmd.replace('-', '_')
188
def _unsquish_command_name(cmd):
189
return cmd[4:].replace('_','-')
192
def _register_builtin_commands():
193
if builtin_command_registry.keys():
196
import breezy.builtins
197
for cmd_class in _scan_module_for_commands(breezy.builtins):
198
builtin_command_registry.register(cmd_class)
199
breezy.builtins._register_lazy_builtins()
202
def _scan_module_for_commands(module):
203
module_dict = module.__dict__
204
for name in module_dict:
205
if name.startswith("cmd_"):
206
yield module_dict[name]
209
def _list_bzr_commands(names):
210
"""Find commands from bzr's core and plugins.
212
This is not the public interface, just the default hook called by all_command_names.
214
# to eliminate duplicates
215
names.update(builtin_command_names())
216
names.update(plugin_command_names())
220
def all_command_names():
221
"""Return a set of all command names."""
223
for hook in Command.hooks['list_commands']:
226
raise AssertionError(
227
'hook %s returned None' % Command.hooks.get_hook_name(hook))
231
def builtin_command_names():
232
"""Return list of builtin command names.
234
Use of all_command_names() is encouraged rather than builtin_command_names
235
and/or plugin_command_names.
237
_register_builtin_commands()
238
return builtin_command_registry.keys()
241
def plugin_command_names():
242
"""Returns command names from commands registered by plugins."""
243
return plugin_cmds.keys()
246
# Overrides for common mispellings that heuristics get wrong
248
'ic': {'ci': 0}, # heuristic finds nick
252
def guess_command(cmd_name):
253
"""Guess what command a user typoed.
255
:param cmd_name: Command to search for
256
:return: None if no command was found, name of a command otherwise
259
for name in all_command_names():
261
cmd = get_cmd_object(name)
262
names.update(cmd.aliases)
263
# candidate: modified levenshtein distance against cmd_name.
265
from . import patiencediff
266
for name in sorted(names):
267
matcher = patiencediff.PatienceSequenceMatcher(None, cmd_name, name)
269
opcodes = matcher.get_opcodes()
270
for opcode, l1, l2, r1, r2 in opcodes:
271
if opcode == 'delete':
273
elif opcode == 'replace':
274
distance += max(l2-l1, r2-l1)
275
elif opcode == 'insert':
277
elif opcode == 'equal':
278
# Score equal ranges lower, making similar commands of equal
279
# length closer than arbitrary same length commands.
280
distance -= 0.1 * (l2-l1)
281
costs[name] = distance
282
costs.update(_GUESS_OVERRIDES.get(cmd_name, {}))
283
costs = sorted((value, key) for key, value in costs.iteritems())
288
candidate = costs[0][1]
292
def get_cmd_object(cmd_name, plugins_override=True):
293
"""Return the command object for a command.
296
If true, plugin commands can override builtins.
299
return _get_cmd_object(cmd_name, plugins_override)
301
# No command found, see if this was a typo
302
candidate = guess_command(cmd_name)
303
if candidate is not None:
304
raise errors.BzrCommandError(
305
gettext('unknown command "%s". Perhaps you meant "%s"')
306
% (cmd_name, candidate))
307
raise errors.BzrCommandError(gettext('unknown command "%s"')
311
def _get_cmd_object(cmd_name, plugins_override=True, check_missing=True):
312
"""Get a command object.
314
:param cmd_name: The name of the command.
315
:param plugins_override: Allow plugins to override builtins.
316
:param check_missing: Look up commands not found in the regular index via
317
the get_missing_command hook.
318
:return: A Command object instance
319
:raises KeyError: If no command is found.
321
# We want only 'ascii' command names, but the user may have typed
322
# in a Unicode name. In that case, they should just get a
323
# 'command not found' error later.
324
# In the future, we may actually support Unicode command names.
327
for hook in Command.hooks['get_command']:
328
cmd = hook(cmd, cmd_name)
329
if cmd is not None and not plugins_override and not cmd.plugin_name():
330
# We've found a non-plugin command, don't permit it to be
333
if cmd is None and check_missing:
334
for hook in Command.hooks['get_missing_command']:
341
# Allow plugins to extend commands
342
for hook in Command.hooks['extend_command']:
344
if getattr(cmd, 'invoked_as', None) is None:
345
cmd.invoked_as = cmd_name
349
class NoPluginAvailable(errors.BzrError):
353
def _try_plugin_provider(cmd_name):
354
"""Probe for a plugin provider having cmd_name."""
356
plugin_metadata, provider = probe_for_provider(cmd_name)
357
raise CommandAvailableInPlugin(cmd_name, plugin_metadata, provider)
358
except NoPluginAvailable:
362
def probe_for_provider(cmd_name):
363
"""Look for a provider for cmd_name.
365
:param cmd_name: The command name.
366
:return: plugin_metadata, provider for getting cmd_name.
367
:raises NoPluginAvailable: When no provider can supply the plugin.
369
# look for providers that provide this command but aren't installed
370
for provider in command_providers_registry:
372
return provider.plugin_for_command(cmd_name), provider
373
except NoPluginAvailable:
375
raise NoPluginAvailable(cmd_name)
378
def _get_bzr_command(cmd_or_None, cmd_name):
379
"""Get a command from bzr's core."""
381
cmd_class = builtin_command_registry.get(cmd_name)
389
def _get_external_command(cmd_or_None, cmd_name):
390
"""Lookup a command that is a shell script."""
391
# Only do external command lookups when no command is found so far.
392
if cmd_or_None is not None:
394
from breezy.externalcommand import ExternalCommand
395
cmd_obj = ExternalCommand.find_command(cmd_name)
400
def _get_plugin_command(cmd_or_None, cmd_name):
401
"""Get a command from brz's plugins."""
403
return plugin_cmds.get(cmd_name)()
406
for key in plugin_cmds.keys():
407
info = plugin_cmds.get_info(key)
408
if cmd_name in info.aliases:
409
return plugin_cmds.get(key)()
413
class Command(object):
414
"""Base class for commands.
416
Commands are the heart of the command-line brz interface.
418
The command object mostly handles the mapping of command-line
419
parameters into one or more breezy operations, and of the results
422
Commands normally don't have any state. All their arguments are
423
passed in to the run method. (Subclasses may take a different
424
policy if the behaviour of the instance needs to depend on e.g. a
425
shell plugin and not just its Python class.)
427
The docstring for an actual command should give a single-line
428
summary, then a complete description of the command. A grammar
429
description will be inserted.
431
:cvar aliases: Other accepted names for this command.
433
:cvar takes_args: List of argument forms, marked with whether they are
434
optional, repeated, etc. Examples::
436
['to_location', 'from_branch?', 'file*']
438
* 'to_location' is required
439
* 'from_branch' is optional
440
* 'file' can be specified 0 or more times
442
:cvar takes_options: List of options that may be given for this command.
443
These can be either strings, referring to globally-defined options, or
444
option objects. Retrieve through options().
446
:cvar hidden: If true, this command isn't advertised. This is typically
447
for commands intended for expert users.
449
:cvar encoding_type: Command objects will get a 'outf' attribute, which has
450
been setup to properly handle encoding of unicode strings.
451
encoding_type determines what will happen when characters cannot be
454
* strict - abort if we cannot decode
455
* replace - put in a bogus character (typically '?')
456
* exact - do not encode sys.stdout
458
NOTE: by default on Windows, sys.stdout is opened as a text stream,
459
therefore LF line-endings are converted to CRLF. When a command uses
460
encoding_type = 'exact', then sys.stdout is forced to be a binary
461
stream, and line-endings will not mangled.
464
A string indicating the real name under which this command was
465
invoked, before expansion of aliases.
466
(This may be None if the command was constructed and run in-process.)
468
:cvar hooks: An instance of CommandHooks.
470
:cvar __doc__: The help shown by 'brz help command' for this command.
471
This is set by assigning explicitly to __doc__ so that -OO can
475
__doc__ = "My help goes here"
480
encoding_type = 'strict'
487
"""Construct an instance of this command."""
488
# List of standard options directly supported
489
self.supported_std_options = []
492
def add_cleanup(self, cleanup_func, *args, **kwargs):
493
"""Register a function to call after self.run returns or raises.
495
Functions will be called in LIFO order.
497
self._operation.add_cleanup(cleanup_func, *args, **kwargs)
499
def cleanup_now(self):
500
"""Execute and empty pending cleanup functions immediately.
502
After cleanup_now all registered cleanups are forgotten. add_cleanup
503
may be called again after cleanup_now; these cleanups will be called
504
after self.run returns or raises (or when cleanup_now is next called).
506
This is useful for releasing expensive or contentious resources (such
507
as write locks) before doing further work that does not require those
508
resources (such as writing results to self.outf). Note though, that
509
as it releases all resources, this may release locks that the command
510
wants to hold, so use should be done with care.
512
self._operation.cleanup_now()
515
"""Return single-line grammar for this command.
517
Only describes arguments, not options.
519
s = 'brz ' + self.name() + ' '
520
for aname in self.takes_args:
521
aname = aname.upper()
522
if aname[-1] in ['$', '+']:
523
aname = aname[:-1] + '...'
524
elif aname[-1] == '?':
525
aname = '[' + aname[:-1] + ']'
526
elif aname[-1] == '*':
527
aname = '[' + aname[:-1] + '...]'
529
s = s[:-1] # remove last space
532
def get_help_text(self, additional_see_also=None, plain=True,
533
see_also_as_links=False, verbose=True):
534
"""Return a text string with help for this command.
536
:param additional_see_also: Additional help topics to be
538
:param plain: if False, raw help (reStructuredText) is
539
returned instead of plain text.
540
:param see_also_as_links: if True, convert items in 'See also'
541
list to internal links (used by bzr_man rstx generator)
542
:param verbose: if True, display the full help, otherwise
543
leave out the descriptive sections and just display
544
usage help (e.g. Purpose, Usage, Options) with a
545
message explaining how to obtain full help.
548
i18n.install() # Install i18n only for get_help_text for now.
551
# Note: If self.gettext() translates ':Usage:\n', the section will
552
# be shown after "Description" section and we don't want to
553
# translate the usage string.
554
# Though, brz export-pot don't exports :Usage: section and it must
556
doc = self.gettext(doc)
558
doc = gettext("No help for this command.")
560
# Extract the summary (purpose) and sections out from the text
561
purpose,sections,order = self._get_help_parts(doc)
563
# If a custom usage section was provided, use it
564
if 'Usage' in sections:
565
usage = sections.pop('Usage')
567
usage = self._usage()
569
# The header is the purpose and usage
571
result += gettext(':Purpose: %s\n') % (purpose,)
572
if usage.find('\n') >= 0:
573
result += gettext(':Usage:\n%s\n') % (usage,)
575
result += gettext(':Usage: %s\n') % (usage,)
580
# XXX: optparse implicitly rewraps the help, and not always perfectly,
581
# so we get <https://bugs.launchpad.net/bzr/+bug/249908>. -- mbp
583
parser = option.get_optparser(self.options())
584
options = parser.format_option_help()
585
# FIXME: According to the spec, ReST option lists actually don't
586
# support options like --1.14 so that causes syntax errors (in Sphinx
587
# at least). As that pattern always appears in the commands that
588
# break, we trap on that and then format that block of 'format' options
589
# as a literal block. We use the most recent format still listed so we
590
# don't have to do that too often -- vila 20110514
591
if not plain and options.find(' --1.14 ') != -1:
592
options = options.replace(' format:\n', ' format::\n\n', 1)
593
if options.startswith('Options:'):
594
result += gettext(':Options:%s') % (options[len('options:'):],)
600
# Add the description, indenting it 2 spaces
601
# to match the indentation of the options
603
text = sections.pop(None)
604
text = '\n '.join(text.splitlines())
605
result += gettext(':Description:\n %s\n\n') % (text,)
607
# Add the custom sections (e.g. Examples). Note that there's no need
608
# to indent these as they must be indented already in the source.
611
if label in sections:
612
result += ':%s:\n%s\n' % (label, sections[label])
615
result += (gettext("See brz help %s for more details and examples.\n\n")
618
# Add the aliases, source (plug-in) and see also links, if any
620
result += gettext(':Aliases: ')
621
result += ', '.join(self.aliases) + '\n'
622
plugin_name = self.plugin_name()
623
if plugin_name is not None:
624
result += gettext(':From: plugin "%s"\n') % plugin_name
625
see_also = self.get_see_also(additional_see_also)
627
if not plain and see_also_as_links:
629
for item in see_also:
631
# topics doesn't have an independent section
632
# so don't create a real link
633
see_also_links.append(item)
635
# Use a Sphinx link for this entry
636
link_text = gettext(":doc:`{0} <{1}-help>`").format(
638
see_also_links.append(link_text)
639
see_also = see_also_links
640
result += gettext(':See also: %s') % ', '.join(see_also) + '\n'
642
# If this will be rendered as plain text, convert it
644
import breezy.help_topics
645
result = breezy.help_topics.help_as_plain_text(result)
649
def _get_help_parts(text):
650
"""Split help text into a summary and named sections.
652
:return: (summary,sections,order) where summary is the top line and
653
sections is a dictionary of the rest indexed by section name.
654
order is the order the section appear in the text.
655
A section starts with a heading line of the form ":xxx:".
656
Indented text on following lines is the section value.
657
All text found outside a named section is assigned to the
658
default section which is given the key of None.
660
def save_section(sections, order, label, section):
662
if label in sections:
663
sections[label] += '\n' + section
666
sections[label] = section
668
lines = text.rstrip().splitlines()
669
summary = lines.pop(0)
672
label,section = None,''
674
if line.startswith(':') and line.endswith(':') and len(line) > 2:
675
save_section(sections, order, label, section)
676
label,section = line[1:-1],''
677
elif (label is not None) and len(line) > 1 and not line[0].isspace():
678
save_section(sections, order, label, section)
679
label,section = None,line
682
section += '\n' + line
685
save_section(sections, order, label, section)
686
return summary, sections, order
688
def get_help_topic(self):
689
"""Return the commands help topic - its name."""
692
def get_see_also(self, additional_terms=None):
693
"""Return a list of help topics that are related to this command.
695
The list is derived from the content of the _see_also attribute. Any
696
duplicates are removed and the result is in lexical order.
697
:param additional_terms: Additional help topics to cross-reference.
698
:return: A list of help topics.
700
see_also = set(getattr(self, '_see_also', []))
702
see_also.update(additional_terms)
703
return sorted(see_also)
706
"""Return dict of valid options for this command.
708
Maps from long option name to option object."""
709
r = Option.STD_OPTIONS.copy()
711
for o in self.takes_options:
712
if isinstance(o, string_types):
713
o = option.Option.OPTIONS[o]
715
if o.name in std_names:
716
self.supported_std_options.append(o.name)
719
def _setup_outf(self):
720
"""Return a file linked to stdout, which has proper encoding."""
721
self.outf = ui.ui_factory.make_output_stream(
722
encoding_type=self.encoding_type)
724
def run_argv_aliases(self, argv, alias_argv=None):
725
"""Parse the command line and run with extra aliases in alias_argv."""
726
args, opts = parse_args(self, argv, alias_argv)
729
# Process the standard options
730
if 'help' in opts: # e.g. brz add --help
731
self.outf.write(self.get_help_text())
733
if 'usage' in opts: # e.g. brz add --usage
734
self.outf.write(self.get_help_text(verbose=False))
736
trace.set_verbosity_level(option._verbosity_level)
737
if 'verbose' in self.supported_std_options:
738
opts['verbose'] = trace.is_verbose()
739
elif 'verbose' in opts:
741
if 'quiet' in self.supported_std_options:
742
opts['quiet'] = trace.is_quiet()
743
elif 'quiet' in opts:
745
# mix arguments and options into one dictionary
746
cmdargs = _match_argform(self.name(), self.takes_args, args)
748
for k, v in opts.items():
749
cmdopts[k.replace('-', '_')] = v
751
all_cmd_args = cmdargs.copy()
752
all_cmd_args.update(cmdopts)
755
return self.run(**all_cmd_args)
757
# reset it, so that other commands run in the same process won't
758
# inherit state. Before we reset it, log any activity, so that it
759
# gets properly tracked.
760
ui.ui_factory.log_transport_activity(
761
display=('bytes' in debug.debug_flags))
762
trace.set_verbosity_level(0)
764
def _setup_run(self):
765
"""Wrap the defined run method on self with a cleanup.
767
This is called by __init__ to make the Command be able to be run
768
by just calling run(), as it could be before cleanups were added.
770
If a different form of cleanups are in use by your Command subclass,
771
you can override this method.
774
def run(*args, **kwargs):
775
for hook in Command.hooks['pre_command']:
777
self._operation = cleanup.OperationWithCleanups(class_run)
779
return self._operation.run_simple(*args, **kwargs)
782
for hook in Command.hooks['post_command']:
787
"""Actually run the command.
789
This is invoked with the options and arguments bound to
792
Return 0 or None if the command was successful, or a non-zero
793
shell error code if not. It's OK for this method to allow
794
an exception to raise up.
796
This method is automatically wrapped by Command.__init__ with a
797
cleanup operation, stored as self._operation. This can be used
798
via self.add_cleanup to perform automatic cleanups at the end of
801
The argument for run are assembled by introspection. So for instance,
802
if your command takes an argument files, you would declare::
804
def run(self, files=None):
807
raise NotImplementedError('no implementation of command %r'
811
"""Return help message for this class."""
812
from inspect import getdoc
813
if self.__doc__ is Command.__doc__:
817
def gettext(self, message):
818
"""Returns the gettext function used to translate this command's help.
820
Commands provided by plugins should override this to use their
823
return i18n.gettext_per_paragraph(message)
826
"""Return the canonical name for this command.
828
The name under which it was actually invoked is available in invoked_as.
830
return _unsquish_command_name(self.__class__.__name__)
832
def plugin_name(self):
833
"""Get the name of the plugin that provides this command.
835
:return: The name of the plugin or None if the command is builtin.
837
return plugin_name(self.__module__)
840
class CommandHooks(Hooks):
841
"""Hooks related to Command object creation/enumeration."""
844
"""Create the default hooks.
846
These are all empty initially, because by default nothing should get
849
Hooks.__init__(self, "breezy.commands", "Command.hooks")
850
self.add_hook('extend_command',
851
"Called after creating a command object to allow modifications "
852
"such as adding or removing options, docs etc. Called with the "
853
"new breezy.commands.Command object.", (1, 13))
854
self.add_hook('get_command',
855
"Called when creating a single command. Called with "
856
"(cmd_or_None, command_name). get_command should either return "
857
"the cmd_or_None parameter, or a replacement Command object that "
858
"should be used for the command. Note that the Command.hooks "
859
"hooks are core infrastructure. Many users will prefer to use "
860
"breezy.commands.register_command or plugin_cmds.register_lazy.",
862
self.add_hook('get_missing_command',
863
"Called when creating a single command if no command could be "
864
"found. Called with (command_name). get_missing_command should "
865
"either return None, or a Command object to be used for the "
867
self.add_hook('list_commands',
868
"Called when enumerating commands. Called with a set of "
869
"cmd_name strings for all the commands found so far. This set "
870
" is safe to mutate - e.g. to remove a command. "
871
"list_commands should return the updated set of command names.",
873
self.add_hook('pre_command',
874
"Called prior to executing a command. Called with the command "
876
self.add_hook('post_command',
877
"Called after executing a command. Called with the command "
880
Command.hooks = CommandHooks()
883
def parse_args(command, argv, alias_argv=None):
884
"""Parse command line.
886
Arguments and options are parsed at this level before being passed
887
down to specific command handlers. This routine knows, from a
888
lookup table, something about the available options, what optargs
889
they take, and which commands will accept them.
891
# TODO: make it a method of the Command?
892
parser = option.get_optparser(command.options())
893
if alias_argv is not None:
894
args = alias_argv + argv
898
# for python 2.5 and later, optparse raises this exception if a non-ascii
899
# option name is given. See http://bugs.python.org/issue2931
901
options, args = parser.parse_args(args)
902
except UnicodeEncodeError as e:
903
raise errors.BzrCommandError(
904
gettext('Only ASCII permitted in option names'))
906
opts = dict((k, v) for k, v in options.__dict__.items() if
907
v is not option.OptionParser.DEFAULT_VALUE)
911
def _match_argform(cmd, takes_args, args):
914
# step through args and takes_args, allowing appropriate 0-many matches
915
for ap in takes_args:
919
argdict[argname] = args.pop(0)
920
elif ap[-1] == '*': # all remaining arguments
922
argdict[argname + '_list'] = args[:]
925
argdict[argname + '_list'] = None
928
raise errors.BzrCommandError(gettext(
929
"command {0!r} needs one or more {1}").format(
930
cmd, argname.upper()))
932
argdict[argname + '_list'] = args[:]
934
elif ap[-1] == '$': # all but one
936
raise errors.BzrCommandError(
937
gettext("command {0!r} needs one or more {1}").format(
938
cmd, argname.upper()))
939
argdict[argname + '_list'] = args[:-1]
945
raise errors.BzrCommandError(
946
gettext("command {0!r} requires argument {1}").format(
947
cmd, argname.upper()))
949
argdict[argname] = args.pop(0)
952
raise errors.BzrCommandError( gettext(
953
"extra argument to command {0}: {1}").format(
958
def apply_coveraged(dirname, the_callable, *args, **kwargs):
959
# Cannot use "import trace", as that would import breezy.trace instead of
960
# the standard library's trace.
961
trace = __import__('trace')
963
tracer = trace.Trace(count=1, trace=0)
964
sys.settrace(tracer.globaltrace)
965
threading.settrace(tracer.globaltrace)
968
return exception_to_return_code(the_callable, *args, **kwargs)
971
results = tracer.results()
972
results.write_results(show_missing=1, summary=False,
976
def apply_profiled(the_callable, *args, **kwargs):
980
pffileno, pfname = tempfile.mkstemp()
982
prof = hotshot.Profile(pfname)
984
ret = prof.runcall(exception_to_return_code, the_callable, *args,
988
stats = hotshot.stats.load(pfname)
990
stats.sort_stats('cum') # 'time'
991
## XXX: Might like to write to stderr or the trace file instead but
992
## print_stats seems hardcoded to stdout
993
stats.print_stats(20)
1000
def exception_to_return_code(the_callable, *args, **kwargs):
1001
"""UI level helper for profiling and coverage.
1003
This transforms exceptions into a return value of 3. As such its only
1004
relevant to the UI layer, and should never be called where catching
1005
exceptions may be desirable.
1008
return the_callable(*args, **kwargs)
1009
except (KeyboardInterrupt, Exception) as e:
1010
# used to handle AssertionError and KeyboardInterrupt
1011
# specially here, but hopefully they're handled ok by the logger now
1012
exc_info = sys.exc_info()
1013
exitcode = trace.report_exception(exc_info, sys.stderr)
1014
if os.environ.get('BRZ_PDB'):
1015
print('**** entering debugger')
1017
pdb.post_mortem(exc_info[2])
1021
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
1022
from breezy.lsprof import profile
1023
ret, stats = profile(exception_to_return_code, the_callable,
1026
if filename is None:
1029
stats.save(filename)
1030
trace.note(gettext('Profile data written to "%s".'), filename)
1034
def get_alias(cmd, config=None):
1035
"""Return an expanded alias, or None if no alias exists.
1038
Command to be checked for an alias.
1040
Used to specify an alternative config to use,
1041
which is especially useful for testing.
1042
If it is unspecified, the global config will be used.
1045
import breezy.config
1046
config = breezy.config.GlobalConfig()
1047
alias = config.get_alias(cmd)
1049
return cmdline.split(alias)
1053
def run_bzr(argv, load_plugins=load_plugins, disable_plugins=disable_plugins):
1054
"""Execute a command.
1056
:param argv: The command-line arguments, without the program name from
1057
argv[0] These should already be decoded. All library/test code calling
1058
run_bzr should be passing valid strings (don't need decoding).
1059
:param load_plugins: What function to call when triggering plugin loading.
1060
This function should take no arguments and cause all plugins to be
1062
:param disable_plugins: What function to call when disabling plugin
1063
loading. This function should take no arguments and cause all plugin
1064
loading to be prohibited (so that code paths in your application that
1065
know about some plugins possibly being present will fail to import
1066
those plugins even if they are installed.)
1067
:return: Returns a command exit code or raises an exception.
1069
Special master options: these must come before the command because
1070
they control how the command is interpreted.
1073
Do not load plugin modules at all
1076
Do not allow aliases
1079
Only use builtin commands. (Plugins are still allowed to change
1083
Run under the Python hotshot profiler.
1086
Run under the Python lsprof profiler.
1089
Generate line coverage report in the specified directory.
1092
Specify the number of processes that can be run concurrently (selftest).
1094
trace.mutter("breezy version: " + breezy.__version__)
1095
argv = _specified_or_unicode_argv(argv)
1096
trace.mutter("brz arguments: %r", argv)
1098
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
1099
opt_no_l10n = opt_no_aliases = False
1100
opt_lsprof_file = opt_coverage_dir = None
1102
# --no-plugins is handled specially at a very early stage. We need
1103
# to load plugins before doing other command parsing so that they
1104
# can override commands, but this needs to happen first.
1108
override_config = []
1109
while i < len(argv):
1111
if a == '--profile':
1113
elif a == '--lsprof':
1115
elif a == '--lsprof-file':
1117
opt_lsprof_file = argv[i + 1]
1119
elif a == '--no-plugins':
1120
opt_no_plugins = True
1121
elif a == '--no-aliases':
1122
opt_no_aliases = True
1123
elif a == '--no-l10n':
1125
elif a == '--builtin':
1127
elif a == '--concurrency':
1128
os.environ['BRZ_CONCURRENCY'] = argv[i + 1]
1130
elif a == '--coverage':
1131
opt_coverage_dir = argv[i + 1]
1133
elif a == '--profile-imports':
1134
pass # already handled in startup script Bug #588277
1135
elif a.startswith('-D'):
1136
debug.debug_flags.add(a[2:])
1137
elif a.startswith('-O'):
1138
override_config.append(a[2:])
1143
if breezy.global_state is None:
1144
# FIXME: Workaround for users that imported breezy but didn't call
1145
# breezy.initialize -- vila 2012-01-19
1146
cmdline_overrides = config.CommandLineStore()
1148
cmdline_overrides = breezy.global_state.cmdline_overrides
1149
cmdline_overrides._from_cmdline(override_config)
1151
debug.set_debug_flags_from_config()
1153
if not opt_no_plugins:
1160
get_cmd_object('help').run_argv_aliases([])
1163
if argv[0] == '--version':
1164
get_cmd_object('version').run_argv_aliases([])
1169
if not opt_no_aliases:
1170
alias_argv = get_alias(argv[0])
1172
argv[0] = alias_argv.pop(0)
1175
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
1177
cmd_obj.l10n = False
1178
run = cmd_obj.run_argv_aliases
1179
run_argv = [argv, alias_argv]
1182
# We can be called recursively (tests for example), but we don't want
1183
# the verbosity level to propagate.
1184
saved_verbosity_level = option._verbosity_level
1185
option._verbosity_level = 0
1187
if opt_coverage_dir:
1189
'--coverage ignored, because --lsprof is in use.')
1190
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
1192
if opt_coverage_dir:
1194
'--coverage ignored, because --profile is in use.')
1195
ret = apply_profiled(run, *run_argv)
1196
elif opt_coverage_dir:
1197
ret = apply_coveraged(opt_coverage_dir, run, *run_argv)
1199
ret = run(*run_argv)
1202
# reset, in case we may do other commands later within the same
1203
# process. Commands that want to execute sub-commands must propagate
1204
# --verbose in their own way.
1205
if 'memory' in debug.debug_flags:
1206
trace.debug_memory('Process status after command:', short=False)
1207
option._verbosity_level = saved_verbosity_level
1208
# Reset the overrides
1209
cmdline_overrides._reset()
1212
def display_command(func):
1213
"""Decorator that suppresses pipe/interrupt errors."""
1214
def ignore_pipe(*args, **kwargs):
1216
result = func(*args, **kwargs)
1219
except IOError as e:
1220
if getattr(e, 'errno', None) is None:
1222
if e.errno != errno.EPIPE:
1223
# Win32 raises IOError with errno=0 on a broken pipe
1224
if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
1227
except KeyboardInterrupt:
1232
def install_bzr_command_hooks():
1233
"""Install the hooks to supply bzr's own commands."""
1234
if _list_bzr_commands in Command.hooks["list_commands"]:
1236
Command.hooks.install_named_hook("list_commands", _list_bzr_commands,
1238
Command.hooks.install_named_hook("get_command", _get_bzr_command,
1240
Command.hooks.install_named_hook("get_command", _get_plugin_command,
1241
"bzr plugin commands")
1242
Command.hooks.install_named_hook("get_command", _get_external_command,
1243
"bzr external command lookup")
1244
Command.hooks.install_named_hook("get_missing_command",
1245
_try_plugin_provider,
1246
"bzr plugin-provider-db check")
1250
def _specified_or_unicode_argv(argv):
1251
# For internal or testing use, argv can be passed. Otherwise, get it from
1252
# the process arguments in a unicode-safe way.
1254
return osutils.get_unicode_argv()
1258
# ensure all arguments are unicode strings
1260
if isinstance(a, unicode):
1263
new_argv.append(a.decode('ascii'))
1264
except UnicodeDecodeError:
1265
raise errors.BzrError("argv should be list of unicode strings.")
1269
def main(argv=None):
1270
"""Main entry point of command-line interface.
1272
Typically `breezy.initialize` should be called first.
1274
:param argv: list of unicode command-line arguments similar to sys.argv.
1275
argv[0] is script name usually, it will be ignored.
1276
Don't pass here sys.argv because this list contains plain strings
1277
and not unicode; pass None instead.
1279
:return: exit code of brz command.
1281
if argv is not None:
1283
_register_builtin_commands()
1284
ret = run_bzr_catch_errors(argv)
1285
trace.mutter("return code %d", ret)
1289
def run_bzr_catch_errors(argv):
1290
"""Run a bzr command with parameters as described by argv.
1292
This function assumed that that UI layer is setup, that symbol deprecations
1293
are already applied, and that unicode decoding has already been performed on argv.
1295
# done here so that they're covered for every test run
1296
install_bzr_command_hooks()
1297
return exception_to_return_code(run_bzr, argv)
1300
def run_bzr_catch_user_errors(argv):
1301
"""Run brz and report user errors, but let internal errors propagate.
1303
This is used for the test suite, and might be useful for other programs
1304
that want to wrap the commandline interface.
1306
# done here so that they're covered for every test run
1307
install_bzr_command_hooks()
1309
return run_bzr(argv)
1310
except Exception as e:
1311
if (isinstance(e, (OSError, IOError))
1312
or not getattr(e, 'internal_error', True)):
1313
trace.report_exception(sys.exc_info(), sys.stderr)
1319
class HelpCommandIndex(object):
1320
"""A index for bzr help that returns commands."""
1323
self.prefix = 'commands/'
1325
def get_topics(self, topic):
1326
"""Search for topic amongst commands.
1328
:param topic: A topic to search for.
1329
:return: A list which is either empty or contains a single
1332
if topic and topic.startswith(self.prefix):
1333
topic = topic[len(self.prefix):]
1335
cmd = _get_cmd_object(topic, check_missing=False)
1342
class Provider(object):
1343
"""Generic class to be overriden by plugins"""
1345
def plugin_for_command(self, cmd_name):
1346
"""Takes a command and returns the information for that plugin
1348
:return: A dictionary with all the available information
1349
for the requested plugin
1351
raise NotImplementedError
1354
class ProvidersRegistry(registry.Registry):
1355
"""This registry exists to allow other providers to exist"""
1358
for key, provider in self.items():
1361
command_providers_registry = ProvidersRegistry()