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__)
316
def parse_spec(spec):
322
>>> parse_spec("../@")
324
>>> parse_spec("../f/@35")
326
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
327
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
332
parsed = spec.split('/@')
333
assert len(parsed) == 2
338
parsed[1] = int(parsed[1])
340
pass # We can allow stuff like ./@revid:blahblahblah
344
parsed = [spec, None]
347
def parse_args(command, argv, alias_argv=None):
348
"""Parse command line.
350
Arguments and options are parsed at this level before being passed
351
down to specific command handlers. This routine knows, from a
352
lookup table, something about the available options, what optargs
353
they take, and which commands will accept them.
355
# TODO: chop up this beast; make it a method of the Command
360
cmd_options = command.options()
362
proc_aliasarg = True # Are we processing alias_argv now?
363
for proc_argv in alias_argv, argv:
370
# We've received a standalone -- No more flags
374
# option names must not be unicode
378
mutter(" got option %r", a)
380
optname, optarg = a[2:].split('=', 1)
383
if optname not in cmd_options:
384
raise BzrOptionError('unknown long option %r for'
389
if shortopt in Option.SHORT_OPTIONS:
390
# Multi-character options must have a space to delimit
392
# ^^^ what does this mean? mbp 20051014
393
optname = Option.SHORT_OPTIONS[shortopt].name
395
# Single character short options, can be chained,
396
# and have their value appended to their name
398
if shortopt not in Option.SHORT_OPTIONS:
399
# We didn't find the multi-character name, and we
400
# didn't find the single char name
401
raise BzrError('unknown short option %r' % a)
402
optname = Option.SHORT_OPTIONS[shortopt].name
405
# There are extra things on this option
406
# see if it is the value, or if it is another
408
optargfn = Option.OPTIONS[optname].type
410
# This option does not take an argument, so the
411
# next entry is another short option, pack it
413
proc_argv.insert(0, '-' + a[2:])
415
# This option takes an argument, so pack it
419
if optname not in cmd_options:
420
raise BzrOptionError('unknown short option %r for'
422
(shortopt, command.name()))
424
# XXX: Do we ever want to support this, e.g. for -r?
426
raise BzrError('repeated option %r' % a)
427
elif optname in alias_opts:
428
# Replace what's in the alias with what's in the real
430
del alias_opts[optname]
432
proc_argv.insert(0, a)
435
raise BzrError('repeated option %r' % a)
437
option_obj = cmd_options[optname]
438
optargfn = option_obj.type
442
raise BzrError('option %r needs an argument' % a)
444
optarg = proc_argv.pop(0)
445
opts[optname] = optargfn(optarg)
447
alias_opts[optname] = optargfn(optarg)
450
raise BzrError('option %r takes no argument' % optname)
453
alias_opts[optname] = True
456
proc_aliasarg = False # Done with alias argv
460
def _match_argform(cmd, takes_args, args):
463
# step through args and takes_args, allowing appropriate 0-many matches
464
for ap in takes_args:
468
argdict[argname] = args.pop(0)
469
elif ap[-1] == '*': # all remaining arguments
471
argdict[argname + '_list'] = args[:]
474
argdict[argname + '_list'] = None
477
raise BzrCommandError("command %r needs one or more %s"
478
% (cmd, argname.upper()))
480
argdict[argname + '_list'] = args[:]
482
elif ap[-1] == '$': # all but one
484
raise BzrCommandError("command %r needs one or more %s"
485
% (cmd, argname.upper()))
486
argdict[argname + '_list'] = args[:-1]
492
raise BzrCommandError("command %r requires argument %s"
493
% (cmd, argname.upper()))
495
argdict[argname] = args.pop(0)
498
raise BzrCommandError("extra argument to command %s: %s"
505
def apply_profiled(the_callable, *args, **kwargs):
509
pffileno, pfname = tempfile.mkstemp()
511
prof = hotshot.Profile(pfname)
513
ret = prof.runcall(the_callable, *args, **kwargs) or 0
516
stats = hotshot.stats.load(pfname)
518
stats.sort_stats('cum') # 'time'
519
## XXX: Might like to write to stderr or the trace file instead but
520
## print_stats seems hardcoded to stdout
521
stats.print_stats(20)
528
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
529
from bzrlib.lsprof import profile
531
ret, stats = profile(the_callable, *args, **kwargs)
537
cPickle.dump(stats, open(filename, 'w'), 2)
538
print 'Profile data written to %r.' % filename
543
"""Return an expanded alias, or None if no alias exists"""
545
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
547
return alias.split(' ')
552
"""Execute a command.
554
This is similar to main(), but without all the trappings for
555
logging and error handling.
558
The command-line arguments, without the program name from argv[0]
559
These should already be decoded. All library/test code calling
560
run_bzr should be passing valid strings (don't need decoding).
562
Returns a command status or raises an exception.
564
Special master options: these must come before the command because
565
they control how the command is interpreted.
568
Do not load plugin modules at all
574
Only use builtin commands. (Plugins are still allowed to change
578
Run under the Python hotshot profiler.
581
Run under the Python lsprof profiler.
585
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
586
opt_no_aliases = False
587
opt_lsprof_file = None
589
# --no-plugins is handled specially at a very early stage. We need
590
# to load plugins before doing other command parsing so that they
591
# can override commands, but this needs to happen first.
599
elif a == '--lsprof':
601
elif a == '--lsprof-file':
602
opt_lsprof_file = argv[i + 1]
604
elif a == '--no-plugins':
605
opt_no_plugins = True
606
elif a == '--no-aliases':
607
opt_no_aliases = True
608
elif a == '--builtin':
610
elif a in ('--quiet', '-q'):
618
from bzrlib.builtins import cmd_help
619
cmd_help().run_argv_aliases([])
622
if argv[0] == '--version':
623
from bzrlib.builtins import show_version
627
if not opt_no_plugins:
628
from bzrlib.plugin import load_plugins
631
from bzrlib.plugin import disable_plugins
636
if not opt_no_aliases:
637
alias_argv = get_alias(argv[0])
639
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
640
argv[0] = alias_argv.pop(0)
642
cmd = str(argv.pop(0))
644
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
645
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
646
run = cmd_obj.run_argv
649
run = cmd_obj.run_argv_aliases
650
run_argv = [argv, alias_argv]
654
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
656
ret = apply_profiled(run, *run_argv)
661
# reset, in case we may do other commands later within the same process
664
def display_command(func):
665
"""Decorator that suppresses pipe/interrupt errors."""
666
def ignore_pipe(*args, **kwargs):
668
result = func(*args, **kwargs)
672
if not hasattr(e, 'errno'):
674
if e.errno != errno.EPIPE:
677
except KeyboardInterrupt:
684
from bzrlib.ui.text import TextUIFactory
685
## bzrlib.trace.enable_default_logging()
686
bzrlib.trace.log_startup(argv)
687
bzrlib.ui.ui_factory = TextUIFactory()
689
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
690
ret = run_bzr_catch_errors(argv)
691
mutter("return code %d", ret)
695
def run_bzr_catch_errors(argv):
700
# do this here inside the exception wrappers to catch EPIPE
703
# used to handle AssertionError and KeyboardInterrupt
704
# specially here, but hopefully they're handled ok by the logger now
706
if (isinstance(e, IOError)
707
and hasattr(e, 'errno')
708
and e.errno == errno.EPIPE):
709
bzrlib.trace.note('broken pipe')
712
bzrlib.trace.log_exception()
713
if os.environ.get('BZR_PDB'):
714
print '**** entering debugger'
716
pdb.post_mortem(sys.exc_traceback)
719
if __name__ == '__main__':
720
sys.exit(main(sys.argv))