1
# Copyright (C) 2004, 2005 by Canonical Ltd
1
# Copyright (C) 2006 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.
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
# would help with validation and shell completion.
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?
31
34
from warnings import warn
32
from inspect import getdoc
38
import bzrlib.errors as errors
39
from bzrlib.errors import (BzrError,
43
from bzrlib import option
44
from bzrlib.option import Option
46
from bzrlib.revisionspec import RevisionSpec
47
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
35
48
import bzrlib.trace
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
49
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
44
def register_command(cmd):
45
"Utility function to help register a command"
54
def register_command(cmd, decorate=False):
55
"""Utility function to help register a command
57
:param cmd: Command subclass to register
58
:param decorate: If true, allow overriding an existing command
59
of the same name; the old command is returned by this function.
60
Otherwise it is an error to try to override an existing command.
48
64
if k.startswith("cmd_"):
66
88
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
91
def _builtin_commands():
154
92
import bzrlib.builtins
156
94
builtins = bzrlib.builtins.__dict__
157
95
for name in builtins:
158
96
if name.startswith("cmd_"):
159
real_name = _unsquish_command_name(name)
97
real_name = _unsquish_command_name(name)
160
98
r[real_name] = builtins[name]
165
102
def builtin_command_names():
239
176
List of argument forms, marked with whether they are optional,
181
['to_location', 'from_branch?', 'file*']
183
'to_location' is required
184
'from_branch' is optional
185
'file' can be specified 0 or more times
243
List of options that may be given for this command.
188
List of options that may be given for this command. These can
189
be either strings, referring to globally-defined options,
190
or option objects. Retrieve through options().
246
193
If true, this command isn't advertised. This is typically
247
194
for commands intended for expert users.
197
Command objects will get a 'outf' attribute, which has been
198
setup to properly handle encoding of unicode strings.
199
encoding_type determines what will happen when characters cannot
201
strict - abort if we cannot decode
202
replace - put in a bogus character (typically '?')
203
exact - do not encode sys.stdout
252
208
takes_options = []
209
encoding_type = 'strict'
258
215
if self.__doc__ == Command.__doc__:
259
216
warn("No help message set for %r" % self)
219
"""Return dict of valid options for this command.
221
Maps from long option name to option object."""
223
r['help'] = Option.OPTIONS['help']
224
for o in self.takes_options:
225
if isinstance(o, basestring):
226
o = Option.OPTIONS[o]
230
def _setup_outf(self):
231
"""Return a file linked to stdout, which has proper encoding."""
232
assert self.encoding_type in ['strict', 'exact', 'replace']
234
# Originally I was using self.stdout, but that looks
235
# *way* too much like sys.stdout
236
if self.encoding_type == 'exact':
237
self.outf = sys.stdout
240
output_encoding = bzrlib.osutils.get_terminal_encoding()
242
# use 'replace' so that we don't abort if trying to write out
243
# in e.g. the default C locale.
244
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
245
# For whatever reason codecs.getwriter() does not advertise its encoding
246
# it just returns the encoding of the wrapped file, which is completely
247
# bogus. So set the attribute, so we can find the correct encoding later.
248
self.outf.encoding = output_encoding
250
@deprecated_method(zero_eight)
262
251
def run_argv(self, argv):
263
"""Parse command line and run."""
264
args, opts = parse_args(argv)
252
"""Parse command line and run.
254
See run_argv_aliases for the 0.8 and beyond api.
256
return self.run_argv_aliases(argv)
258
def run_argv_aliases(self, argv, alias_argv=None):
259
"""Parse the command line and run with extra aliases in alias_argv."""
261
warn("Passing None for [] is deprecated from bzrlib 0.10",
262
DeprecationWarning, stacklevel=2)
264
args, opts = parse_args(self, argv, alias_argv)
266
265
if 'help' in opts: # e.g. bzr add --help
267
266
from bzrlib.help import help_on_command
268
267
help_on_command(self.name())
271
# check options are reasonable
272
allowed = self.takes_options
274
if oname not in allowed:
275
raise BzrCommandError("option '--%s' is not allowed for command %r"
276
% (oname, self.name()))
278
269
# mix arguments and options into one dictionary
279
270
cmdargs = _match_argform(self.name(), self.takes_args, args)
341
345
parsed = [spec, None]
347
# list of all available options; the rhs can be either None for an
348
# option that takes no argument, or a constructor function that checks
361
'revision': _parse_revision_str,
373
'merge-type': get_merge_type,
387
def parse_args(argv):
348
def parse_args(command, argv, alias_argv=None):
388
349
"""Parse command line.
390
351
Arguments and options are parsed at this level before being passed
391
352
down to specific command handlers. This routine knows, from a
392
353
lookup table, something about the available options, what optargs
393
354
they take, and which commands will accept them.
395
>>> parse_args('--help'.split())
397
>>> parse_args('help -- --invalidcmd'.split())
398
(['help', '--invalidcmd'], {})
399
>>> parse_args('--version'.split())
400
([], {'version': True})
401
>>> parse_args('status --all'.split())
402
(['status'], {'all': True})
403
>>> parse_args('commit --message=biter'.split())
404
(['commit'], {'message': u'biter'})
405
>>> parse_args('log -r 500'.split())
406
(['log'], {'revision': [500]})
407
>>> parse_args('log -r500..600'.split())
408
(['log'], {'revision': [500, 600]})
409
>>> parse_args('log -vr500..600'.split())
410
(['log'], {'verbose': True, 'revision': [500, 600]})
411
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
412
(['log'], {'revision': ['v500', 600]})
420
if not argsover and a[0] == '-':
421
# option names must not be unicode
426
# We've received a standalone -- No more flags
429
mutter(" got option %r" % a)
431
optname, optarg = a[2:].split('=', 1)
434
if optname not in OPTIONS:
435
raise BzrError('unknown long option %r' % a)
438
if shortopt in SHORT_OPTIONS:
439
# Multi-character options must have a space to delimit
441
optname = SHORT_OPTIONS[shortopt]
443
# Single character short options, can be chained,
444
# and have their value appended to their name
446
if shortopt not in SHORT_OPTIONS:
447
# We didn't find the multi-character name, and we
448
# didn't find the single char name
449
raise BzrError('unknown short option %r' % a)
450
optname = SHORT_OPTIONS[shortopt]
453
# There are extra things on this option
454
# see if it is the value, or if it is another
456
optargfn = OPTIONS[optname]
458
# This option does not take an argument, so the
459
# next entry is another short option, pack it back
461
argv.insert(0, '-' + a[2:])
463
# This option takes an argument, so pack it
468
# XXX: Do we ever want to support this, e.g. for -r?
469
raise BzrError('repeated option %r' % a)
471
optargfn = OPTIONS[optname]
475
raise BzrError('option %r needs an argument' % a)
478
opts[optname] = optargfn(optarg)
481
raise BzrError('option %r takes no argument' % optname)
356
# TODO: make it a method of the Command?
357
parser = option.get_optparser(command.options())
358
if alias_argv is not None:
359
args = alias_argv + argv
363
options, args = parser.parse_args(args)
364
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
365
v is not option.OptionParser.DEFAULT_VALUE])
486
366
return args, opts
491
369
def _match_argform(cmd, takes_args, args):
543
422
ret = prof.runcall(the_callable, *args, **kwargs) or 0
548
425
stats = hotshot.stats.load(pfname)
550
stats.sort_stats('time')
427
stats.sort_stats('cum') # 'time'
551
428
## XXX: Might like to write to stderr or the trace file instead but
552
429
## print_stats seems hardcoded to stdout
553
430
stats.print_stats(20)
557
433
os.close(pffileno)
558
434
os.remove(pfname)
437
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
438
from bzrlib.lsprof import profile
440
ret, stats = profile(the_callable, *args, **kwargs)
446
cPickle.dump(stats, open(filename, 'w'), 2)
447
print 'Profile data written to %r.' % filename
452
"""Return an expanded alias, or None if no alias exists"""
454
alias = bzrlib.config.GlobalConfig().get_alias(cmd)
456
return alias.split(' ')
561
460
def run_bzr(argv):
562
461
"""Execute a command.
576
477
Do not load plugin modules at all
579
483
Only use builtin commands. (Plugins are still allowed to change
580
484
other behaviour.)
583
Run under the Python profiler.
487
Run under the Python hotshot profiler.
490
Run under the Python lsprof profiler.
586
argv = [a.decode(bzrlib.user_encoding) for a in argv]
588
opt_profile = opt_no_plugins = opt_builtin = False
494
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
495
opt_no_aliases = False
496
opt_lsprof_file = None
590
498
# --no-plugins is handled specially at a very early stage. We need
591
499
# to load plugins before doing other command parsing so that they
592
500
# can override commands, but this needs to happen first.
595
506
if a == '--profile':
596
507
opt_profile = True
508
elif a == '--lsprof':
510
elif a == '--lsprof-file':
512
opt_lsprof_file = argv[i + 1]
597
514
elif a == '--no-plugins':
598
515
opt_no_plugins = True
516
elif a == '--no-aliases':
517
opt_no_aliases = True
599
518
elif a == '--builtin':
600
519
opt_builtin = True
520
elif a in ('--quiet', '-q'):
605
if (not argv) or (argv[0] == '--help'):
606
from bzrlib.help import help
528
from bzrlib.builtins import cmd_help
529
cmd_help().run_argv_aliases([])
613
532
if argv[0] == '--version':
614
from bzrlib.builtins import show_version
533
from bzrlib.version import show_version
618
537
if not opt_no_plugins:
619
538
from bzrlib.plugin import load_plugins
541
from bzrlib.plugin import disable_plugins
546
if not opt_no_aliases:
547
alias_argv = get_alias(argv[0])
549
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
550
argv[0] = alias_argv.pop(0)
622
552
cmd = str(argv.pop(0))
624
554
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
627
ret = apply_profiled(cmd_obj.run_argv, argv)
555
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
556
run = cmd_obj.run_argv
629
ret = cmd_obj.run_argv(argv)
559
run = cmd_obj.run_argv_aliases
560
run_argv = [argv, alias_argv]
564
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
566
ret = apply_profiled(run, *run_argv)
571
# reset, in case we may do other commands later within the same process
574
def display_command(func):
575
"""Decorator that suppresses pipe/interrupt errors."""
576
def ignore_pipe(*args, **kwargs):
578
result = func(*args, **kwargs)
582
if not hasattr(e, 'errno'):
584
if e.errno != errno.EPIPE:
585
# Win32 raises IOError with errno=0 on a broken pipe
586
if sys.platform != 'win32' or e.errno != 0:
589
except KeyboardInterrupt:
635
bzrlib.trace.log_startup(argv)
636
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
596
from bzrlib.ui.text import TextUIFactory
597
bzrlib.ui.ui_factory = TextUIFactory()
598
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
599
ret = run_bzr_catch_errors(argv)
600
mutter("return code %d", ret)
604
def run_bzr_catch_errors(argv):
640
return run_bzr(argv[1:])
642
# do this here inside the exception wrappers to catch EPIPE
644
except BzrCommandError, e:
645
# command line syntax error, etc
649
bzrlib.trace.log_exception()
651
except AssertionError, e:
652
bzrlib.trace.log_exception('assertion failed: ' + str(e))
607
# do this here inside the exception wrappers to catch EPIPE
610
# used to handle AssertionError and KeyboardInterrupt
611
# specially here, but hopefully they're handled ok by the logger now
612
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
613
if os.environ.get('BZR_PDB'):
614
print '**** entering debugger'
616
pdb.post_mortem(sys.exc_traceback)
654
except KeyboardInterrupt, e:
655
bzrlib.trace.note('interrupted')
659
if (isinstance(e, IOError)
660
and hasattr(e, 'errno')
661
and e.errno == errno.EPIPE):
662
bzrlib.trace.note('broken pipe')
665
bzrlib.trace.log_exception()
669
619
if __name__ == '__main__':
670
620
sys.exit(main(sys.argv))