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: 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?
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.
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
38
45
from warnings import warn
46
from inspect import getdoc
51
from bzrlib.symbol_versioning import (
58
from bzrlib.option import Option
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
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.
58
def register_command(cmd):
59
"Utility function to help register a command"
74
62
if k.startswith("cmd_"):
75
63
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
66
if not plugin_cmds.has_key(k_unsquished):
67
plugin_cmds[k_unsquished] = cmd
68
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__])
70
log_error('Two plugins defined the same command: %r' % k)
71
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
92
74
def _squish_command_name(cmd):
98
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))
101
151
def _builtin_commands():
102
152
import bzrlib.builtins
104
154
builtins = bzrlib.builtins.__dict__
105
155
for name in builtins:
106
156
if name.startswith("cmd_"):
107
real_name = _unsquish_command_name(name)
157
real_name = _unsquish_command_name(name)
108
158
r[real_name] = builtins[name]
112
163
def builtin_command_names():
196
237
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().
241
List of options that may be given for this command.
213
244
If true, this command isn't advertised. This is typically
214
245
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
250
takes_options = []
235
encoding_type = 'strict'
241
256
if self.__doc__ == Command.__doc__:
242
257
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)
260
def run_argv(self, argv):
261
"""Parse command line and run."""
262
args, opts = parse_args(argv)
375
264
if 'help' in opts: # e.g. bzr add --help
376
sys.stdout.write(self.get_help_text())
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()))
378
276
# mix arguments and options into one dictionary
379
277
cmdargs = _match_argform(self.name(), self.takes_args, args)
457
339
parsed = [spec, None]
460
def parse_args(command, argv, alias_argv=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):
461
384
"""Parse command line.
463
386
Arguments and options are parsed at this level before being passed
464
387
down to specific command handlers. This routine knows, from a
465
388
lookup table, something about the available options, what optargs
466
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>]})
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])
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)
478
482
return args, opts
481
487
def _match_argform(cmd, takes_args, args):
495
501
argdict[argname + '_list'] = None
496
502
elif ap[-1] == '+':
498
raise errors.BzrCommandError("command %r needs one or more %s"
499
% (cmd, argname.upper()))
504
raise BzrCommandError("command %r needs one or more %s"
505
% (cmd, argname.upper()))
501
507
argdict[argname + '_list'] = args[:]
503
509
elif ap[-1] == '$': # all but one
504
510
if len(args) < 2:
505
raise errors.BzrCommandError("command %r needs one or more %s"
506
% (cmd, argname.upper()))
511
raise BzrCommandError("command %r needs one or more %s"
512
% (cmd, argname.upper()))
507
513
argdict[argname + '_list'] = args[:-1]
510
516
# just a plain arg
513
raise errors.BzrCommandError("command %r requires argument %s"
514
% (cmd, argname.upper()))
519
raise BzrCommandError("command %r requires argument %s"
520
% (cmd, argname.upper()))
516
522
argdict[argname] = args.pop(0)
519
raise errors.BzrCommandError("extra argument to command %s: %s"
525
raise BzrCommandError("extra argument to command %s: %s"
534
539
ret = prof.runcall(the_callable, *args, **kwargs) or 0
537
544
stats = hotshot.stats.load(pfname)
539
stats.sort_stats('cum') # 'time'
546
stats.sort_stats('time')
540
547
## XXX: Might like to write to stderr or the trace file instead but
541
548
## print_stats seems hardcoded to stdout
542
549
stats.print_stats(20)
545
553
os.close(pffileno)
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)
582
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
584
opt_profile = opt_no_plugins = opt_builtin = False
620
586
# --no-plugins is handled specially at a very early stage. We need
621
587
# to load plugins before doing other command parsing so that they
622
588
# can override commands, but this needs to happen first.
628
591
if a == '--profile':
629
592
opt_profile = True
630
elif a == '--lsprof':
632
elif a == '--lsprof-file':
634
opt_lsprof_file = argv[i + 1]
636
593
elif a == '--no-plugins':
637
594
opt_no_plugins = True
638
elif a == '--no-aliases':
639
opt_no_aliases = True
640
595
elif a == '--builtin':
641
596
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([])
601
if (not argv) or (argv[0] == '--help'):
602
from bzrlib.help import help
656
609
if argv[0] == '--version':
657
from bzrlib.version import show_version
610
from bzrlib.builtins import show_version
661
614
if not opt_no_plugins:
662
615
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.
618
cmd = str(argv.pop(0))
681
620
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:
623
ret = apply_profiled(cmd_obj.run_argv, argv)
625
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)
631
bzrlib.trace.log_startup(argv)
632
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
634
return run_bzr_catch_errors(argv[1:])
727
637
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)
643
# do this here inside the exception wrappers to catch EPIPE
645
#wrap common errors as CommandErrors.
646
except (NotBranchError,), e:
647
raise BzrCommandError(str(e))
648
except BzrCommandError, e:
649
# command line syntax error, etc
653
bzrlib.trace.log_exception()
655
except AssertionError, e:
656
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)
658
except KeyboardInterrupt, e:
659
bzrlib.trace.log_exception('interrupted')
663
if (isinstance(e, IOError)
664
and hasattr(e, 'errno')
665
and e.errno == errno.EPIPE):
666
bzrlib.trace.note('broken pipe')
669
bzrlib.trace.log_exception()
764
673
if __name__ == '__main__':