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
output_encoding = bzrlib.user_encoding
241
mutter('encoding stdout bzrlib.user_encoding %r', output_encoding)
243
mutter('encoding stdout log as sys.stdout encoding %r', output_encoding)
245
# use 'replace' so that we don't abort if trying to write out
246
# in e.g. the default C locale.
247
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
249
@deprecated_method(zero_eight)
250
def run_argv(self, argv):
251
"""Parse command line and run.
253
See run_argv_aliases for the 0.8 and beyond api.
255
return self.run_argv_aliases(argv)
257
def run_argv_aliases(self, argv, alias_argv=None):
258
"""Parse the command line and run with extra aliases in alias_argv."""
259
args, opts = parse_args(self, argv, alias_argv)
260
if 'help' in opts: # e.g. bzr add --help
261
from bzrlib.help import help_on_command
262
help_on_command(self.name())
264
# XXX: This should be handled by the parser
265
allowed_names = self.options().keys()
267
if oname not in allowed_names:
268
raise BzrCommandError("option '--%s' is not allowed for"
269
" command %r" % (oname, self.name()))
270
# mix arguments and options into one dictionary
271
cmdargs = _match_argform(self.name(), self.takes_args, args)
273
for k, v in opts.items():
274
cmdopts[k.replace('-', '_')] = v
276
all_cmd_args = cmdargs.copy()
277
all_cmd_args.update(cmdopts)
281
return self.run(**all_cmd_args)
284
"""Actually run the command.
286
This is invoked with the options and arguments bound to
289
Return 0 or None if the command was successful, or a non-zero
290
shell error code if not. It's OK for this method to allow
291
an exception to raise up.
293
raise NotImplementedError('no implementation of command %r'
297
"""Return help message for this class."""
298
from inspect import getdoc
299
if self.__doc__ is Command.__doc__:
304
return _unsquish_command_name(self.__class__.__name__)
307
def parse_spec(spec):
313
>>> parse_spec("../@")
315
>>> parse_spec("../f/@35")
317
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
318
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
323
parsed = spec.split('/@')
324
assert len(parsed) == 2
329
parsed[1] = int(parsed[1])
331
pass # We can allow stuff like ./@revid:blahblahblah
335
parsed = [spec, None]
338
def parse_args(command, argv, alias_argv=None):
339
"""Parse command line.
341
Arguments and options are parsed at this level before being passed
342
down to specific command handlers. This routine knows, from a
343
lookup table, something about the available options, what optargs
344
they take, and which commands will accept them.
346
# TODO: chop up this beast; make it a method of the Command
351
cmd_options = command.options()
353
proc_aliasarg = True # Are we processing alias_argv now?
354
for proc_argv in alias_argv, argv:
361
# We've received a standalone -- No more flags
365
# option names must not be unicode
369
mutter(" got option %r", a)
371
optname, optarg = a[2:].split('=', 1)
374
if optname not in cmd_options:
375
raise BzrOptionError('unknown long option %r for'
380
if shortopt in Option.SHORT_OPTIONS:
381
# Multi-character options must have a space to delimit
383
# ^^^ what does this mean? mbp 20051014
384
optname = Option.SHORT_OPTIONS[shortopt].name
386
# Single character short options, can be chained,
387
# and have their value appended to their name
389
if shortopt not in Option.SHORT_OPTIONS:
390
# We didn't find the multi-character name, and we
391
# didn't find the single char name
392
raise BzrError('unknown short option %r' % a)
393
optname = Option.SHORT_OPTIONS[shortopt].name
396
# There are extra things on this option
397
# see if it is the value, or if it is another
399
optargfn = Option.OPTIONS[optname].type
401
# This option does not take an argument, so the
402
# next entry is another short option, pack it
404
proc_argv.insert(0, '-' + a[2:])
406
# This option takes an argument, so pack it
410
if optname not in cmd_options:
411
raise BzrOptionError('unknown short option %r for'
413
(shortopt, command.name()))
415
# XXX: Do we ever want to support this, e.g. for -r?
417
raise BzrError('repeated option %r' % a)
418
elif optname in alias_opts:
419
# Replace what's in the alias with what's in the real
421
del alias_opts[optname]
423
proc_argv.insert(0, a)
426
raise BzrError('repeated option %r' % a)
428
option_obj = cmd_options[optname]
429
optargfn = option_obj.type
433
raise BzrError('option %r needs an argument' % a)
435
optarg = proc_argv.pop(0)
436
opts[optname] = optargfn(optarg)
438
alias_opts[optname] = optargfn(optarg)
441
raise BzrError('option %r takes no argument' % optname)
444
alias_opts[optname] = True
447
proc_aliasarg = False # Done with alias argv
451
def _match_argform(cmd, takes_args, args):
454
# step through args and takes_args, allowing appropriate 0-many matches
455
for ap in takes_args:
459
argdict[argname] = args.pop(0)
460
elif ap[-1] == '*': # all remaining arguments
462
argdict[argname + '_list'] = args[:]
465
argdict[argname + '_list'] = None
468
raise BzrCommandError("command %r needs one or more %s"
469
% (cmd, argname.upper()))
471
argdict[argname + '_list'] = args[:]
473
elif ap[-1] == '$': # all but one
475
raise BzrCommandError("command %r needs one or more %s"
476
% (cmd, argname.upper()))
477
argdict[argname + '_list'] = args[:-1]
483
raise BzrCommandError("command %r requires argument %s"
484
% (cmd, argname.upper()))
486
argdict[argname] = args.pop(0)
489
raise BzrCommandError("extra argument to command %s: %s"
496
def apply_profiled(the_callable, *args, **kwargs):
500
pffileno, pfname = tempfile.mkstemp()
502
prof = hotshot.Profile(pfname)
504
ret = prof.runcall(the_callable, *args, **kwargs) or 0
507
stats = hotshot.stats.load(pfname)
509
stats.sort_stats('cum') # 'time'
510
## XXX: Might like to write to stderr or the trace file instead but
511
## print_stats seems hardcoded to stdout
512
stats.print_stats(20)
519
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
520
from bzrlib.lsprof import profile
522
ret, stats = profile(the_callable, *args, **kwargs)
528
cPickle.dump(stats, open(filename, 'w'), 2)
529
print 'Profile data written to %r.' % filename
534
"""Return an expanded alias, or None if no alias exists"""
536
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
538
return alias.split(' ')
543
"""Execute a command.
545
This is similar to main(), but without all the trappings for
546
logging and error handling.
549
The command-line arguments, without the program name from argv[0]
550
These should already be decoded. All library/test code calling
551
run_bzr should be passing valid strings (don't need decoding).
553
Returns a command status or raises an exception.
555
Special master options: these must come before the command because
556
they control how the command is interpreted.
559
Do not load plugin modules at all
565
Only use builtin commands. (Plugins are still allowed to change
569
Run under the Python hotshot profiler.
572
Run under the Python lsprof profiler.
576
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
577
opt_no_aliases = False
578
opt_lsprof_file = None
580
# --no-plugins is handled specially at a very early stage. We need
581
# to load plugins before doing other command parsing so that they
582
# can override commands, but this needs to happen first.
590
elif a == '--lsprof':
592
elif a == '--lsprof-file':
593
opt_lsprof_file = argv[i + 1]
595
elif a == '--no-plugins':
596
opt_no_plugins = True
597
elif a == '--no-aliases':
598
opt_no_aliases = True
599
elif a == '--builtin':
601
elif a in ('--quiet', '-q'):
609
from bzrlib.builtins import cmd_help
610
cmd_help().run_argv_aliases([])
613
if argv[0] == '--version':
614
from bzrlib.builtins import show_version
618
if not opt_no_plugins:
619
from bzrlib.plugin import load_plugins
622
from bzrlib.plugin import disable_plugins
627
if not opt_no_aliases:
628
alias_argv = get_alias(argv[0])
630
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
631
argv[0] = alias_argv.pop(0)
633
cmd = str(argv.pop(0))
635
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
636
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
637
run = cmd_obj.run_argv
640
run = cmd_obj.run_argv_aliases
641
run_argv = [argv, alias_argv]
645
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
647
ret = apply_profiled(run, *run_argv)
652
# reset, in case we may do other commands later within the same process
655
def display_command(func):
656
"""Decorator that suppresses pipe/interrupt errors."""
657
def ignore_pipe(*args, **kwargs):
659
result = func(*args, **kwargs)
663
if not hasattr(e, 'errno'):
665
if e.errno != errno.EPIPE:
668
except KeyboardInterrupt:
675
from bzrlib.ui.text import TextUIFactory
676
## bzrlib.trace.enable_default_logging()
677
bzrlib.trace.log_startup(argv)
678
bzrlib.ui.ui_factory = TextUIFactory()
680
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
681
ret = run_bzr_catch_errors(argv)
682
mutter("return code %d", ret)
686
def run_bzr_catch_errors(argv):
691
# do this here inside the exception wrappers to catch EPIPE
694
# used to handle AssertionError and KeyboardInterrupt
695
# specially here, but hopefully they're handled ok by the logger now
697
if (isinstance(e, IOError)
698
and hasattr(e, 'errno')
699
and e.errno == errno.EPIPE):
700
bzrlib.trace.note('broken pipe')
703
bzrlib.trace.log_exception()
704
if os.environ.get('BZR_PDB'):
705
print '**** entering debugger'
707
pdb.post_mortem(sys.exc_traceback)
710
if __name__ == '__main__':
711
sys.exit(main(sys.argv))