1
# Copyright (C) 2006 Canonical Ltd
1
# Copyright (C) 2004, 2005 by Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
21
# TODO: Help messages for options.
21
23
# TODO: Define arguments by objects, rather than just using names.
22
24
# 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.
25
# would help with validation and shell completion.
28
27
# TODO: "--profile=cum", to change sort order. Is there any value in leaving
29
28
# the profile output behind so it can be interactively examined?
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
38
32
from warnings import warn
33
from inspect import getdoc
51
from bzrlib.symbol_versioning import (
58
from bzrlib.option import Option
37
from bzrlib.trace import mutter, note, log_error, warning
38
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
39
from bzrlib.revisionspec import RevisionSpec
40
from bzrlib import BZRDIR
64
def register_command(cmd, decorate=False):
65
"""Utility function to help register a command
67
:param cmd: Command subclass to register
68
:param decorate: If true, allow overriding an existing command
69
of the same name; the old command is returned by this function.
70
Otherwise it is an error to try to override an existing command.
45
def register_command(cmd):
46
"Utility function to help register a command"
74
49
if k.startswith("cmd_"):
75
50
k_unsquished = _unsquish_command_name(k)
78
if k_unsquished not in plugin_cmds:
79
plugin_cmds[k_unsquished] = cmd
80
## trace.mutter('registered plugin command %s', k_unsquished)
81
if decorate and k_unsquished in builtin_command_names():
82
return _builtin_commands()[k_unsquished]
84
result = plugin_cmds[k_unsquished]
85
plugin_cmds[k_unsquished] = cmd
53
if not plugin_cmds.has_key(k_unsquished):
54
plugin_cmds[k_unsquished] = cmd
55
mutter('registered plugin command %s', k_unsquished)
88
trace.log_error('Two plugins defined the same command: %r' % k)
89
trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
57
log_error('Two plugins defined the same command: %r' % k)
58
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
92
61
def _squish_command_name(cmd):
98
67
return cmd[4:].replace('_','-')
70
def _parse_revision_str(revstr):
71
"""This handles a revision string -> revno.
73
This always returns a list. The list will have one element for
74
each revision specifier supplied.
76
>>> _parse_revision_str('234')
77
[<RevisionSpec_int 234>]
78
>>> _parse_revision_str('234..567')
79
[<RevisionSpec_int 234>, <RevisionSpec_int 567>]
80
>>> _parse_revision_str('..')
81
[<RevisionSpec None>, <RevisionSpec None>]
82
>>> _parse_revision_str('..234')
83
[<RevisionSpec None>, <RevisionSpec_int 234>]
84
>>> _parse_revision_str('234..')
85
[<RevisionSpec_int 234>, <RevisionSpec None>]
86
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
[<RevisionSpec_int 234>, <RevisionSpec_int 456>, <RevisionSpec_int 789>]
88
>>> _parse_revision_str('234....789') #Error ?
89
[<RevisionSpec_int 234>, <RevisionSpec None>, <RevisionSpec_int 789>]
90
>>> _parse_revision_str('revid:test@other.com-234234')
91
[<RevisionSpec_revid revid:test@other.com-234234>]
92
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
93
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_revid revid:test@other.com-234235>]
94
>>> _parse_revision_str('revid:test@other.com-234234..23')
95
[<RevisionSpec_revid revid:test@other.com-234234>, <RevisionSpec_int 23>]
96
>>> _parse_revision_str('date:2005-04-12')
97
[<RevisionSpec_date date:2005-04-12>]
98
>>> _parse_revision_str('date:2005-04-12 12:24:33')
99
[<RevisionSpec_date date:2005-04-12 12:24:33>]
100
>>> _parse_revision_str('date:2005-04-12T12:24:33')
101
[<RevisionSpec_date date:2005-04-12T12:24:33>]
102
>>> _parse_revision_str('date:2005-04-12,12:24:33')
103
[<RevisionSpec_date date:2005-04-12,12:24:33>]
104
>>> _parse_revision_str('-5..23')
105
[<RevisionSpec_int -5>, <RevisionSpec_int 23>]
106
>>> _parse_revision_str('-5')
107
[<RevisionSpec_int -5>]
108
>>> _parse_revision_str('123a')
109
Traceback (most recent call last):
111
BzrError: No namespace registered for string: '123a'
112
>>> _parse_revision_str('abc')
113
Traceback (most recent call last):
115
BzrError: No namespace registered for string: 'abc'
116
>>> _parse_revision_str('branch:../branch2')
117
[<RevisionSpec_branch branch:../branch2>]
120
old_format_re = re.compile('\d*:\d*')
121
m = old_format_re.match(revstr)
124
warning('Colon separator for revision numbers is deprecated.'
126
for rev in revstr.split(':'):
128
revs.append(RevisionSpec(int(rev)))
130
revs.append(RevisionSpec(None))
133
for x in revstr.split('..'):
135
revs.append(RevisionSpec(None))
137
# looks like a namespace:.. has happened
138
next_prefix = x + '..'
140
if next_prefix is not None:
142
revs.append(RevisionSpec(x))
144
if next_prefix is not None:
145
revs.append(RevisionSpec(next_prefix))
101
149
def _builtin_commands():
102
150
import bzrlib.builtins
104
152
builtins = bzrlib.builtins.__dict__
105
153
for name in builtins:
106
154
if name.startswith("cmd_"):
107
real_name = _unsquish_command_name(name)
155
real_name = _unsquish_command_name(name)
108
156
r[real_name] = builtins[name]
112
161
def builtin_command_names():
196
235
List of argument forms, marked with whether they are optional,
201
['to_location', 'from_branch?', 'file*']
203
'to_location' is required
204
'from_branch' is optional
205
'file' can be specified 0 or more times
208
List of options that may be given for this command. These can
209
be either strings, referring to globally-defined options,
210
or option objects. Retrieve through options().
239
List of options that may be given for this command.
213
242
If true, this command isn't advertised. This is typically
214
243
for commands intended for expert users.
217
Command objects will get a 'outf' attribute, which has been
218
setup to properly handle encoding of unicode strings.
219
encoding_type determines what will happen when characters cannot
221
strict - abort if we cannot decode
222
replace - put in a bogus character (typically '?')
223
exact - do not encode sys.stdout
225
NOTE: by default on Windows, sys.stdout is opened as a text
226
stream, therefore LF line-endings are converted to CRLF.
227
When a command uses encoding_type = 'exact', then
228
sys.stdout is forced to be a binary stream, and line-endings
234
248
takes_options = []
235
encoding_type = 'strict'
241
254
if self.__doc__ == Command.__doc__:
242
255
warn("No help message set for %r" % self)
244
def _maybe_expand_globs(self, file_list):
245
"""Glob expand file_list if the platform does not do that itself.
247
:return: A possibly empty list of unicode paths.
249
Introduced in bzrlib 0.18.
253
if sys.platform == 'win32':
254
file_list = win32utils.glob_expand(file_list)
255
return list(file_list)
258
"""Return single-line grammar for this command.
260
Only describes arguments, not options.
262
s = 'bzr ' + self.name() + ' '
263
for aname in self.takes_args:
264
aname = aname.upper()
265
if aname[-1] in ['$', '+']:
266
aname = aname[:-1] + '...'
267
elif aname[-1] == '?':
268
aname = '[' + aname[:-1] + ']'
269
elif aname[-1] == '*':
270
aname = '[' + aname[:-1] + '...]'
277
def get_help_text(self, additional_see_also=None):
278
"""Return a text string with help for this command.
280
:param additional_see_also: Additional help topics to be
285
raise NotImplementedError("sorry, no detailed help yet for %r" % self.name())
288
result += 'usage: %s\n' % self._usage()
291
result += 'aliases: '
292
result += ', '.join(self.aliases) + '\n'
296
plugin_name = self.plugin_name()
297
if plugin_name is not None:
298
result += '(From plugin "%s")' % plugin_name
302
if result[-1] != '\n':
305
result += option.get_optparser(self.options()).format_option_help()
306
see_also = self.get_see_also(additional_see_also)
308
result += '\nSee also: '
309
result += ', '.join(see_also)
313
def get_help_topic(self):
314
"""Return the commands help topic - its name."""
317
def get_see_also(self, additional_terms=None):
318
"""Return a list of help topics that are related to this ommand.
320
The list is derived from the content of the _see_also attribute. Any
321
duplicates are removed and the result is in lexical order.
322
:param additional_terms: Additional help topics to cross-reference.
323
:return: A list of help topics.
325
see_also = set(getattr(self, '_see_also', []))
327
see_also.update(additional_terms)
328
return sorted(see_also)
331
"""Return dict of valid options for this command.
333
Maps from long option name to option object."""
335
r['help'] = option._help_option
336
for o in self.takes_options:
337
if isinstance(o, basestring):
338
o = option.Option.OPTIONS[o]
342
def _setup_outf(self):
343
"""Return a file linked to stdout, which has proper encoding."""
344
assert self.encoding_type in ['strict', 'exact', 'replace']
346
# Originally I was using self.stdout, but that looks
347
# *way* too much like sys.stdout
348
if self.encoding_type == 'exact':
349
# force sys.stdout to be binary stream on win32
350
if sys.platform == 'win32':
351
fileno = getattr(sys.stdout, 'fileno', None)
354
msvcrt.setmode(fileno(), os.O_BINARY)
355
self.outf = sys.stdout
358
output_encoding = osutils.get_terminal_encoding()
360
# use 'replace' so that we don't abort if trying to write out
361
# in e.g. the default C locale.
362
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
363
# For whatever reason codecs.getwriter() does not advertise its encoding
364
# it just returns the encoding of the wrapped file, which is completely
365
# bogus. So set the attribute, so we can find the correct encoding later.
366
self.outf.encoding = output_encoding
368
def run_argv_aliases(self, argv, alias_argv=None):
369
"""Parse the command line and run with extra aliases in alias_argv."""
371
warn("Passing None for [] is deprecated from bzrlib 0.10",
372
DeprecationWarning, stacklevel=2)
374
args, opts = parse_args(self, argv, alias_argv)
258
def run_argv(self, argv):
259
"""Parse command line and run."""
260
args, opts = parse_args(argv)
375
262
if 'help' in opts: # e.g. bzr add --help
376
sys.stdout.write(self.get_help_text())
263
from bzrlib.help import help_on_command
264
help_on_command(self.name())
267
# check options are reasonable
268
allowed = self.takes_options
270
if oname not in allowed:
271
raise BzrCommandError("option '--%s' is not allowed for command %r"
272
% (oname, self.name()))
378
274
# mix arguments and options into one dictionary
379
275
cmdargs = _match_argform(self.name(), self.takes_args, args)
457
337
parsed = [spec, None]
460
def parse_args(command, argv, alias_argv=None):
341
# list of all available options; the rhs can be either None for an
342
# option that takes no argument, or a constructor function that checks
358
'revision': _parse_revision_str,
385
def parse_args(argv):
461
386
"""Parse command line.
463
388
Arguments and options are parsed at this level before being passed
464
389
down to specific command handlers. This routine knows, from a
465
390
lookup table, something about the available options, what optargs
466
391
they take, and which commands will accept them.
393
>>> parse_args('--help'.split())
395
>>> parse_args('help -- --invalidcmd'.split())
396
(['help', '--invalidcmd'], {})
397
>>> parse_args('--version'.split())
398
([], {'version': True})
399
>>> parse_args('status --all'.split())
400
(['status'], {'all': True})
401
>>> parse_args('commit --message=biter'.split())
402
(['commit'], {'message': u'biter'})
403
>>> parse_args('log -r 500'.split())
404
(['log'], {'revision': [<RevisionSpec_int 500>]})
405
>>> parse_args('log -r500..600'.split())
406
(['log'], {'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
407
>>> parse_args('log -vr500..600'.split())
408
(['log'], {'verbose': True, 'revision': [<RevisionSpec_int 500>, <RevisionSpec_int 600>]})
409
>>> parse_args('log -rrevno:500..600'.split()) #the r takes an argument
410
(['log'], {'revision': [<RevisionSpec_revno revno:500>, <RevisionSpec_int 600>]})
468
# TODO: make it a method of the Command?
469
parser = option.get_optparser(command.options())
470
if alias_argv is not None:
471
args = alias_argv + argv
475
options, args = parser.parse_args(args)
476
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
477
v is not option.OptionParser.DEFAULT_VALUE])
418
if not argsover and a[0] == '-':
419
# option names must not be unicode
424
# We've received a standalone -- No more flags
427
mutter(" got option %r" % a)
429
optname, optarg = a[2:].split('=', 1)
432
if optname not in OPTIONS:
433
raise BzrError('unknown long option %r' % a)
436
if shortopt in SHORT_OPTIONS:
437
# Multi-character options must have a space to delimit
439
optname = SHORT_OPTIONS[shortopt]
441
# Single character short options, can be chained,
442
# and have their value appended to their name
444
if shortopt not in SHORT_OPTIONS:
445
# We didn't find the multi-character name, and we
446
# didn't find the single char name
447
raise BzrError('unknown short option %r' % a)
448
optname = SHORT_OPTIONS[shortopt]
451
# There are extra things on this option
452
# see if it is the value, or if it is another
454
optargfn = OPTIONS[optname]
456
# This option does not take an argument, so the
457
# next entry is another short option, pack it back
459
argv.insert(0, '-' + a[2:])
461
# This option takes an argument, so pack it
466
# XXX: Do we ever want to support this, e.g. for -r?
467
raise BzrError('repeated option %r' % a)
469
optargfn = OPTIONS[optname]
473
raise BzrError('option %r needs an argument' % a)
476
opts[optname] = optargfn(optarg)
479
raise BzrError('option %r takes no argument' % optname)
478
484
return args, opts
481
489
def _match_argform(cmd, takes_args, args):
495
503
argdict[argname + '_list'] = None
496
504
elif ap[-1] == '+':
498
raise errors.BzrCommandError("command %r needs one or more %s"
499
% (cmd, argname.upper()))
506
raise BzrCommandError("command %r needs one or more %s"
507
% (cmd, argname.upper()))
501
509
argdict[argname + '_list'] = args[:]
503
511
elif ap[-1] == '$': # all but one
504
512
if len(args) < 2:
505
raise errors.BzrCommandError("command %r needs one or more %s"
506
% (cmd, argname.upper()))
513
raise BzrCommandError("command %r needs one or more %s"
514
% (cmd, argname.upper()))
507
515
argdict[argname + '_list'] = args[:-1]
510
518
# just a plain arg
513
raise errors.BzrCommandError("command %r requires argument %s"
514
% (cmd, argname.upper()))
521
raise BzrCommandError("command %r requires argument %s"
522
% (cmd, argname.upper()))
516
524
argdict[argname] = args.pop(0)
519
raise errors.BzrCommandError("extra argument to command %s: %s"
527
raise BzrCommandError("extra argument to command %s: %s"
546
554
os.remove(pfname)
549
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
550
from bzrlib.lsprof import profile
551
ret, stats = profile(the_callable, *args, **kwargs)
557
trace.note('Profile data written to "%s".', filename)
561
def get_alias(cmd, config=None):
562
"""Return an expanded alias, or None if no alias exists.
565
Command to be checked for an alias.
567
Used to specify an alternative config to use,
568
which is especially useful for testing.
569
If it is unspecified, the global config will be used.
573
config = bzrlib.config.GlobalConfig()
574
alias = config.get_alias(cmd)
577
return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
581
557
def run_bzr(argv):
582
558
"""Execute a command.
598
572
Do not load plugin modules at all
604
575
Only use builtin commands. (Plugins are still allowed to change
605
576
other behaviour.)
608
Run under the Python hotshot profiler.
611
Run under the Python lsprof profiler.
579
Run under the Python profiler.
614
trace.mutter("bzr arguments: %r", argv)
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]
616
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
617
opt_no_aliases = False
618
opt_lsprof_file = None
586
opt_profile = opt_no_plugins = opt_builtin = False
620
588
# --no-plugins is handled specially at a very early stage. We need
621
589
# to load plugins before doing other command parsing so that they
622
590
# can override commands, but this needs to happen first.
628
593
if a == '--profile':
629
594
opt_profile = True
630
elif a == '--lsprof':
632
elif a == '--lsprof-file':
634
opt_lsprof_file = argv[i + 1]
636
595
elif a == '--no-plugins':
637
596
opt_no_plugins = True
638
elif a == '--no-aliases':
639
opt_no_aliases = True
640
597
elif a == '--builtin':
641
598
opt_builtin = True
642
elif a in ('--quiet', '-q'):
644
elif a.startswith('-D'):
645
debug.debug_flags.add(a[2:])
652
from bzrlib.builtins import cmd_help
653
cmd_help().run_argv_aliases([])
603
if (not argv) or (argv[0] == '--help'):
604
from bzrlib.help import help
656
611
if argv[0] == '--version':
657
from bzrlib.version import show_version
612
from bzrlib.builtins import show_version
661
616
if not opt_no_plugins:
662
617
from bzrlib.plugin import load_plugins
665
from bzrlib.plugin import disable_plugins
670
if not opt_no_aliases:
671
alias_argv = get_alias(argv[0])
673
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
674
argv[0] = alias_argv.pop(0)
677
# We want only 'ascii' command names, but the user may have typed
678
# in a Unicode name. In that case, they should just get a
679
# 'command not found' error later.
620
cmd = str(argv.pop(0))
681
622
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
682
run = cmd_obj.run_argv_aliases
683
run_argv = [argv, alias_argv]
687
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
689
ret = apply_profiled(run, *run_argv)
694
# reset, in case we may do other commands later within the same process
695
trace.be_quiet(False)
697
def display_command(func):
698
"""Decorator that suppresses pipe/interrupt errors."""
699
def ignore_pipe(*args, **kwargs):
701
result = func(*args, **kwargs)
705
if getattr(e, 'errno', None) is None:
707
if e.errno != errno.EPIPE:
708
# Win32 raises IOError with errno=0 on a broken pipe
709
if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
712
except KeyboardInterrupt:
625
ret = apply_profiled(cmd_obj.run_argv, argv)
627
ret = cmd_obj.run_argv(argv)
719
from bzrlib.ui.text import TextUIFactory
720
bzrlib.ui.ui_factory = TextUIFactory()
721
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
722
ret = run_bzr_catch_errors(argv)
723
trace.mutter("return code %d", ret)
633
bzrlib.trace.log_startup(argv)
634
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
636
return run_bzr_catch_errors(argv[1:])
727
639
def run_bzr_catch_errors(argv):
730
except (KeyboardInterrupt, Exception), e:
731
# used to handle AssertionError and KeyboardInterrupt
732
# specially here, but hopefully they're handled ok by the logger now
733
trace.report_exception(sys.exc_info(), sys.stderr)
734
if os.environ.get('BZR_PDB'):
735
print '**** entering debugger'
737
pdb.post_mortem(sys.exc_traceback)
644
# do this here inside the exception wrappers to catch EPIPE
646
except BzrCommandError, e:
647
# command line syntax error, etc
651
bzrlib.trace.log_exception()
653
except AssertionError, e:
654
bzrlib.trace.log_exception('assertion failed: ' + str(e))
741
class HelpCommandIndex(object):
742
"""A index for bzr help that returns commands."""
745
self.prefix = 'commands/'
747
def get_topics(self, topic):
748
"""Search for topic amongst commands.
750
:param topic: A topic to search for.
751
:return: A list which is either empty or contains a single
754
if topic and topic.startswith(self.prefix):
755
topic = topic[len(self.prefix):]
757
cmd = _get_cmd_object(topic)
656
except KeyboardInterrupt, e:
657
bzrlib.trace.log_exception('interrupted')
661
if (isinstance(e, IOError)
662
and hasattr(e, 'errno')
663
and e.errno == errno.EPIPE):
664
bzrlib.trace.note('broken pipe')
669
bzrlib.trace.log_exception()
764
672
if __name__ == '__main__':
765
673
sys.exit(main(sys.argv))