1
# Copyright (C) 2004, 2005 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: Help messages for options.
23
# TODO: Define arguments by objects, rather than just using names.
24
# Those objects can specify the expected type of the argument, which
25
# would help with validation and shell completion.
27
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
28
# the profile output behind so it can be interactively examined?
32
from warnings import warn
33
from inspect import getdoc
37
from bzrlib.config import GlobalConfig
39
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
40
from bzrlib.errors import (BzrError,
47
from bzrlib.revisionspec import RevisionSpec
48
from bzrlib import BZRDIR
49
from bzrlib.option import Option
54
def register_command(cmd, decorate=False):
55
"Utility function to help register a command"
58
if k.startswith("cmd_"):
59
k_unsquished = _unsquish_command_name(k)
62
if not plugin_cmds.has_key(k_unsquished):
63
plugin_cmds[k_unsquished] = cmd
64
mutter('registered plugin command %s', k_unsquished)
65
if decorate and k_unsquished in builtin_command_names():
66
return _builtin_commands()[k_unsquished]
68
result = plugin_cmds[k_unsquished]
69
plugin_cmds[k_unsquished] = cmd
72
log_error('Two plugins defined the same command: %r' % k)
73
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
76
def _squish_command_name(cmd):
77
return 'cmd_' + cmd.replace('-', '_')
80
def _unsquish_command_name(cmd):
81
assert cmd.startswith("cmd_")
82
return cmd[4:].replace('_','-')
85
def _builtin_commands():
86
import bzrlib.builtins
88
builtins = bzrlib.builtins.__dict__
90
if name.startswith("cmd_"):
91
real_name = _unsquish_command_name(name)
92
r[real_name] = builtins[name]
97
def builtin_command_names():
98
"""Return list of builtin command names."""
99
return _builtin_commands().keys()
102
def plugin_command_names():
103
return plugin_cmds.keys()
106
def _get_cmd_dict(plugins_override=True):
107
"""Return name->class mapping for all commands."""
108
d = _builtin_commands()
110
d.update(plugin_cmds)
114
def get_all_cmds(plugins_override=True):
115
"""Return canonical name and class for all registered commands."""
116
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
120
def get_cmd_object(cmd_name, plugins_override=True):
121
"""Return the canonical name and command class for a command.
124
If true, plugin commands can override builtins.
126
from bzrlib.externalcommand import ExternalCommand
128
cmd_name = str(cmd_name) # not unicode
130
# first look up this command under the specified name
131
cmds = _get_cmd_dict(plugins_override=plugins_override)
133
return cmds[cmd_name]()
137
# look for any command which claims this as an alias
138
for real_cmd_name, cmd_class in cmds.iteritems():
139
if cmd_name in cmd_class.aliases:
142
cmd_obj = ExternalCommand.find_command(cmd_name)
146
raise BzrCommandError("unknown command %r" % cmd_name)
149
class Command(object):
150
"""Base class for commands.
152
Commands are the heart of the command-line bzr interface.
154
The command object mostly handles the mapping of command-line
155
parameters into one or more bzrlib operations, and of the results
158
Commands normally don't have any state. All their arguments are
159
passed in to the run method. (Subclasses may take a different
160
policy if the behaviour of the instance needs to depend on e.g. a
161
shell plugin and not just its Python class.)
163
The docstring for an actual command should give a single-line
164
summary, then a complete description of the command. A grammar
165
description will be inserted.
168
Other accepted names for this command.
171
List of argument forms, marked with whether they are optional,
176
['to_location', 'from_branch?', 'file*']
178
'to_location' is required
179
'from_branch' is optional
180
'file' can be specified 0 or more times
183
List of options that may be given for this command. These can
184
be either strings, referring to globally-defined options,
185
or option objects. Retrieve through options().
188
If true, this command isn't advertised. This is typically
189
for commands intended for expert users.
198
"""Construct an instance of this command."""
199
if self.__doc__ == Command.__doc__:
200
warn("No help message set for %r" % self)
203
"""Return dict of valid options for this command.
205
Maps from long option name to option object."""
207
r['help'] = Option.OPTIONS['help']
208
for o in self.takes_options:
209
if not isinstance(o, Option):
210
o = Option.OPTIONS[o]
214
def run_argv(self, argv, defaults=None):
215
"""Parse command line and run."""
216
if defaults is not None:
217
args, opts = parse_args(self, defaults)
221
cmd_args, cmd_opts = parse_args(self, argv)
222
args.extend(cmd_args)
223
opts.update(cmd_opts)
224
if 'help' in opts: # e.g. bzr add --help
225
from bzrlib.help import help_on_command
226
help_on_command(self.name())
228
# XXX: This should be handled by the parser
229
allowed_names = self.options().keys()
231
if oname not in allowed_names:
232
raise BzrCommandError("option '--%s' is not allowed for command %r"
233
% (oname, self.name()))
234
# mix arguments and options into one dictionary
235
cmdargs = _match_argform(self.name(), self.takes_args, args)
237
for k, v in opts.items():
238
cmdopts[k.replace('-', '_')] = v
240
all_cmd_args = cmdargs.copy()
241
all_cmd_args.update(cmdopts)
243
return self.run(**all_cmd_args)
246
"""Actually run the command.
248
This is invoked with the options and arguments bound to
251
Return 0 or None if the command was successful, or a non-zero
252
shell error code if not. It's OK for this method to allow
253
an exception to raise up.
255
raise NotImplementedError()
259
"""Return help message for this class."""
260
if self.__doc__ is Command.__doc__:
265
return _unsquish_command_name(self.__class__.__name__)
268
def parse_spec(spec):
274
>>> parse_spec("../@")
276
>>> parse_spec("../f/@35")
278
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
279
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
284
parsed = spec.split('/@')
285
assert len(parsed) == 2
290
parsed[1] = int(parsed[1])
292
pass # We can allow stuff like ./@revid:blahblahblah
296
parsed = [spec, None]
299
def parse_args(command, argv):
300
"""Parse command line.
302
Arguments and options are parsed at this level before being passed
303
down to specific command handlers. This routine knows, from a
304
lookup table, something about the available options, what optargs
305
they take, and which commands will accept them.
307
# TODO: chop up this beast; make it a method of the Command
311
cmd_options = command.options()
319
# We've received a standalone -- No more flags
323
# option names must not be unicode
327
mutter(" got option %r", a)
329
optname, optarg = a[2:].split('=', 1)
332
if optname not in cmd_options:
333
raise BzrOptionError('unknown long option %r for command %s'
334
% (a, command.name()))
337
if shortopt in Option.SHORT_OPTIONS:
338
# Multi-character options must have a space to delimit
340
# ^^^ what does this mean? mbp 20051014
341
optname = Option.SHORT_OPTIONS[shortopt].name
343
# Single character short options, can be chained,
344
# and have their value appended to their name
346
if shortopt not in Option.SHORT_OPTIONS:
347
# We didn't find the multi-character name, and we
348
# didn't find the single char name
349
raise BzrError('unknown short option %r' % a)
350
optname = Option.SHORT_OPTIONS[shortopt].name
353
# There are extra things on this option
354
# see if it is the value, or if it is another
356
optargfn = Option.OPTIONS[optname].type
358
# This option does not take an argument, so the
359
# next entry is another short option, pack it back
361
argv.insert(0, '-' + a[2:])
363
# This option takes an argument, so pack it
367
if optname not in cmd_options:
368
raise BzrOptionError('unknown short option %r for command'
369
' %s' % (shortopt, command.name()))
371
# XXX: Do we ever want to support this, e.g. for -r?
372
raise BzrError('repeated option %r' % a)
374
option_obj = cmd_options[optname]
375
optargfn = option_obj.type
379
raise BzrError('option %r needs an argument' % a)
382
opts[optname] = optargfn(optarg)
385
raise BzrError('option %r takes no argument' % optname)
392
def _match_argform(cmd, takes_args, args):
395
# step through args and takes_args, allowing appropriate 0-many matches
396
for ap in takes_args:
400
argdict[argname] = args.pop(0)
401
elif ap[-1] == '*': # all remaining arguments
403
argdict[argname + '_list'] = args[:]
406
argdict[argname + '_list'] = None
409
raise BzrCommandError("command %r needs one or more %s"
410
% (cmd, argname.upper()))
412
argdict[argname + '_list'] = args[:]
414
elif ap[-1] == '$': # all but one
416
raise BzrCommandError("command %r needs one or more %s"
417
% (cmd, argname.upper()))
418
argdict[argname + '_list'] = args[:-1]
424
raise BzrCommandError("command %r requires argument %s"
425
% (cmd, argname.upper()))
427
argdict[argname] = args.pop(0)
430
raise BzrCommandError("extra argument to command %s: %s"
437
def apply_profiled(the_callable, *args, **kwargs):
441
pffileno, pfname = tempfile.mkstemp()
443
prof = hotshot.Profile(pfname)
445
ret = prof.runcall(the_callable, *args, **kwargs) or 0
448
stats = hotshot.stats.load(pfname)
450
stats.sort_stats('cum') # 'time'
451
## XXX: Might like to write to stderr or the trace file instead but
452
## print_stats seems hardcoded to stdout
453
stats.print_stats(20)
460
def apply_lsprofiled(the_callable, *args, **kwargs):
461
from bzrlib.lsprof import profile
462
ret,stats = profile(the_callable,*args,**kwargs)
468
"""Execute a command.
470
This is similar to main(), but without all the trappings for
471
logging and error handling.
474
The command-line arguments, without the program name from argv[0]
476
Returns a command status or raises an exception.
478
Special master options: these must come before the command because
479
they control how the command is interpreted.
482
Do not load plugin modules at all
485
Only use builtin commands. (Plugins are still allowed to change
489
Run under the Python hotshot profiler.
492
Run under the Python lsprof profiler.
494
argv = [a.decode(bzrlib.user_encoding) for a in argv]
496
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = False
497
opt_no_defaults = False
499
# --no-plugins is handled specially at a very early stage. We need
500
# to load plugins before doing other command parsing so that they
501
# can override commands, but this needs to happen first.
506
elif a == '--lsprof':
508
elif a == '--no-plugins':
509
opt_no_plugins = True
510
elif a == '--builtin':
512
elif a in ('--quiet', '-q'):
514
elif a in ('--no-defaults',):
515
opt_no_defaults = True
520
if (not argv) or (argv[0] == '--help'):
521
from bzrlib.help import help
528
if argv[0] == '--version':
529
from bzrlib.builtins import show_version
533
if not opt_no_plugins:
534
from bzrlib.plugin import load_plugins
537
cmd = str(argv.pop(0))
539
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
540
if opt_no_defaults is True:
543
cmd_defaults = GlobalConfig().get_command_defaults(cmd_obj.name())
546
ret = apply_lsprofiled(cmd_obj.run_argv, argv, cmd_defaults)
548
ret = apply_profiled(cmd_obj.run_argv, argv, cmd_defaults)
550
ret = cmd_obj.run_argv(argv, cmd_defaults)
553
# reset, in case we may do other commands later within the same process
556
def display_command(func):
557
"""Decorator that suppresses pipe/interrupt errors."""
558
def ignore_pipe(*args, **kwargs):
560
result = func(*args, **kwargs)
564
if not hasattr(e, 'errno'):
566
if e.errno != errno.EPIPE:
569
except KeyboardInterrupt:
576
from bzrlib.ui.text import TextUIFactory
577
## bzrlib.trace.enable_default_logging()
578
bzrlib.trace.log_startup(argv)
579
bzrlib.ui.ui_factory = TextUIFactory()
580
ret = run_bzr_catch_errors(argv[1:])
581
mutter("return code %d", ret)
585
def run_bzr_catch_errors(argv):
590
# do this here inside the exception wrappers to catch EPIPE
593
# used to handle AssertionError and KeyboardInterrupt
594
# specially here, but hopefully they're handled ok by the logger now
596
if (isinstance(e, IOError)
597
and hasattr(e, 'errno')
598
and e.errno == errno.EPIPE):
599
bzrlib.trace.note('broken pipe')
602
bzrlib.trace.log_exception()
603
if os.environ.get('BZR_PDB'):
604
print '**** entering debugger'
606
pdb.post_mortem(sys.exc_traceback)
609
if __name__ == '__main__':
610
sys.exit(main(sys.argv))