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?
34
from warnings import warn
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.symbol_versioning import (deprecated_method, zero_eight)
32
47
import bzrlib.trace
33
from bzrlib.trace import mutter, note, log_error, warning
34
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
35
from bzrlib.branch import find_branch
36
from bzrlib import BZRDIR
48
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
42
def register_command(cmd):
43
"Utility function to help register a command"
53
def register_command(cmd, decorate=False):
54
"""Utility function to help register a command
56
:param cmd: Command subclass to register
57
:param decorate: If true, allow overriding an existing command
58
of the same name; the old command is returned by this function.
59
Otherwise it is an error to try to override an existing command.
46
63
if k.startswith("cmd_"):
47
64
k_unsquished = _unsquish_command_name(k)
50
if not plugin_cmds.has_key(k_unsquished):
51
plugin_cmds[k_unsquished] = cmd
52
mutter('registered plugin command %s', k_unsquished)
67
if k_unsquished not in plugin_cmds:
68
plugin_cmds[k_unsquished] = cmd
69
mutter('registered plugin command %s', k_unsquished)
70
if decorate and k_unsquished in builtin_command_names():
71
return _builtin_commands()[k_unsquished]
73
result = plugin_cmds[k_unsquished]
74
plugin_cmds[k_unsquished] = cmd
54
77
log_error('Two plugins defined the same command: %r' % k)
55
78
log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
64
87
return cmd[4:].replace('_','-')
67
def _parse_revision_str(revstr):
68
"""This handles a revision string -> revno.
70
This always returns a list. The list will have one element for
72
It supports integers directly, but everything else it
73
defers for passing to Branch.get_revision_info()
75
>>> _parse_revision_str('234')
77
>>> _parse_revision_str('234..567')
79
>>> _parse_revision_str('..')
81
>>> _parse_revision_str('..234')
83
>>> _parse_revision_str('234..')
85
>>> _parse_revision_str('234..456..789') # Maybe this should be an error
87
>>> _parse_revision_str('234....789') # Error?
89
>>> _parse_revision_str('revid:test@other.com-234234')
90
['revid:test@other.com-234234']
91
>>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
92
['revid:test@other.com-234234', 'revid:test@other.com-234235']
93
>>> _parse_revision_str('revid:test@other.com-234234..23')
94
['revid:test@other.com-234234', 23]
95
>>> _parse_revision_str('date:2005-04-12')
97
>>> _parse_revision_str('date:2005-04-12 12:24:33')
98
['date:2005-04-12 12:24:33']
99
>>> _parse_revision_str('date:2005-04-12T12:24:33')
100
['date:2005-04-12T12:24:33']
101
>>> _parse_revision_str('date:2005-04-12,12:24:33')
102
['date:2005-04-12,12:24:33']
103
>>> _parse_revision_str('-5..23')
105
>>> _parse_revision_str('-5')
107
>>> _parse_revision_str('123a')
109
>>> _parse_revision_str('abc')
113
old_format_re = re.compile('\d*:\d*')
114
m = old_format_re.match(revstr)
116
warning('Colon separator for revision numbers is deprecated.'
119
for rev in revstr.split(':'):
121
revs.append(int(rev))
126
for x in revstr.split('..'):
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)
166
def _get_cmd_dict(plugins_override=True):
90
def _builtin_commands():
167
91
import bzrlib.builtins
170
93
builtins = bzrlib.builtins.__dict__
171
94
for name in builtins:
172
95
if name.startswith("cmd_"):
173
d[_unsquish_command_name(name)] = builtins[name]
174
# If we didn't load plugins, the plugin_cmds dict will be empty
96
real_name = _unsquish_command_name(name)
97
r[real_name] = builtins[name]
101
def builtin_command_names():
102
"""Return list of builtin command names."""
103
return _builtin_commands().keys()
106
def plugin_command_names():
107
return plugin_cmds.keys()
110
def _get_cmd_dict(plugins_override=True):
111
"""Return name->class mapping for all commands."""
112
d = _builtin_commands()
175
113
if plugins_override:
176
114
d.update(plugin_cmds)
178
d2 = plugin_cmds.copy()
190
def get_cmd_class(cmd, plugins_override=True):
124
def get_cmd_object(cmd_name, plugins_override=True):
191
125
"""Return the canonical name and command class for a command.
128
If true, plugin commands can override builtins.
193
cmd = str(cmd) # not unicode
130
from bzrlib.externalcommand import ExternalCommand
132
# We want only 'ascii' command names, but the user may have typed
133
# in a Unicode name. In that case, they should just get a
134
# 'command not found' error later.
135
# In the future, we may actually support Unicode command names.
195
137
# first look up this command under the specified name
196
138
cmds = _get_cmd_dict(plugins_override=plugins_override)
197
mutter("all commands: %r", cmds.keys())
199
return cmd, cmds[cmd]
140
return cmds[cmd_name]()
203
144
# look for any command which claims this as an alias
204
for cmdname, cmdclass in cmds.iteritems():
205
if cmd in cmdclass.aliases:
206
return cmdname, cmdclass
208
cmdclass = ExternalCommand.find_command(cmd)
212
raise BzrCommandError("unknown command %r" % cmd)
145
for real_cmd_name, cmd_class in cmds.iteritems():
146
if cmd_name in cmd_class.aliases:
149
cmd_obj = ExternalCommand.find_command(cmd_name)
153
raise BzrCommandError('unknown command "%s"' % cmd_name)
215
156
class Command(object):
216
157
"""Base class for commands.
159
Commands are the heart of the command-line bzr interface.
161
The command object mostly handles the mapping of command-line
162
parameters into one or more bzrlib operations, and of the results
165
Commands normally don't have any state. All their arguments are
166
passed in to the run method. (Subclasses may take a different
167
policy if the behaviour of the instance needs to depend on e.g. a
168
shell plugin and not just its Python class.)
218
170
The docstring for an actual command should give a single-line
219
171
summary, then a complete description of the command. A grammar
220
172
description will be inserted.
175
Other accepted names for this command.
223
178
List of argument forms, marked with whether they are optional,
183
['to_location', 'from_branch?', 'file*']
185
'to_location' is required
186
'from_branch' is optional
187
'file' can be specified 0 or more times
227
List of options that may be given for this command.
190
List of options that may be given for this command. These can
191
be either strings, referring to globally-defined options,
192
or option objects. Retrieve through options().
230
If true, this command isn't advertised.
195
If true, this command isn't advertised. This is typically
196
for commands intended for expert users.
199
Command objects will get a 'outf' attribute, which has been
200
setup to properly handle encoding of unicode strings.
201
encoding_type determines what will happen when characters cannot
203
strict - abort if we cannot decode
204
replace - put in a bogus character (typically '?')
205
exact - do not encode sys.stdout
235
210
takes_options = []
211
encoding_type = 'strict'
239
def __init__(self, options, arguments):
240
"""Construct and run the command.
242
Sets self.status to the return value of run()."""
243
assert isinstance(options, dict)
244
assert isinstance(arguments, dict)
245
cmdargs = options.copy()
246
cmdargs.update(arguments)
216
"""Construct an instance of this command."""
247
217
if self.__doc__ == Command.__doc__:
248
from warnings import warn
249
218
warn("No help message set for %r" % self)
250
self.status = self.run(**cmdargs)
251
if self.status is None:
221
"""Return dict of valid options for this command.
223
Maps from long option name to option object."""
225
r['help'] = Option.OPTIONS['help']
226
for o in self.takes_options:
227
if isinstance(o, basestring):
228
o = Option.OPTIONS[o]
232
def _setup_outf(self):
233
"""Return a file linked to stdout, which has proper encoding."""
234
assert self.encoding_type in ['strict', 'exact', 'replace']
236
# Originally I was using self.stdout, but that looks
237
# *way* too much like sys.stdout
238
if self.encoding_type == 'exact':
239
self.outf = sys.stdout
242
output_encoding = bzrlib.osutils.get_terminal_encoding()
244
# use 'replace' so that we don't abort if trying to write out
245
# in e.g. the default C locale.
246
self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
247
# For whatever reason codecs.getwriter() does not advertise its encoding
248
# it just returns the encoding of the wrapped file, which is completely
249
# bogus. So set the attribute, so we can find the correct encoding later.
250
self.outf.encoding = output_encoding
252
@deprecated_method(zero_eight)
253
def run_argv(self, argv):
254
"""Parse command line and run.
256
See run_argv_aliases for the 0.8 and beyond api.
258
return self.run_argv_aliases(argv)
260
def run_argv_aliases(self, argv, alias_argv=None):
261
"""Parse the command line and run with extra aliases in alias_argv."""
263
warn("Passing None for [] is deprecated from bzrlib 0.10",
264
DeprecationWarning, stacklevel=2)
266
args, opts = parse_args(self, argv, alias_argv)
267
if 'help' in opts: # e.g. bzr add --help
268
from bzrlib.help import help_on_command
269
help_on_command(self.name())
271
# mix arguments and options into one dictionary
272
cmdargs = _match_argform(self.name(), self.takes_args, args)
274
for k, v in opts.items():
275
cmdopts[k.replace('-', '_')] = v
277
all_cmd_args = cmdargs.copy()
278
all_cmd_args.update(cmdopts)
282
return self.run(**all_cmd_args)
255
def run(self, *args, **kwargs):
256
"""Override this in sub-classes.
285
"""Actually run the command.
258
287
This is invoked with the options and arguments bound to
259
288
keyword parameters.
261
Return 0 or None if the command was successful, or a shell
264
raise NotImplementedError()
267
class ExternalCommand(Command):
268
"""Class to wrap external commands.
270
We cheat a little here, when get_cmd_class() calls us we actually
271
give it back an object we construct that has the appropriate path,
272
help, options etc for the specified command.
274
When run_bzr() tries to instantiate that 'class' it gets caught by
275
the __call__ method, which we override to call the Command.__init__
276
method. That then calls our run method which is pretty straight
279
The only wrinkle is that we have to map bzr's dictionary of options
280
and arguments back into command line options and arguments for the
284
def find_command(cls, cmd):
286
bzrpath = os.environ.get('BZRPATH', '')
288
for dir in bzrpath.split(os.pathsep):
289
path = os.path.join(dir, cmd)
290
if os.path.isfile(path):
291
return ExternalCommand(path)
295
find_command = classmethod(find_command)
297
def __init__(self, path):
300
pipe = os.popen('%s --bzr-usage' % path, 'r')
301
self.takes_options = pipe.readline().split()
303
for opt in self.takes_options:
304
if not opt in OPTIONS:
305
raise BzrError("Unknown option '%s' returned by external command %s"
308
# TODO: Is there any way to check takes_args is valid here?
309
self.takes_args = pipe.readline().split()
311
if pipe.close() is not None:
312
raise BzrError("Failed funning '%s --bzr-usage'" % path)
314
pipe = os.popen('%s --bzr-help' % path, 'r')
315
self.__doc__ = pipe.read()
316
if pipe.close() is not None:
317
raise BzrError("Failed funning '%s --bzr-help'" % path)
319
def __call__(self, options, arguments):
320
Command.__init__(self, options, arguments)
323
def run(self, **kargs):
330
optname = name.replace('_','-')
332
if OPTIONS.has_key(optname):
334
opts.append('--%s' % optname)
335
if value is not None and value is not True:
336
opts.append(str(value))
338
# it's an arg, or arg list
339
if type(value) is not list:
345
self.status = os.spawnv(os.P_WAIT, self.path, [self.path] + opts + args)
290
Return 0 or None if the command was successful, or a non-zero
291
shell error code if not. It's OK for this method to allow
292
an exception to raise up.
294
raise NotImplementedError('no implementation of command %r'
298
"""Return help message for this class."""
299
from inspect import getdoc
300
if self.__doc__ is Command.__doc__:
305
return _unsquish_command_name(self.__class__.__name__)
307
def plugin_name(self):
308
"""Get the name of the plugin that provides this command.
310
:return: The name of the plugin or None if the command is builtin.
312
mod_parts = self.__module__.split('.')
313
if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
350
319
def parse_spec(spec):
378
347
parsed = [spec, None]
384
# list of all available options; the rhs can be either None for an
385
# option that takes no argument, or a constructor function that checks
398
'revision': _parse_revision_str,
410
'merge-type': get_merge_type,
424
def parse_args(argv):
350
def parse_args(command, argv, alias_argv=None):
425
351
"""Parse command line.
427
353
Arguments and options are parsed at this level before being passed
428
354
down to specific command handlers. This routine knows, from a
429
355
lookup table, something about the available options, what optargs
430
356
they take, and which commands will accept them.
432
>>> parse_args('--help'.split())
434
>>> parse_args('help -- --invalidcmd'.split())
435
(['help', '--invalidcmd'], {})
436
>>> parse_args('--version'.split())
437
([], {'version': True})
438
>>> parse_args('status --all'.split())
439
(['status'], {'all': True})
440
>>> parse_args('commit --message=biter'.split())
441
(['commit'], {'message': u'biter'})
442
>>> parse_args('log -r 500'.split())
443
(['log'], {'revision': [500]})
444
>>> parse_args('log -r500..600'.split())
445
(['log'], {'revision': [500, 600]})
446
>>> parse_args('log -vr500..600'.split())
447
(['log'], {'verbose': True, 'revision': [500, 600]})
448
>>> parse_args('log -rv500..600'.split()) #the r takes an argument
449
(['log'], {'revision': ['v500', 600]})
457
if not argsover and a[0] == '-':
458
# option names must not be unicode
463
# We've received a standalone -- No more flags
466
mutter(" got option %r" % a)
468
optname, optarg = a[2:].split('=', 1)
471
if optname not in OPTIONS:
472
raise BzrError('unknown long option %r' % a)
475
if shortopt in SHORT_OPTIONS:
476
# Multi-character options must have a space to delimit
478
optname = SHORT_OPTIONS[shortopt]
480
# Single character short options, can be chained,
481
# and have their value appended to their name
483
if shortopt not in SHORT_OPTIONS:
484
# We didn't find the multi-character name, and we
485
# didn't find the single char name
486
raise BzrError('unknown short option %r' % a)
487
optname = SHORT_OPTIONS[shortopt]
490
# There are extra things on this option
491
# see if it is the value, or if it is another
493
optargfn = OPTIONS[optname]
495
# This option does not take an argument, so the
496
# next entry is another short option, pack it back
498
argv.insert(0, '-' + a[2:])
500
# This option takes an argument, so pack it
505
# XXX: Do we ever want to support this, e.g. for -r?
506
raise BzrError('repeated option %r' % a)
508
optargfn = OPTIONS[optname]
512
raise BzrError('option %r needs an argument' % a)
515
opts[optname] = optargfn(optarg)
518
raise BzrError('option %r takes no argument' % optname)
358
# TODO: make it a method of the Command?
359
parser = option.get_optparser(command.options())
360
if alias_argv is not None:
361
args = alias_argv + argv
365
options, args = parser.parse_args(args)
366
opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
367
v is not option.OptionParser.DEFAULT_VALUE])
523
368
return args, opts
528
371
def _match_argform(cmd, takes_args, args):
588
479
Do not load plugin modules at all
591
485
Only use builtin commands. (Plugins are still allowed to change
592
486
other behaviour.)
595
Run under the Python profiler.
489
Run under the Python hotshot profiler.
492
Run under the Python lsprof profiler.
598
argv = [a.decode(bzrlib.user_encoding) for a in argv]
600
opt_profile = opt_no_plugins = opt_builtin = False
496
opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = \
497
opt_no_aliases = False
498
opt_lsprof_file = None
602
500
# --no-plugins is handled specially at a very early stage. We need
603
501
# to load plugins before doing other command parsing so that they
604
502
# can override commands, but this needs to happen first.
607
508
if a == '--profile':
608
509
opt_profile = True
510
elif a == '--lsprof':
512
elif a == '--lsprof-file':
514
opt_lsprof_file = argv[i + 1]
609
516
elif a == '--no-plugins':
610
517
opt_no_plugins = True
518
elif a == '--no-aliases':
519
opt_no_aliases = True
611
520
elif a == '--builtin':
612
521
opt_builtin = True
522
elif a in ('--quiet', '-q'):
530
from bzrlib.builtins import cmd_help
531
cmd_help().run_argv_aliases([])
534
if argv[0] == '--version':
535
from bzrlib.version import show_version
617
539
if not opt_no_plugins:
618
540
from bzrlib.plugin import load_plugins
621
args, opts = parse_args(argv)
624
from bzrlib.help import help
543
from bzrlib.plugin import disable_plugins
548
if not opt_no_aliases:
549
alias_argv = get_alias(argv[0])
551
alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
552
argv[0] = alias_argv.pop(0)
555
# We want only 'ascii' command names, but the user may have typed
556
# in a Unicode name. In that case, they should just get a
557
# 'command not found' error later.
559
cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
560
if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
561
run = cmd_obj.run_argv
564
run = cmd_obj.run_argv_aliases
565
run_argv = [argv, alias_argv]
569
ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
571
ret = apply_profiled(run, *run_argv)
631
if 'version' in opts:
632
from bzrlib.builtins import show_version
637
from bzrlib.help import help
641
cmd = str(args.pop(0))
643
canonical_cmd, cmd_class = \
644
get_cmd_class(cmd, plugins_override=not opt_builtin)
646
# check options are reasonable
647
allowed = cmd_class.takes_options
649
if oname not in allowed:
650
raise BzrCommandError("option '--%s' is not allowed for command %r"
653
# mix arguments and options into one dictionary
654
cmdargs = _match_argform(cmd, cmd_class.takes_args, args)
656
for k, v in opts.items():
657
cmdopts[k.replace('-', '_')] = v
660
import hotshot, tempfile
661
pffileno, pfname = tempfile.mkstemp()
576
# reset, in case we may do other commands later within the same process
579
def display_command(func):
580
"""Decorator that suppresses pipe/interrupt errors."""
581
def ignore_pipe(*args, **kwargs):
663
prof = hotshot.Profile(pfname)
664
ret = prof.runcall(cmd_class, cmdopts, cmdargs) or 0
668
stats = hotshot.stats.load(pfname)
670
stats.sort_stats('time')
671
## XXX: Might like to write to stderr or the trace file instead but
672
## print_stats seems hardcoded to stdout
673
stats.print_stats(20)
681
return cmd_class(cmdopts, cmdargs).status
583
result = func(*args, **kwargs)
587
if getattr(e, 'errno', None) is None:
589
if e.errno != errno.EPIPE:
590
# Win32 raises IOError with errno=0 on a broken pipe
591
if sys.platform != 'win32' or e.errno != 0:
594
except KeyboardInterrupt:
686
bzrlib.trace.log_startup(argv)
687
bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
601
from bzrlib.ui.text import TextUIFactory
602
bzrlib.ui.ui_factory = TextUIFactory()
603
argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
604
ret = run_bzr_catch_errors(argv)
605
mutter("return code %d", ret)
609
def run_bzr_catch_errors(argv):
691
return run_bzr(argv[1:])
693
# do this here inside the exception wrappers to catch EPIPE
695
except BzrCommandError, e:
696
# command line syntax error, etc
700
bzrlib.trace.log_exception()
702
except AssertionError, e:
703
bzrlib.trace.log_exception('assertion failed: ' + str(e))
612
# do this here inside the exception wrappers to catch EPIPE
615
# used to handle AssertionError and KeyboardInterrupt
616
# specially here, but hopefully they're handled ok by the logger now
617
bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
618
if os.environ.get('BZR_PDB'):
619
print '**** entering debugger'
621
pdb.post_mortem(sys.exc_traceback)
705
except KeyboardInterrupt, e:
706
bzrlib.trace.note('interrupted')
710
if (isinstance(e, IOError)
711
and hasattr(e, 'errno')
712
and e.errno == errno.EPIPE):
713
bzrlib.trace.note('broken pipe')
716
bzrlib.trace.log_exception()
720
624
if __name__ == '__main__':
721
625
sys.exit(main(sys.argv))