1
# Copyright (C) 2006 by 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
# TODO: probably should say which arguments are candidates for glob
19
# expansion on windows and do that at the command level.
21
# TODO: Define arguments by objects, rather than just using names.
22
# Those objects can specify the expected type of the argument, which
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.
28
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
29
# the profile output behind so it can be interactively examined?
33
from warnings import warn
38
from bzrlib.errors import (BzrError,
43
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
52
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.
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__])
80
def _squish_command_name(cmd):
81
return 'cmd_' + cmd.replace('-', '_')
84
def _unsquish_command_name(cmd):
85
assert cmd.startswith("cmd_")
86
return cmd[4:].replace('_','-')
89
def _builtin_commands():
90
import bzrlib.builtins
92
builtins = bzrlib.builtins.__dict__
94
if name.startswith("cmd_"):
95
real_name = _unsquish_command_name(name)
96
r[real_name] = builtins[name]
100
def builtin_command_names():
101
"""Return list of builtin command names."""
102
return _builtin_commands().keys()
105
def plugin_command_names():
106
return plugin_cmds.keys()
109
def _get_cmd_dict(plugins_override=True):
110
"""Return name->class mapping for all commands."""
111
d = _builtin_commands()
113
d.update(plugin_cmds)
117
def get_all_cmds(plugins_override=True):
118
"""Return canonical name and class for all registered commands."""
119
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
123
def get_cmd_object(cmd_name, plugins_override=True):
124
"""Return the canonical name and command class for a command.
127
If true, plugin commands can override builtins.
129
from bzrlib.externalcommand import ExternalCommand
131
cmd_name = str(cmd_name) # not unicode
133
# first look up this command under the specified name
134
cmds = _get_cmd_dict(plugins_override=plugins_override)
136
return cmds[cmd_name]()
140
# look for any command which claims this as an alias
141
for real_cmd_name, cmd_class in cmds.iteritems():
142
if cmd_name in cmd_class.aliases:
145
cmd_obj = ExternalCommand.find_command(cmd_name)
149
raise BzrCommandError("unknown command %r" % cmd_name)
152
class Command(object):
153
"""Base class for commands.
155
Commands are the heart of the command-line bzr interface.
157
The command object mostly handles the mapping of command-line
158
parameters into one or more bzrlib operations, and of the results
161
Commands normally don't have any state. All their arguments are
162
passed in to the run method. (Subclasses may take a different
163
policy if the behaviour of the instance needs to depend on e.g. a
164
shell plugin and not just its Python class.)
166
The docstring for an actual command should give a single-line
167
summary, then a complete description of the command. A grammar
168
description will be inserted.
171
Other accepted names for this command.
174
List of argument forms, marked with whether they are optional,
179
['to_location', 'from_branch?', 'file*']
181
'to_location' is required
182
'from_branch' is optional
183
'file' can be specified 0 or more times
186
List of options that may be given for this command. These can
187
be either strings, referring to globally-defined options,
188
or option objects. Retrieve through options().
191
If true, this command isn't advertised. This is typically
192
for commands intended for expert users.
195
Command objects will get a 'outf' attribute, which has been
196
setup to properly handle encoding of unicode strings.
197
encoding_type determines what will happen when characters cannot
199
strict - abort if we cannot decode
200
replace - put in a bogus character (typically '?')
201
exact - do not encode sys.stdout
207
encoding_type = 'strict'
212
"""Construct an instance of this command."""
213
if self.__doc__ == Command.__doc__:
214
warn("No help message set for %r" % self)
217
"""Return dict of valid options for this command.
219
Maps from long option name to option object."""
221
r['help'] = Option.OPTIONS['help']
222
for o in self.takes_options:
223
if not isinstance(o, Option):
224
o = Option.OPTIONS[o]
228
def _setup_outf(self):
229
"""Return a file linked to stdout, which has proper encoding."""
230
assert self.encoding_type in ['strict', 'exact', 'replace']
232
# Originally I was using self.stdout, but that looks
233
# *way* too much like sys.stdout
234
if self.encoding_type == 'exact':
235
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)
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)
253
# For whatever reason codecs.getwriter() does not advertise its encoding
254
# it just returns the encoding of the wrapped file, which is completely
255
# bogus. So set the attribute, so we can find the correct encoding later.
256
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
def run_argv_aliases(self, argv, alias_argv=None):
267
"""Parse the command line and run with extra aliases in alias_argv."""
268
args, opts = parse_args(self, argv, alias_argv)
269
if 'help' in opts: # e.g. bzr add --help
270
from bzrlib.help import help_on_command
271
help_on_command(self.name())
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()))
279
# mix arguments and options into one dictionary
280
cmdargs = _match_argform(self.name(), self.takes_args, args)
282
for k, v in opts.items():
283
cmdopts[k.replace('-', '_')] = v
285
all_cmd_args = cmdargs.copy()
286
all_cmd_args.update(cmdopts)
290
return self.run(**all_cmd_args)
293
"""Actually run the command.
295
This is invoked with the options and arguments bound to
298
Return 0 or None if the command was successful, or a non-zero
299
shell error code if not. It's OK for this method to allow
300
an exception to raise up.
302
raise NotImplementedError('no implementation of command %r'
306
"""Return help message for this class."""
307
from inspect import getdoc
308
if self.__doc__ is Command.__doc__:
313
return _unsquish_command_name(self.__class__.__name__)
315
def plugin_name(self):
316
"""Get the name of the plugin that provides this command.
318
:return: The name of the plugin or None if the command is builtin.
320
mod_parts = self.__module__.split('.')
321
if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
327
def parse_spec(spec):
333
>>> parse_spec("../@")
335
>>> parse_spec("../f/@35")
337
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
338
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
343
parsed = spec.split('/@')
344
assert len(parsed) == 2
349
parsed[1] = int(parsed[1])
351
pass # We can allow stuff like ./@revid:blahblahblah
355
parsed = [spec, None]
358
def parse_args(command, argv, alias_argv=None):
359
"""Parse command line.
361
Arguments and options are parsed at this level before being passed
362
down to specific command handlers. This routine knows, from a
363
lookup table, something about the available options, what optargs
364
they take, and which commands will accept them.
366
# TODO: chop up this beast; make it a method of the Command
371
cmd_options = command.options()
373
proc_aliasarg = True # Are we processing alias_argv now?
374
for proc_argv in alias_argv, argv:
381
# We've received a standalone -- No more flags
385
# option names must not be unicode
389
mutter(" got option %r", a)
391
optname, optarg = a[2:].split('=', 1)
394
if optname not in cmd_options:
395
raise BzrOptionError('unknown long option %r for'
400
if shortopt in Option.SHORT_OPTIONS:
401
# Multi-character options must have a space to delimit
403
# ^^^ what does this mean? mbp 20051014
404
optname = Option.SHORT_OPTIONS[shortopt].name
406
# Single character short options, can be chained,
407
# and have their value appended to their name
409
if shortopt not in Option.SHORT_OPTIONS:
410
# We didn't find the multi-character name, and we
411
# didn't find the single char name
412
raise BzrError('unknown short option %r' % a)
413
optname = Option.SHORT_OPTIONS[shortopt].name
416
# There are extra things on this option
417
# see if it is the value, or if it is another
419
optargfn = Option.OPTIONS[optname].type
421
# This option does not take an argument, so the
422
# next entry is another short option, pack it
424
proc_argv.insert(0, '-' + a[2:])
426
# This option takes an argument, so pack it
430
if optname not in cmd_options:
431
raise BzrOptionError('unknown short option %r for'
433
(shortopt, command.name()))
435
# XXX: Do we ever want to support this, e.g. for -r?
437
raise BzrError('repeated option %r' % a)
438
elif optname in alias_opts:
439
# Replace what's in the alias with what's in the real
441
del alias_opts[optname]
443
proc_argv.insert(0, a)
446
raise BzrError('repeated option %r' % a)
448
option_obj = cmd_options[optname]
449
optargfn = option_obj.type
453
raise BzrError('option %r needs an argument' % a)
455
optarg = proc_argv.pop(0)
456
opts[optname] = optargfn(optarg)
458
alias_opts[optname] = optargfn(optarg)
461
raise BzrError('option %r takes no argument' % optname)
464
alias_opts[optname] = True
467
proc_aliasarg = False # Done with alias argv
471
def _match_argform(cmd, takes_args, args):
474
# step through args and takes_args, allowing appropriate 0-many matches
475
for ap in takes_args:
479
argdict[argname] = args.pop(0)
480
elif ap[-1] == '*': # all remaining arguments
482
argdict[argname + '_list'] = args[:]
485
argdict[argname + '_list'] = None
488
raise BzrCommandError("command %r needs one or more %s"
489
% (cmd, argname.upper()))
491
argdict[argname + '_list'] = args[:]
493
elif ap[-1] == '$': # all but one
495
raise BzrCommandError("command %r needs one or more %s"
496
% (cmd, argname.upper()))
497
argdict[argname + '_list'] = args[:-1]
503
raise BzrCommandError("command %r requires argument %s"
504
% (cmd, argname.upper()))
506
argdict[argname] = args.pop(0)
509
raise BzrCommandError("extra argument to command %s: %s"
516
def apply_profiled(the_callable, *args, **kwargs):
520
pffileno, pfname = tempfile.mkstemp()
522
prof = hotshot.Profile(pfname)
524
ret = prof.runcall(the_callable, *args, **kwargs) or 0
527
stats = hotshot.stats.load(pfname)
529
stats.sort_stats('cum') # 'time'
530
## XXX: Might like to write to stderr or the trace file instead but
531
## print_stats seems hardcoded to stdout
532
stats.print_stats(20)
539
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
540
from bzrlib.lsprof import profile
542
ret, stats = profile(the_callable, *args, **kwargs)
548
cPickle.dump(stats, open(filename, 'w'), 2)
549
print 'Profile data written to %r.' % filename
554
"""Return an expanded alias, or None if no alias exists"""
556
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
558
return alias.split(' ')
563
"""Execute a command.
565
This is similar to main(), but without all the trappings for
566
logging and error handling.
569
The command-line arguments, without the program name from argv[0]
570
These should already be decoded. All library/test code calling
571
run_bzr should be passing valid strings (don't need decoding).
573
Returns a command status or raises an exception.
575
Special master options: these must come before the command because
576
they control how the command is interpreted.
579
Do not load plugin modules at all
585
Only use builtin commands. (Plugins are still allowed to change
589
Run under the Python hotshot profiler.
592
Run under the Python lsprof profiler.
596
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
597
opt_no_aliases = False
598
opt_lsprof_file = None
600
# --no-plugins is handled specially at a very early stage. We need
601
# to load plugins before doing other command parsing so that they
602
# can override commands, but this needs to happen first.
610
elif a == '--lsprof':
612
elif a == '--lsprof-file':
613
opt_lsprof_file = argv[i + 1]
615
elif a == '--no-plugins':
616
opt_no_plugins = True
617
elif a == '--no-aliases':
618
opt_no_aliases = True
619
elif a == '--builtin':
621
elif a in ('--quiet', '-q'):
629
from bzrlib.builtins import cmd_help
630
cmd_help().run_argv_aliases([])
633
if argv[0] == '--version':
634
from bzrlib.builtins import show_version
638
if not opt_no_plugins:
639
from bzrlib.plugin import load_plugins
642
from bzrlib.plugin import disable_plugins
647
if not opt_no_aliases:
648
alias_argv = get_alias(argv[0])
650
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
651
argv[0] = alias_argv.pop(0)
653
cmd = str(argv.pop(0))
655
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
656
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
657
run = cmd_obj.run_argv
660
run = cmd_obj.run_argv_aliases
661
run_argv = [argv, alias_argv]
665
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
667
ret = apply_profiled(run, *run_argv)
672
# reset, in case we may do other commands later within the same process
675
def display_command(func):
676
"""Decorator that suppresses pipe/interrupt errors."""
677
def ignore_pipe(*args, **kwargs):
679
result = func(*args, **kwargs)
683
if not hasattr(e, 'errno'):
685
if e.errno != errno.EPIPE:
688
except KeyboardInterrupt:
695
from bzrlib.ui.text import TextUIFactory
696
## bzrlib.trace.enable_default_logging()
697
bzrlib.trace.log_startup(argv)
698
bzrlib.ui.ui_factory = TextUIFactory()
700
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
701
ret = run_bzr_catch_errors(argv)
702
mutter("return code %d", ret)
706
def run_bzr_catch_errors(argv):
711
# do this here inside the exception wrappers to catch EPIPE
714
# used to handle AssertionError and KeyboardInterrupt
715
# specially here, but hopefully they're handled ok by the logger now
717
if (isinstance(e, IOError)
718
and hasattr(e, 'errno')
719
and e.errno == errno.EPIPE):
720
bzrlib.trace.note('broken pipe')
723
bzrlib.trace.log_exception()
724
if os.environ.get('BZR_PDB'):
725
print '**** entering debugger'
727
pdb.post_mortem(sys.exc_traceback)
730
if __name__ == '__main__':
731
sys.exit(main(sys.argv))