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.branch import find_branch
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
74
It supports integers directly, but everything else it
75
defers for passing to Branch.get_revision_info()
77
>>> _parse_revision_str('234')
79
>>> _parse_revision_str('234..567')
81
>>> _parse_revision_str('..')
83
>>> _parse_revision_str('..234')
85
>>> _parse_revision_str('234..')
87
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
89
>>> _parse_revision_str('234....789') # Error?
91
>>> _parse_revision_str('revid:test@other.com-234234')
92
['revid:test@other.com-234234']
93
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
94
['revid:test@other.com-234234', 'revid:test@other.com-234235']
95
>>> _parse_revision_str('revid:test@other.com-234234..23')
96
['revid:test@other.com-234234', 23]
97
>>> _parse_revision_str('date:2005-04-12')
99
>>> _parse_revision_str('date:2005-04-12 12:24:33')
100
['date:2005-04-12 12:24:33']
101
>>> _parse_revision_str('date:2005-04-12T12:24:33')
102
['date:2005-04-12T12:24:33']
103
>>> _parse_revision_str('date:2005-04-12,12:24:33')
104
['date:2005-04-12,12:24:33']
105
>>> _parse_revision_str('-5..23')
107
>>> _parse_revision_str('-5')
109
>>> _parse_revision_str('123a')
111
>>> _parse_revision_str('abc')
115
old_format_re = re.compile('\d*:\d*')
116
m = old_format_re.match(revstr)
118
warning('Colon separator for revision numbers is deprecated.'
121
for rev in revstr.split(':'):
123
revs.append(int(rev))
128
for x in revstr.split('..'):
139
def get_merge_type(typestring):
140
"""Attempt to find the merge class/factory associated with a string."""
141
from merge import merge_types
143
return merge_types[typestring][0]
145
templ = '%s%%7s: %%s' % (' '*12)
146
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
147
type_list = '\n'.join(lines)
148
msg = "No known merge type %s. Supported types are:\n%s" %\
149
(typestring, type_list)
150
raise BzrCommandError(msg)
153
def get_merge_type(typestring):
154
"""Attempt to find the merge class/factory associated with a string."""
155
from merge import merge_types
157
return merge_types[typestring][0]
159
templ = '%s%%7s: %%s' % (' '*12)
160
lines = [templ % (f[0], f[1][1]) for f in merge_types.iteritems()]
161
type_list = '\n'.join(lines)
162
msg = "No known merge type %s. Supported types are:\n%s" %\
163
(typestring, type_list)
164
raise BzrCommandError(msg)
167
def _builtin_commands():
168
import bzrlib.builtins
170
builtins = bzrlib.builtins.__dict__
171
for name in builtins:
172
if name.startswith("cmd_"):
173
real_name = _unsquish_command_name(name)
174
r[real_name] = builtins[name]
179
def builtin_command_names():
180
"""Return list of builtin command names."""
181
return _builtin_commands().keys()
184
def plugin_command_names():
185
return plugin_cmds.keys()
188
def _get_cmd_dict(plugins_override=True):
189
"""Return name->class mapping for all commands."""
190
d = _builtin_commands()
192
d.update(plugin_cmds)
196
def get_all_cmds(plugins_override=True):
197
"""Return canonical name and class for all registered commands."""
198
for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
202
def get_cmd_object(cmd_name, plugins_override=True):
203
"""Return the canonical name and command class for a command.
206
If true, plugin commands can override builtins.
208
from bzrlib.externalcommand import ExternalCommand
210
cmd_name = str(cmd_name) # not unicode
212
# first look up this command under the specified name
213
cmds = _get_cmd_dict(plugins_override=plugins_override)
215
return cmds[cmd_name]()
219
# look for any command which claims this as an alias
220
for real_cmd_name, cmd_class in cmds.iteritems():
221
if cmd_name in cmd_class.aliases:
224
cmd_obj = ExternalCommand.find_command(cmd_name)
228
raise BzrCommandError("unknown command %r" % cmd_name)
231
class Command(object):
232
"""Base class for commands.
234
Commands are the heart of the command-line bzr interface.
236
The command object mostly handles the mapping of command-line
237
parameters into one or more bzrlib operations, and of the results
240
Commands normally don't have any state. All their arguments are
241
passed in to the run method. (Subclasses may take a different
242
policy if the behaviour of the instance needs to depend on e.g. a
243
shell plugin and not just its Python class.)
245
The docstring for an actual command should give a single-line
246
summary, then a complete description of the command. A grammar
247
description will be inserted.
250
Other accepted names for this command.
253
List of argument forms, marked with whether they are optional,
257
List of options that may be given for this command.
260
If true, this command isn't advertised. This is typically
261
for commands intended for expert users.
271
"""Construct an instance of this command."""
272
if self.__doc__ == Command.__doc__:
273
warn("No help message set for %r" % self)
277
"""Actually run the command.
279
This is invoked with the options and arguments bound to
282
Return 0 or None if the command was successful, or a non-zero
283
shell error code if not. It's OK for this method to allow
284
an exception to raise up.
286
raise NotImplementedError()
290
"""Return help message for this class."""
291
if self.__doc__ is Command.__doc__:
296
return _unsquish_command_name(self.__class__.__name__)
299
def parse_spec(spec):
305
>>> parse_spec("../@")
307
>>> parse_spec("../f/@35")
309
>>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
310
['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
315
parsed = spec.split('/@')
316
assert len(parsed) == 2
321
parsed[1] = int(parsed[1])
323
pass # We can allow stuff like ./@revid:blahblahblah
327
parsed = [spec, None]
333
# list of all available options; the rhs can be either None for an
334
# option that takes no argument, or a constructor function that checks
347
'revision': _parse_revision_str,
359
'merge-type': get_merge_type,
373
def parse_args(argv):
374
"""Parse command line.
376
Arguments and options are parsed at this level before being passed
377
down to specific command handlers. This routine knows, from a
378
lookup table, something about the available options, what optargs
379
they take, and which commands will accept them.
381
>>> parse_args('--help'.split())
383
>>> parse_args('help -- --invalidcmd'.split())
384
(['help', '--invalidcmd'], {})
385
>>> parse_args('--version'.split())
386
([], {'version': True})
387
>>> parse_args('status --all'.split())
388
(['status'], {'all': True})
389
>>> parse_args('commit --message=biter'.split())
390
(['commit'], {'message': u'biter'})
391
>>> parse_args('log -r 500'.split())
392
(['log'], {'revision': [500]})
393
>>> parse_args('log -r500..600'.split())
394
(['log'], {'revision': [500, 600]})
395
>>> parse_args('log -vr500..600'.split())
396
(['log'], {'verbose': True, 'revision': [500, 600]})
397
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
398
(['log'], {'revision': ['v500', 600]})
406
if not argsover and a[0] == '-':
407
# option names must not be unicode
412
# We've received a standalone -- No more flags
415
mutter(" got option %r" % a)
417
optname, optarg = a[2:].split('=', 1)
420
if optname not in OPTIONS:
421
raise BzrError('unknown long option %r' % a)
424
if shortopt in SHORT_OPTIONS:
425
# Multi-character options must have a space to delimit
427
optname = SHORT_OPTIONS[shortopt]
429
# Single character short options, can be chained,
430
# and have their value appended to their name
432
if shortopt not in SHORT_OPTIONS:
433
# We didn't find the multi-character name, and we
434
# didn't find the single char name
435
raise BzrError('unknown short option %r' % a)
436
optname = SHORT_OPTIONS[shortopt]
439
# There are extra things on this option
440
# see if it is the value, or if it is another
442
optargfn = OPTIONS[optname]
444
# This option does not take an argument, so the
445
# next entry is another short option, pack it back
447
argv.insert(0, '-' + a[2:])
449
# This option takes an argument, so pack it
454
# XXX: Do we ever want to support this, e.g. for -r?
455
raise BzrError('repeated option %r' % a)
457
optargfn = OPTIONS[optname]
461
raise BzrError('option %r needs an argument' % a)
464
opts[optname] = optargfn(optarg)
467
raise BzrError('option %r takes no argument' % optname)
477
def _match_argform(cmd, takes_args, args):
480
# step through args and takes_args, allowing appropriate 0-many matches
481
for ap in takes_args:
485
argdict[argname] = args.pop(0)
486
elif ap[-1] == '*': # all remaining arguments
488
argdict[argname + '_list'] = args[:]
491
argdict[argname + '_list'] = None
494
raise BzrCommandError("command %r needs one or more %s"
495
% (cmd, argname.upper()))
497
argdict[argname + '_list'] = args[:]
499
elif ap[-1] == '$': # all but one
501
raise BzrCommandError("command %r needs one or more %s"
502
% (cmd, argname.upper()))
503
argdict[argname + '_list'] = args[:-1]
509
raise BzrCommandError("command %r requires argument %s"
510
% (cmd, argname.upper()))
512
argdict[argname] = args.pop(0)
515
raise BzrCommandError("extra argument to command %s: %s"
522
def apply_profiled(the_callable, *args, **kwargs):
525
pffileno, pfname = tempfile.mkstemp()
527
prof = hotshot.Profile(pfname)
529
ret = prof.runcall(the_callable, *args, **kwargs) or 0
534
stats = hotshot.stats.load(pfname)
536
stats.sort_stats('time')
537
## XXX: Might like to write to stderr or the trace file instead but
538
## print_stats seems hardcoded to stdout
539
stats.print_stats(20)
548
"""Execute a command.
550
This is similar to main(), but without all the trappings for
551
logging and error handling.
554
The command-line arguments, without the program name from argv[0]
556
Returns a command status or raises an exception.
558
Special master options: these must come before the command because
559
they control how the command is interpreted.
562
Do not load plugin modules at all
565
Only use builtin commands. (Plugins are still allowed to change
569
Run under the Python profiler.
572
argv = [a.decode(bzrlib.user_encoding) for a in argv]
574
opt_profile = opt_no_plugins = opt_builtin = False
576
# --no-plugins is handled specially at a very early stage. We need
577
# to load plugins before doing other command parsing so that they
578
# can override commands, but this needs to happen first.
583
elif a == '--no-plugins':
584
opt_no_plugins = True
585
elif a == '--builtin':
591
if not opt_no_plugins:
592
from bzrlib.plugin import load_plugins
595
args, opts = parse_args(argv)
598
from bzrlib.help import help
605
if 'version' in opts:
606
from bzrlib.builtins import show_version
611
from bzrlib.help import help
615
cmd = str(args.pop(0))
617
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
619
# check options are reasonable
620
allowed = cmd_obj.takes_options
622
if oname not in allowed:
623
raise BzrCommandError("option '--%s' is not allowed for command %r"
626
# mix arguments and options into one dictionary
627
cmdargs = _match_argform(cmd, cmd_obj.takes_args, args)
629
for k, v in opts.items():
630
cmdopts[k.replace('-', '_')] = v
632
all_cmd_args = cmdargs.copy()
633
all_cmd_args.update(cmdopts)
636
ret = apply_profiled(cmd_obj.run, **all_cmd_args)
638
ret = cmd_obj.run(**all_cmd_args)
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))