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
37
from bzrlib.errors import (BzrError,
42
from bzrlib.option import Option
43
from bzrlib.revisionspec import RevisionSpec
44
from bzrlib.symbol_versioning import *
46
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
51
def register_command(cmd, decorate=False):
52
"Utility function to help register a command"
55
if k.startswith("cmd_"):
56
k_unsquished = _unsquish_command_name(k)
59
if not plugin_cmds.has_key(k_unsquished):
60
plugin_cmds[k_unsquished] = cmd
61
mutter('registered plugin command %s', k_unsquished)
62
if decorate and k_unsquished in builtin_command_names():
63
return _builtin_commands()[k_unsquished]
65
result = plugin_cmds[k_unsquished]
66
plugin_cmds[k_unsquished] = cmd
69
log_error('Two plugins defined the same command: %r' % k)
70
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
73
def _squish_command_name(cmd):
74
return 'cmd_' + cmd.replace('-', '_')
77
def _unsquish_command_name(cmd):
78
assert cmd.startswith("cmd_")
79
return cmd[4:].replace('_','-')
82
def _builtin_commands():
83
import bzrlib.builtins
85
builtins = bzrlib.builtins.__dict__
87
if name.startswith("cmd_"):
88
real_name = _unsquish_command_name(name)
89
r[real_name] = builtins[name]
94
def builtin_command_names():
95
"""Return list of builtin command names."""
96
return _builtin_commands().keys()
99
def plugin_command_names():
100
return plugin_cmds.keys()
103
def _get_cmd_dict(plugins_override=True):
104
"""Return name->class mapping for all commands."""
105
d = _builtin_commands()
107
d.update(plugin_cmds)
111
def get_all_cmds(plugins_override=True):
112
"""Return canonical name and class for all registered commands."""
113
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
117
def get_cmd_object(cmd_name, plugins_override=True):
118
"""Return the canonical name and command class for a command.
121
If true, plugin commands can override builtins.
123
from bzrlib.externalcommand import ExternalCommand
125
cmd_name = str(cmd_name) # not unicode
127
# first look up this command under the specified name
128
cmds = _get_cmd_dict(plugins_override=plugins_override)
130
return cmds[cmd_name]()
134
# look for any command which claims this as an alias
135
for real_cmd_name, cmd_class in cmds.iteritems():
136
if cmd_name in cmd_class.aliases:
139
cmd_obj = ExternalCommand.find_command(cmd_name)
143
raise BzrCommandError("unknown command %r" % cmd_name)
146
class Command(object):
147
"""Base class for commands.
149
Commands are the heart of the command-line bzr interface.
151
The command object mostly handles the mapping of command-line
152
parameters into one or more bzrlib operations, and of the results
155
Commands normally don't have any state. All their arguments are
156
passed in to the run method. (Subclasses may take a different
157
policy if the behaviour of the instance needs to depend on e.g. a
158
shell plugin and not just its Python class.)
160
The docstring for an actual command should give a single-line
161
summary, then a complete description of the command. A grammar
162
description will be inserted.
165
Other accepted names for this command.
168
List of argument forms, marked with whether they are optional,
173
['to_location', 'from_branch?', 'file*']
175
'to_location' is required
176
'from_branch' is optional
177
'file' can be specified 0 or more times
180
List of options that may be given for this command. These can
181
be either strings, referring to globally-defined options,
182
or option objects. Retrieve through options().
185
If true, this command isn't advertised. This is typically
186
for commands intended for expert users.
195
"""Construct an instance of this command."""
196
if self.__doc__ == Command.__doc__:
197
warn("No help message set for %r" % self)
200
"""Return dict of valid options for this command.
202
Maps from long option name to option object."""
204
r['help'] = Option.OPTIONS['help']
205
for o in self.takes_options:
206
if not isinstance(o, Option):
207
o = Option.OPTIONS[o]
211
@deprecated_method(zero_eight)
212
def run_argv(self, argv):
213
"""Parse command line and run.
215
See run_argv_aliases for the 0.8 and beyond api.
217
return self.run_argv_aliases(argv)
219
def run_argv_aliases(self, argv, alias_argv=None):
220
"""Parse the command line and run with extra aliases in alias_argv."""
221
args, opts = parse_args(self, argv, alias_argv)
222
if 'help' in opts: # e.g. bzr add --help
223
from bzrlib.help import help_on_command
224
help_on_command(self.name())
226
# XXX: This should be handled by the parser
227
allowed_names = self.options().keys()
229
if oname not in allowed_names:
230
raise BzrCommandError("option '--%s' is not allowed for"
231
" command %r" % (oname, self.name()))
232
# mix arguments and options into one dictionary
233
cmdargs = _match_argform(self.name(), self.takes_args, args)
235
for k, v in opts.items():
236
cmdopts[k.replace('-', '_')] = v
238
all_cmd_args = cmdargs.copy()
239
all_cmd_args.update(cmdopts)
241
return self.run(**all_cmd_args)
244
"""Actually run the command.
246
This is invoked with the options and arguments bound to
249
Return 0 or None if the command was successful, or a non-zero
250
shell error code if not. It's OK for this method to allow
251
an exception to raise up.
253
raise NotImplementedError('no implementation of command %r'
257
"""Return help message for this class."""
258
from inspect import getdoc
259
if self.__doc__ is Command.__doc__:
264
return _unsquish_command_name(self.__class__.__name__)
267
def parse_spec(spec):
273
>>> parse_spec("../@")
275
>>> parse_spec("../f/@35")
277
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
278
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
283
parsed = spec.split('/@')
284
assert len(parsed) == 2
289
parsed[1] = int(parsed[1])
291
pass # We can allow stuff like ./@revid:blahblahblah
295
parsed = [spec, None]
298
def parse_args(command, argv, alias_argv=None):
299
"""Parse command line.
301
Arguments and options are parsed at this level before being passed
302
down to specific command handlers. This routine knows, from a
303
lookup table, something about the available options, what optargs
304
they take, and which commands will accept them.
306
# TODO: chop up this beast; make it a method of the Command
311
cmd_options = command.options()
313
proc_aliasarg = True # Are we processing alias_argv now?
314
for proc_argv in alias_argv, argv:
321
# We've received a standalone -- No more flags
325
# option names must not be unicode
329
mutter(" got option %r", a)
331
optname, optarg = a[2:].split('=', 1)
334
if optname not in cmd_options:
335
raise BzrOptionError('unknown long option %r for'
340
if shortopt in Option.SHORT_OPTIONS:
341
# Multi-character options must have a space to delimit
343
# ^^^ what does this mean? mbp 20051014
344
optname = Option.SHORT_OPTIONS[shortopt].name
346
# Single character short options, can be chained,
347
# and have their value appended to their name
349
if shortopt not in Option.SHORT_OPTIONS:
350
# We didn't find the multi-character name, and we
351
# didn't find the single char name
352
raise BzrError('unknown short option %r' % a)
353
optname = Option.SHORT_OPTIONS[shortopt].name
356
# There are extra things on this option
357
# see if it is the value, or if it is another
359
optargfn = Option.OPTIONS[optname].type
361
# This option does not take an argument, so the
362
# next entry is another short option, pack it
364
proc_argv.insert(0, '-' + a[2:])
366
# This option takes an argument, so pack it
370
if optname not in cmd_options:
371
raise BzrOptionError('unknown short option %r for'
373
(shortopt, command.name()))
375
# XXX: Do we ever want to support this, e.g. for -r?
377
raise BzrError('repeated option %r' % a)
378
elif optname in alias_opts:
379
# Replace what's in the alias with what's in the real
381
del alias_opts[optname]
383
proc_argv.insert(0, a)
386
raise BzrError('repeated option %r' % a)
388
option_obj = cmd_options[optname]
389
optargfn = option_obj.type
393
raise BzrError('option %r needs an argument' % a)
395
optarg = proc_argv.pop(0)
396
opts[optname] = optargfn(optarg)
398
alias_opts[optname] = optargfn(optarg)
401
raise BzrError('option %r takes no argument' % optname)
404
alias_opts[optname] = True
407
proc_aliasarg = False # Done with alias argv
411
def _match_argform(cmd, takes_args, args):
414
# step through args and takes_args, allowing appropriate 0-many matches
415
for ap in takes_args:
419
argdict[argname] = args.pop(0)
420
elif ap[-1] == '*': # all remaining arguments
422
argdict[argname + '_list'] = args[:]
425
argdict[argname + '_list'] = None
428
raise BzrCommandError("command %r needs one or more %s"
429
% (cmd, argname.upper()))
431
argdict[argname + '_list'] = args[:]
433
elif ap[-1] == '$': # all but one
435
raise BzrCommandError("command %r needs one or more %s"
436
% (cmd, argname.upper()))
437
argdict[argname + '_list'] = args[:-1]
443
raise BzrCommandError("command %r requires argument %s"
444
% (cmd, argname.upper()))
446
argdict[argname] = args.pop(0)
449
raise BzrCommandError("extra argument to command %s: %s"
456
def apply_profiled(the_callable, *args, **kwargs):
460
pffileno, pfname = tempfile.mkstemp()
462
prof = hotshot.Profile(pfname)
464
ret = prof.runcall(the_callable, *args, **kwargs) or 0
467
stats = hotshot.stats.load(pfname)
469
stats.sort_stats('cum') # 'time'
470
## XXX: Might like to write to stderr or the trace file instead but
471
## print_stats seems hardcoded to stdout
472
stats.print_stats(20)
479
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
480
from bzrlib.lsprof import profile
482
ret, stats = profile(the_callable, *args, **kwargs)
488
cPickle.dump(stats, open(filename, 'w'), 2)
489
print 'Profile data written to %r.' % filename
494
"""Return an expanded alias, or None if no alias exists"""
496
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
498
return alias.split(' ')
503
"""Execute a command.
505
This is similar to main(), but without all the trappings for
506
logging and error handling.
509
The command-line arguments, without the program name from argv[0]
511
Returns a command status or raises an exception.
513
Special master options: these must come before the command because
514
they control how the command is interpreted.
517
Do not load plugin modules at all
523
Only use builtin commands. (Plugins are still allowed to change
527
Run under the Python hotshot profiler.
530
Run under the Python lsprof profiler.
532
argv = [a.decode(bzrlib.user_encoding) for a in argv]
534
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
535
opt_no_aliases = False
536
opt_lsprof_file = None
538
# --no-plugins is handled specially at a very early stage. We need
539
# to load plugins before doing other command parsing so that they
540
# can override commands, but this needs to happen first.
548
elif a == '--lsprof':
550
elif a == '--lsprof-file':
551
opt_lsprof_file = argv[i + 1]
553
elif a == '--no-plugins':
554
opt_no_plugins = True
555
elif a == '--no-aliases':
556
opt_no_aliases = True
557
elif a == '--builtin':
559
elif a in ('--quiet', '-q'):
566
if (not argv) or (argv[0] == '--help'):
567
from bzrlib.help import help
574
if argv[0] == '--version':
575
from bzrlib.builtins import show_version
579
if not opt_no_plugins:
580
from bzrlib.plugin import load_plugins
583
from bzrlib.plugin import disable_plugins
588
if not opt_no_aliases:
589
alias_argv = get_alias(argv[0])
591
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
592
argv[0] = alias_argv.pop(0)
594
cmd = str(argv.pop(0))
596
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
597
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
598
run = cmd_obj.run_argv
601
run = cmd_obj.run_argv_aliases
602
run_argv = [argv, alias_argv]
606
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
608
ret = apply_profiled(run, *run_argv)
613
# reset, in case we may do other commands later within the same process
616
def display_command(func):
617
"""Decorator that suppresses pipe/interrupt errors."""
618
def ignore_pipe(*args, **kwargs):
620
result = func(*args, **kwargs)
624
if not hasattr(e, 'errno'):
626
if e.errno != errno.EPIPE:
629
except KeyboardInterrupt:
636
from bzrlib.ui.text import TextUIFactory
637
## bzrlib.trace.enable_default_logging()
638
bzrlib.trace.log_startup(argv)
639
bzrlib.ui.ui_factory = TextUIFactory()
640
ret = run_bzr_catch_errors(argv[1:])
641
mutter("return code %d", ret)
645
def run_bzr_catch_errors(argv):
650
# do this here inside the exception wrappers to catch EPIPE
653
# used to handle AssertionError and KeyboardInterrupt
654
# specially here, but hopefully they're handled ok by the logger now
656
if (isinstance(e, IOError)
657
and hasattr(e, 'errno')
658
and e.errno == errno.EPIPE):
659
bzrlib.trace.note('broken pipe')
662
bzrlib.trace.log_exception()
663
if os.environ.get('BZR_PDB'):
664
print '**** entering debugger'
666
pdb.post_mortem(sys.exc_traceback)
669
if __name__ == '__main__':
670
sys.exit(main(sys.argv))