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.
28
# TODO: Help messages for options.
30
# TODO: Define arguments by objects, rather than just using names.
31
# Those objects can specify the expected type of the argument, which
32
# would help with validation and shell completion.
35
# TODO: Help messages for options.
37
# TODO: Define arguments by objects, rather than just using names.
38
# Those objects can specify the expected type of the argument, which
39
# would help with validation and shell completion.
45
from warnings import warn
46
from inspect import getdoc
50
from bzrlib.trace import mutter, note, log_error, warning
51
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
52
from bzrlib.revisionspec import RevisionSpec
53
from bzrlib import BZRDIR
58
def register_command(cmd):
59
"Utility function to help register a 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)
70
log_error('Two plugins defined the same command: %r' % k)
71
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
74
def _squish_command_name(cmd):
75
return 'cmd_' + cmd.replace('-', '_')
78
def _unsquish_command_name(cmd):
79
assert cmd.startswith("cmd_")
80
return cmd[4:].replace('_','-')
83
def _parse_revision_str(revstr):
84
"""This handles a revision string -> revno.
86
This always returns a list. The list will have one element for
89
>>> _parse_revision_str('234')
90
[<RevisionSpec_int 234>]
91
>>> _parse_revision_str('234..567')
92
[<RevisionSpec_int 234>, <RevisionSpec_int 567>]
93
>>> _parse_revision_str('..')
94
[<RevisionSpec None>, <RevisionSpec None>]
95
>>> _parse_revision_str('..234')
96
[<RevisionSpec None>, <RevisionSpec_int 234>]
97
>>> _parse_revision_str('234..')
98
[<RevisionSpec_int 234>, <RevisionSpec None>]
99
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
100
[<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
101
>>> _parse_revision_str('234....789') # Error?
102
[<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
103
>>> _parse_revision_str('revid:test@other.com-234234')
104
[<RevisionSpec_revid revid:test@other.com-234234>]
105
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
106
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
107
>>> _parse_revision_str('revid:test@other.com-234234..23')
108
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
109
>>> _parse_revision_str('date:2005-04-12')
110
[<RevisionSpec_date date:2005-04-12>]
111
>>> _parse_revision_str('date:2005-04-12 12:24:33')
112
[<RevisionSpec_date date:2005-04-12 12:24:33>]
113
>>> _parse_revision_str('date:2005-04-12T12:24:33')
114
[<RevisionSpec_date date:2005-04-12T12:24:33>]
115
>>> _parse_revision_str('date:2005-04-12,12:24:33')
116
[<RevisionSpec_date date:2005-04-12,12:24:33>]
117
>>> _parse_revision_str('-5..23')
118
[<RevisionSpec_int -5>, <RevisionSpec_int 23>]
119
>>> _parse_revision_str('-5')
120
[<RevisionSpec_int -5>]
121
>>> _parse_revision_str('123a')
122
Traceback (most recent call last):
124
BzrError: No namespace registered for string: '123a'
125
>>> _parse_revision_str('abc')
126
Traceback (most recent call last):
128
BzrError: No namespace registered for string: 'abc'
131
old_format_re = re.compile('\d*:\d*')
132
m = old_format_re.match(revstr)
135
warning('Colon separator for revision numbers is deprecated.'
137
for rev in revstr.split(':'):
139
revs.append(RevisionSpec(int(rev)))
141
revs.append(RevisionSpec(None))
143
for x in revstr.split('..'):
145
revs.append(RevisionSpec(None))
147
revs.append(RevisionSpec(x))
151
def _builtin_commands():
152
import bzrlib.builtins
154
builtins = bzrlib.builtins.__dict__
155
for name in builtins:
156
if name.startswith("cmd_"):
157
real_name = _unsquish_command_name(name)
158
r[real_name] = builtins[name]
163
def builtin_command_names():
164
"""Return list of builtin command names."""
165
return _builtin_commands().keys()
168
def plugin_command_names():
169
return plugin_cmds.keys()
172
def _get_cmd_dict(plugins_override=True):
173
"""Return name->class mapping for all commands."""
174
d = _builtin_commands()
176
d.update(plugin_cmds)
180
def get_all_cmds(plugins_override=True):
181
"""Return canonical name and class for all registered commands."""
182
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
186
def get_cmd_object(cmd_name, plugins_override=True):
187
"""Return the canonical name and command class for a command.
190
If true, plugin commands can override builtins.
192
from bzrlib.externalcommand import ExternalCommand
194
cmd_name = str(cmd_name) # not unicode
196
# first look up this command under the specified name
197
cmds = _get_cmd_dict(plugins_override=plugins_override)
199
return cmds[cmd_name]()
203
# look for any command which claims this as an alias
204
for real_cmd_name, cmd_class in cmds.iteritems():
205
if cmd_name in cmd_class.aliases:
208
cmd_obj = ExternalCommand.find_command(cmd_name)
212
raise BzrCommandError("unknown command %r" % cmd_name)
215
class Command(object):
216
"""Base class for commands.
218
Commands are the heart of the command-line bzr interface.
220
The command object mostly handles the mapping of command-line
221
parameters into one or more bzrlib operations, and of the results
224
Commands normally don't have any state. All their arguments are
225
passed in to the run method. (Subclasses may take a different
226
policy if the behaviour of the instance needs to depend on e.g. a
227
shell plugin and not just its Python class.)
229
The docstring for an actual command should give a single-line
230
summary, then a complete description of the command. A grammar
231
description will be inserted.
234
Other accepted names for this command.
237
List of argument forms, marked with whether they are optional,
241
List of options that may be given for this command.
244
If true, this command isn't advertised. This is typically
245
for commands intended for expert users.
255
"""Construct an instance of this command."""
256
if self.__doc__ == Command.__doc__:
257
warn("No help message set for %r" % self)
260
def run_argv(self, argv):
261
"""Parse command line and run."""
262
args, opts = parse_args(argv)
264
if 'help' in opts: # e.g. bzr add --help
265
from bzrlib.help import help_on_command
266
help_on_command(self.name())
269
# check options are reasonable
270
allowed = self.takes_options
272
if oname not in allowed:
273
raise BzrCommandError("option '--%s' is not allowed for command %r"
274
% (oname, self.name()))
276
# mix arguments and options into one dictionary
277
cmdargs = _match_argform(self.name(), self.takes_args, args)
279
for k, v in opts.items():
280
cmdopts[k.replace('-', '_')] = v
282
all_cmd_args = cmdargs.copy()
283
all_cmd_args.update(cmdopts)
285
return self.run(**all_cmd_args)
289
"""Actually run the command.
291
This is invoked with the options and arguments bound to
294
Return 0 or None if the command was successful, or a non-zero
295
shell error code if not. It's OK for this method to allow
296
an exception to raise up.
298
raise NotImplementedError()
302
"""Return help message for this class."""
303
if self.__doc__ is Command.__doc__:
308
return _unsquish_command_name(self.__class__.__name__)
311
def parse_spec(spec):
317
>>> parse_spec("../@")
319
>>> parse_spec("../f/@35")
321
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
322
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
327
parsed = spec.split('/@')
328
assert len(parsed) == 2
333
parsed[1] = int(parsed[1])
335
pass # We can allow stuff like ./@revid:blahblahblah
339
parsed = [spec, None]
343
# list of all available options; the rhs can be either None for an
344
# option that takes no argument, or a constructor function that checks
358
'revision': _parse_revision_str,
383
def parse_args(argv):
384
"""Parse command line.
386
Arguments and options are parsed at this level before being passed
387
down to specific command handlers. This routine knows, from a
388
lookup table, something about the available options, what optargs
389
they take, and which commands will accept them.
391
>>> parse_args('--help'.split())
393
>>> parse_args('help -- --invalidcmd'.split())
394
(['help', '--invalidcmd'], {})
395
>>> parse_args('--version'.split())
396
([], {'version': True})
397
>>> parse_args('status --all'.split())
398
(['status'], {'all': True})
399
>>> parse_args('commit --message=biter'.split())
400
(['commit'], {'message': u'biter'})
401
>>> parse_args('log -r 500'.split())
402
(['log'], {'revision': [<RevisionSpec_int 500>]})
403
>>> parse_args('log -r500..600'.split())
404
(['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
405
>>> parse_args('log -vr500..600'.split())
406
(['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
407
>>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
408
(['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
416
if not argsover and a[0] == '-':
417
# option names must not be unicode
422
# We've received a standalone -- No more flags
425
mutter(" got option %r" % a)
427
optname, optarg = a[2:].split('=', 1)
430
if optname not in OPTIONS:
431
raise BzrError('unknown long option %r' % a)
434
if shortopt in SHORT_OPTIONS:
435
# Multi-character options must have a space to delimit
437
optname = SHORT_OPTIONS[shortopt]
439
# Single character short options, can be chained,
440
# and have their value appended to their name
442
if shortopt not in SHORT_OPTIONS:
443
# We didn't find the multi-character name, and we
444
# didn't find the single char name
445
raise BzrError('unknown short option %r' % a)
446
optname = SHORT_OPTIONS[shortopt]
449
# There are extra things on this option
450
# see if it is the value, or if it is another
452
optargfn = OPTIONS[optname]
454
# This option does not take an argument, so the
455
# next entry is another short option, pack it back
457
argv.insert(0, '-' + a[2:])
459
# This option takes an argument, so pack it
464
# XXX: Do we ever want to support this, e.g. for -r?
465
raise BzrError('repeated option %r' % a)
467
optargfn = OPTIONS[optname]
471
raise BzrError('option %r needs an argument' % a)
474
opts[optname] = optargfn(optarg)
477
raise BzrError('option %r takes no argument' % optname)
487
def _match_argform(cmd, takes_args, args):
490
# step through args and takes_args, allowing appropriate 0-many matches
491
for ap in takes_args:
495
argdict[argname] = args.pop(0)
496
elif ap[-1] == '*': # all remaining arguments
498
argdict[argname + '_list'] = args[:]
501
argdict[argname + '_list'] = None
504
raise BzrCommandError("command %r needs one or more %s"
505
% (cmd, argname.upper()))
507
argdict[argname + '_list'] = args[:]
509
elif ap[-1] == '$': # all but one
511
raise BzrCommandError("command %r needs one or more %s"
512
% (cmd, argname.upper()))
513
argdict[argname + '_list'] = args[:-1]
519
raise BzrCommandError("command %r requires argument %s"
520
% (cmd, argname.upper()))
522
argdict[argname] = args.pop(0)
525
raise BzrCommandError("extra argument to command %s: %s"
532
def apply_profiled(the_callable, *args, **kwargs):
535
pffileno, pfname = tempfile.mkstemp()
537
prof = hotshot.Profile(pfname)
539
ret = prof.runcall(the_callable, *args, **kwargs) or 0
544
stats = hotshot.stats.load(pfname)
546
stats.sort_stats('time')
547
## XXX: Might like to write to stderr or the trace file instead but
548
## print_stats seems hardcoded to stdout
549
stats.print_stats(20)
558
"""Execute a command.
560
This is similar to main(), but without all the trappings for
561
logging and error handling.
564
The command-line arguments, without the program name from argv[0]
566
Returns a command status or raises an exception.
568
Special master options: these must come before the command because
569
they control how the command is interpreted.
572
Do not load plugin modules at all
575
Only use builtin commands. (Plugins are still allowed to change
579
Run under the Python profiler.
581
# Load all of the transport methods
582
import bzrlib.transport.local, bzrlib.transport.http
584
argv = [a.decode(bzrlib.user_encoding) for a in argv]
586
opt_profile = opt_no_plugins = opt_builtin = False
588
# --no-plugins is handled specially at a very early stage. We need
589
# to load plugins before doing other command parsing so that they
590
# can override commands, but this needs to happen first.
595
elif a == '--no-plugins':
596
opt_no_plugins = True
597
elif a == '--builtin':
603
if (not argv) or (argv[0] == '--help'):
604
from bzrlib.help import help
611
if argv[0] == '--version':
612
from bzrlib.builtins import show_version
616
if not opt_no_plugins:
617
from bzrlib.plugin import load_plugins
620
cmd = str(argv.pop(0))
622
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
625
ret = apply_profiled(cmd_obj.run_argv, argv)
627
ret = cmd_obj.run_argv(argv)
633
bzrlib.trace.log_startup(argv)
634
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
636
return run_bzr_catch_errors(argv[1:])
639
def run_bzr_catch_errors(argv):
645
# do this here inside the exception wrappers to catch EPIPE
647
#wrap common errors as CommandErrors.
648
except (NotBranchError,), e:
649
raise BzrCommandError(str(e))
650
except BzrCommandError, e:
651
# command line syntax error, etc
655
bzrlib.trace.log_exception()
657
except AssertionError, e:
658
bzrlib.trace.log_exception('assertion failed: ' + str(e))
660
except KeyboardInterrupt, e:
661
bzrlib.trace.log_exception('interrupted')
665
if (isinstance(e, IOError)
666
and hasattr(e, 'errno')
667
and e.errno == errno.EPIPE):
668
bzrlib.trace.note('broken pipe')
671
bzrlib.trace.log_exception()
674
if __name__ == '__main__':
675
sys.exit(main(sys.argv))