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.
31
from warnings import warn
32
from inspect import getdoc
36
from bzrlib.trace import mutter, note, log_error, warning
37
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
38
from bzrlib.revisionspec import RevisionSpec
39
from bzrlib import BZRDIR
44
def register_command(cmd):
45
"Utility function to help register a command"
48
if k.startswith("cmd_"):
49
k_unsquished = _unsquish_command_name(k)
52
if not plugin_cmds.has_key(k_unsquished):
53
plugin_cmds[k_unsquished] = cmd
54
mutter('registered plugin command %s', k_unsquished)
56
log_error('Two plugins defined the same command: %r' % k)
57
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
60
def _squish_command_name(cmd):
61
return 'cmd_' + cmd.replace('-', '_')
64
def _unsquish_command_name(cmd):
65
assert cmd.startswith("cmd_")
66
return cmd[4:].replace('_','-')
69
def _parse_revision_str(revstr):
70
"""This handles a revision string -> revno.
72
This always returns a list. The list will have one element for
75
>>> _parse_revision_str('234')
76
[<RevisionSpec_int 234>]
77
>>> _parse_revision_str('234..567')
78
[<RevisionSpec_int 234>, <RevisionSpec_int 567>]
79
>>> _parse_revision_str('..')
80
[<RevisionSpec None>, <RevisionSpec None>]
81
>>> _parse_revision_str('..234')
82
[<RevisionSpec None>, <RevisionSpec_int 234>]
83
>>> _parse_revision_str('234..')
84
[<RevisionSpec_int 234>, <RevisionSpec None>]
85
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
86
[<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
87
>>> _parse_revision_str('234....789') # Error?
88
[<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
89
>>> _parse_revision_str('revid:test@other.com-234234')
90
[<RevisionSpec_revid revid:test@other.com-234234>]
91
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
92
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
93
>>> _parse_revision_str('revid:test@other.com-234234..23')
94
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
95
>>> _parse_revision_str('date:2005-04-12')
96
[<RevisionSpec_date date:2005-04-12>]
97
>>> _parse_revision_str('date:2005-04-12 12:24:33')
98
[<RevisionSpec_date date:2005-04-12 12:24:33>]
99
>>> _parse_revision_str('date:2005-04-12T12:24:33')
100
[<RevisionSpec_date date:2005-04-12T12:24:33>]
101
>>> _parse_revision_str('date:2005-04-12,12:24:33')
102
[<RevisionSpec_date date:2005-04-12,12:24:33>]
103
>>> _parse_revision_str('-5..23')
104
[<RevisionSpec_int -5>, <RevisionSpec_int 23>]
105
>>> _parse_revision_str('-5')
106
[<RevisionSpec_int -5>]
107
>>> _parse_revision_str('123a')
108
Traceback (most recent call last):
110
BzrError: No namespace registered for string: '123a'
111
>>> _parse_revision_str('abc')
112
Traceback (most recent call last):
114
BzrError: No namespace registered for string: 'abc'
117
old_format_re = re.compile('\d*:\d*')
118
m = old_format_re.match(revstr)
121
warning('Colon separator for revision numbers is deprecated.'
123
for rev in revstr.split(':'):
125
revs.append(RevisionSpec(int(rev)))
127
revs.append(RevisionSpec(None))
129
for x in revstr.split('..'):
131
revs.append(RevisionSpec(None))
133
revs.append(RevisionSpec(x))
137
def get_merge_type(typestring):
138
"""Attempt to find the merge class/factory associated with a string."""
139
from merge import merge_types
141
return merge_types[typestring][0]
143
templ = '%s%%7s: %%s' % (' '*12)
144
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
145
type_list = '\n'.join(lines)
146
msg = "No known merge type %s. Supported types are:\n%s" %\
147
(typestring, type_list)
148
raise BzrCommandError(msg)
151
def get_merge_type(typestring):
152
"""Attempt to find the merge class/factory associated with a string."""
153
from merge import merge_types
155
return merge_types[typestring][0]
157
templ = '%s%%7s: %%s' % (' '*12)
158
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
159
type_list = '\n'.join(lines)
160
msg = "No known merge type %s. Supported types are:\n%s" %\
161
(typestring, type_list)
162
raise BzrCommandError(msg)
165
def _builtin_commands():
166
import bzrlib.builtins
168
builtins = bzrlib.builtins.__dict__
169
for name in builtins:
170
if name.startswith("cmd_"):
171
real_name = _unsquish_command_name(name)
172
r[real_name] = builtins[name]
177
def builtin_command_names():
178
"""Return list of builtin command names."""
179
return _builtin_commands().keys()
182
def plugin_command_names():
183
return plugin_cmds.keys()
186
def _get_cmd_dict(plugins_override=True):
187
"""Return name->class mapping for all commands."""
188
d = _builtin_commands()
190
d.update(plugin_cmds)
194
def get_all_cmds(plugins_override=True):
195
"""Return canonical name and class for all registered commands."""
196
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
200
def get_cmd_object(cmd_name, plugins_override=True):
201
"""Return the canonical name and command class for a command.
204
If true, plugin commands can override builtins.
206
from bzrlib.externalcommand import ExternalCommand
208
cmd_name = str(cmd_name) # not unicode
210
# first look up this command under the specified name
211
cmds = _get_cmd_dict(plugins_override=plugins_override)
213
return cmds[cmd_name]()
217
# look for any command which claims this as an alias
218
for real_cmd_name, cmd_class in cmds.iteritems():
219
if cmd_name in cmd_class.aliases:
222
cmd_obj = ExternalCommand.find_command(cmd_name)
226
raise BzrCommandError("unknown command %r" % cmd_name)
229
class Command(object):
230
"""Base class for commands.
232
Commands are the heart of the command-line bzr interface.
234
The command object mostly handles the mapping of command-line
235
parameters into one or more bzrlib operations, and of the results
238
Commands normally don't have any state. All their arguments are
239
passed in to the run method. (Subclasses may take a different
240
policy if the behaviour of the instance needs to depend on e.g. a
241
shell plugin and not just its Python class.)
243
The docstring for an actual command should give a single-line
244
summary, then a complete description of the command. A grammar
245
description will be inserted.
248
Other accepted names for this command.
251
List of argument forms, marked with whether they are optional,
255
List of options that may be given for this command.
258
If true, this command isn't advertised. This is typically
259
for commands intended for expert users.
269
"""Construct an instance of this command."""
270
if self.__doc__ == Command.__doc__:
271
warn("No help message set for %r" % self)
274
def run_argv(self, argv):
275
"""Parse command line and run."""
276
args, opts = parse_args(argv)
278
if 'help' in opts: # e.g. bzr add --help
279
from bzrlib.help import help_on_command
280
help_on_command(self.name())
283
# check options are reasonable
284
allowed = self.takes_options
286
if oname not in allowed:
287
raise BzrCommandError("option '--%s' is not allowed for command %r"
288
% (oname, self.name()))
290
# mix arguments and options into one dictionary
291
cmdargs = _match_argform(self.name(), self.takes_args, args)
293
for k, v in opts.items():
294
cmdopts[k.replace('-', '_')] = v
296
all_cmd_args = cmdargs.copy()
297
all_cmd_args.update(cmdopts)
299
return self.run(**all_cmd_args)
303
"""Actually run the command.
305
This is invoked with the options and arguments bound to
308
Return 0 or None if the command was successful, or a non-zero
309
shell error code if not. It's OK for this method to allow
310
an exception to raise up.
312
raise NotImplementedError()
316
"""Return help message for this class."""
317
if self.__doc__ is Command.__doc__:
322
return _unsquish_command_name(self.__class__.__name__)
325
def parse_spec(spec):
331
>>> parse_spec("../@")
333
>>> parse_spec("../f/@35")
335
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
336
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
341
parsed = spec.split('/@')
342
assert len(parsed) == 2
347
parsed[1] = int(parsed[1])
349
pass # We can allow stuff like ./@revid:blahblahblah
353
parsed = [spec, None]
359
# list of all available options; the rhs can be either None for an
360
# option that takes no argument, or a constructor function that checks
373
'revision': _parse_revision_str,
385
'merge-type': get_merge_type,
399
def parse_args(argv):
400
"""Parse command line.
402
Arguments and options are parsed at this level before being passed
403
down to specific command handlers. This routine knows, from a
404
lookup table, something about the available options, what optargs
405
they take, and which commands will accept them.
407
>>> parse_args('--help'.split())
409
>>> parse_args('help -- --invalidcmd'.split())
410
(['help', '--invalidcmd'], {})
411
>>> parse_args('--version'.split())
412
([], {'version': True})
413
>>> parse_args('status --all'.split())
414
(['status'], {'all': True})
415
>>> parse_args('commit --message=biter'.split())
416
(['commit'], {'message': u'biter'})
417
>>> parse_args('log -r 500'.split())
418
(['log'], {'revision': [<RevisionSpec_int 500>]})
419
>>> parse_args('log -r500..600'.split())
420
(['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
421
>>> parse_args('log -vr500..600'.split())
422
(['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
423
>>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
424
(['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
432
if not argsover and a[0] == '-':
433
# option names must not be unicode
438
# We've received a standalone -- No more flags
441
mutter(" got option %r" % a)
443
optname, optarg = a[2:].split('=', 1)
446
if optname not in OPTIONS:
447
raise BzrError('unknown long option %r' % a)
450
if shortopt in SHORT_OPTIONS:
451
# Multi-character options must have a space to delimit
453
optname = SHORT_OPTIONS[shortopt]
455
# Single character short options, can be chained,
456
# and have their value appended to their name
458
if shortopt not in SHORT_OPTIONS:
459
# We didn't find the multi-character name, and we
460
# didn't find the single char name
461
raise BzrError('unknown short option %r' % a)
462
optname = SHORT_OPTIONS[shortopt]
465
# There are extra things on this option
466
# see if it is the value, or if it is another
468
optargfn = OPTIONS[optname]
470
# This option does not take an argument, so the
471
# next entry is another short option, pack it back
473
argv.insert(0, '-' + a[2:])
475
# This option takes an argument, so pack it
480
# XXX: Do we ever want to support this, e.g. for -r?
481
raise BzrError('repeated option %r' % a)
483
optargfn = OPTIONS[optname]
487
raise BzrError('option %r needs an argument' % a)
490
opts[optname] = optargfn(optarg)
493
raise BzrError('option %r takes no argument' % optname)
503
def _match_argform(cmd, takes_args, args):
506
# step through args and takes_args, allowing appropriate 0-many matches
507
for ap in takes_args:
511
argdict[argname] = args.pop(0)
512
elif ap[-1] == '*': # all remaining arguments
514
argdict[argname + '_list'] = args[:]
517
argdict[argname + '_list'] = None
520
raise BzrCommandError("command %r needs one or more %s"
521
% (cmd, argname.upper()))
523
argdict[argname + '_list'] = args[:]
525
elif ap[-1] == '$': # all but one
527
raise BzrCommandError("command %r needs one or more %s"
528
% (cmd, argname.upper()))
529
argdict[argname + '_list'] = args[:-1]
535
raise BzrCommandError("command %r requires argument %s"
536
% (cmd, argname.upper()))
538
argdict[argname] = args.pop(0)
541
raise BzrCommandError("extra argument to command %s: %s"
548
def apply_profiled(the_callable, *args, **kwargs):
551
pffileno, pfname = tempfile.mkstemp()
553
prof = hotshot.Profile(pfname)
555
ret = prof.runcall(the_callable, *args, **kwargs) or 0
560
stats = hotshot.stats.load(pfname)
562
stats.sort_stats('time')
563
## XXX: Might like to write to stderr or the trace file instead but
564
## print_stats seems hardcoded to stdout
565
stats.print_stats(20)
574
"""Execute a command.
576
This is similar to main(), but without all the trappings for
577
logging and error handling.
580
The command-line arguments, without the program name from argv[0]
582
Returns a command status or raises an exception.
584
Special master options: these must come before the command because
585
they control how the command is interpreted.
588
Do not load plugin modules at all
591
Only use builtin commands. (Plugins are still allowed to change
595
Run under the Python profiler.
598
argv = [a.decode(bzrlib.user_encoding) for a in argv]
600
opt_profile = opt_no_plugins = opt_builtin = False
602
# --no-plugins is handled specially at a very early stage. We need
603
# to load plugins before doing other command parsing so that they
604
# can override commands, but this needs to happen first.
609
elif a == '--no-plugins':
610
opt_no_plugins = True
611
elif a == '--builtin':
617
if (not argv) or (argv[0] == '--help'):
618
from bzrlib.help import help
625
if argv[0] == '--version':
626
from bzrlib.builtins import show_version
630
if not opt_no_plugins:
631
from bzrlib.plugin import load_plugins
634
cmd = str(argv.pop(0))
636
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
639
ret = apply_profiled(cmd_obj.run_argv, argv)
641
ret = cmd_obj.run_argv(argv)
647
bzrlib.trace.log_startup(argv)
648
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
652
return run_bzr(argv[1:])
654
# do this here inside the exception wrappers to catch EPIPE
656
except BzrCommandError, e:
657
# command line syntax error, etc
661
bzrlib.trace.log_exception()
663
except AssertionError, e:
664
bzrlib.trace.log_exception('assertion failed: ' + str(e))
666
except KeyboardInterrupt, e:
667
bzrlib.trace.note('interrupted')
671
if (isinstance(e, IOError)
672
and hasattr(e, 'errno')
673
and e.errno == errno.EPIPE):
674
bzrlib.trace.note('broken pipe')
677
bzrlib.trace.log_exception()
681
if __name__ == '__main__':
682
sys.exit(main(sys.argv))