~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

Don't ignore bzrlib/plugins -- really bad idea because they can have bad 
old plugins lying around!

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
2
 
#
 
1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
2
 
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.
7
 
#
 
7
 
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.
12
 
#
 
12
 
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.
20
20
 
 
21
# TODO: Help messages for options.
 
22
 
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.
25
 
 
26
 
# TODO: Specific "examples" property on commands for consistent formatting.
 
25
# would help with validation and shell completion.
27
26
 
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?
30
29
 
31
 
import codecs
32
 
import errno
 
30
import sys
33
31
import os
34
32
from warnings import warn
35
 
import sys
 
33
from inspect import getdoc
 
34
import errno
36
35
 
37
36
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.errors import (BzrError,
 
37
import bzrlib.trace
 
38
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
39
from bzrlib.errors import (BzrError, 
 
40
                           BzrCheckError,
40
41
                           BzrCommandError,
41
 
                           BzrCheckError,
 
42
                           BzrOptionError,
42
43
                           NotBranchError)
43
 
from bzrlib import option
 
44
from bzrlib.revisionspec import RevisionSpec
 
45
from bzrlib import BZRDIR
44
46
from bzrlib.option import Option
45
 
import bzrlib.osutils
46
 
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
47
 
import bzrlib.trace
48
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
49
47
 
50
48
plugin_cmds = {}
51
49
 
52
50
 
53
51
def register_command(cmd, decorate=False):
54
 
    """Utility function to help register a command
55
 
 
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.
60
 
    """
 
52
    "Utility function to help register a command"
61
53
    global plugin_cmds
62
54
    k = cmd.__name__
63
55
    if k.startswith("cmd_"):
64
56
        k_unsquished = _unsquish_command_name(k)
65
57
    else:
66
58
        k_unsquished = k
67
 
    if k_unsquished not in plugin_cmds:
 
59
    if not plugin_cmds.has_key(k_unsquished):
68
60
        plugin_cmds[k_unsquished] = cmd
69
 
        mutter('registered plugin command %s', k_unsquished)
 
61
        mutter('registered plugin command %s', k_unsquished)      
70
62
        if decorate and k_unsquished in builtin_command_names():
71
63
            return _builtin_commands()[k_unsquished]
72
64
    elif decorate:
93
85
    builtins = bzrlib.builtins.__dict__
94
86
    for name in builtins:
95
87
        if name.startswith("cmd_"):
96
 
            real_name = _unsquish_command_name(name)
 
88
            real_name = _unsquish_command_name(name)        
97
89
            r[real_name] = builtins[name]
98
90
    return r
 
91
 
99
92
            
100
93
 
101
94
def builtin_command_names():
129
122
    """
130
123
    from bzrlib.externalcommand import ExternalCommand
131
124
 
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.
 
125
    cmd_name = str(cmd_name)            # not unicode
136
126
 
137
127
    # first look up this command under the specified name
138
128
    cmds = _get_cmd_dict(plugins_override=plugins_override)
150
140
    if cmd_obj:
151
141
        return cmd_obj
152
142
 
153
 
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
143
    raise BzrCommandError("unknown command %r" % cmd_name)
154
144
 
155
145
 
156
146
class Command(object):
178
168
        List of argument forms, marked with whether they are optional,
179
169
        repeated, etc.
180
170
 
181
 
                Examples:
182
 
 
183
 
                ['to_location', 'from_branch?', 'file*']
184
 
 
185
 
                'to_location' is required
186
 
                'from_branch' is optional
187
 
                'file' can be specified 0 or more times
 
171
                Examples:
 
172
 
 
173
                ['to_location', 'from_branch?', 'file*']
 
174
 
 
175
                'to_location' is required
 
176
                'from_branch' is optional
 
177
                'file' can be specified 0 or more times
188
178
 
189
179
    takes_options
190
180
        List of options that may be given for this command.  These can
194
184
    hidden
195
185
        If true, this command isn't advertised.  This is typically
196
186
        for commands intended for expert users.
197
 
 
198
 
    encoding_type
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
202
 
        be encoded
203
 
            strict - abort if we cannot decode
204
 
            replace - put in a bogus character (typically '?')
205
 
            exact - do not encode sys.stdout
206
 
 
207
187
    """
208
188
    aliases = []
209
189
    takes_args = []
210
190
    takes_options = []
211
 
    encoding_type = 'strict'
212
191
 
213
192
    hidden = False
214
193
    
224
203
        r = dict()
225
204
        r['help'] = Option.OPTIONS['help']
226
205
        for o in self.takes_options:
227
 
            if isinstance(o, basestring):
 
206
            if not isinstance(o, Option):
228
207
                o = Option.OPTIONS[o]
229
208
            r[o.name] = o
230
209
        return r
231
210
 
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']
235
 
 
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
240
 
            return
241
 
 
242
 
        output_encoding = bzrlib.osutils.get_terminal_encoding()
243
 
 
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
251
 
 
252
 
    @deprecated_method(zero_eight)
253
211
    def run_argv(self, argv):
254
 
        """Parse command line and run.
255
 
        
256
 
        See run_argv_aliases for the 0.8 and beyond api.
257
 
        """
258
 
        return self.run_argv_aliases(argv)
259
 
 
260
 
    def run_argv_aliases(self, argv, alias_argv=None):
261
 
        """Parse the command line and run with extra aliases in alias_argv."""
262
 
        if argv is None:
263
 
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
264
 
                 DeprecationWarning, stacklevel=2)
265
 
            argv = []
266
 
        args, opts = parse_args(self, argv, alias_argv)
 
212
        """Parse command line and run."""
 
213
        args, opts = parse_args(self, argv)
267
214
        if 'help' in opts:  # e.g. bzr add --help
268
215
            from bzrlib.help import help_on_command
269
216
            help_on_command(self.name())
270
217
            return 0
 
218
        # XXX: This should be handled by the parser
 
219
        allowed_names = self.options().keys()
 
220
        for oname in opts:
 
221
            if oname not in allowed_names:
 
222
                raise BzrCommandError("option '--%s' is not allowed for command %r"
 
223
                                      % (oname, self.name()))
271
224
        # mix arguments and options into one dictionary
272
225
        cmdargs = _match_argform(self.name(), self.takes_args, args)
273
226
        cmdopts = {}
277
230
        all_cmd_args = cmdargs.copy()
278
231
        all_cmd_args.update(cmdopts)
279
232
 
280
 
        self._setup_outf()
281
 
 
282
233
        return self.run(**all_cmd_args)
283
234
    
284
235
    def run(self):
291
242
        shell error code if not.  It's OK for this method to allow
292
243
        an exception to raise up.
293
244
        """
294
 
        raise NotImplementedError('no implementation of command %r' 
295
 
                                  % self.name())
 
245
        raise NotImplementedError()
 
246
 
296
247
 
297
248
    def help(self):
298
249
        """Return help message for this class."""
299
 
        from inspect import getdoc
300
250
        if self.__doc__ is Command.__doc__:
301
251
            return None
302
252
        return getdoc(self)
304
254
    def name(self):
305
255
        return _unsquish_command_name(self.__class__.__name__)
306
256
 
307
 
    def plugin_name(self):
308
 
        """Get the name of the plugin that provides this command.
309
 
 
310
 
        :return: The name of the plugin or None if the command is builtin.
311
 
        """
312
 
        mod_parts = self.__module__.split('.')
313
 
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
314
 
            return mod_parts[2]
315
 
        else:
316
 
            return None
317
 
 
318
257
 
319
258
def parse_spec(spec):
320
259
    """
347
286
        parsed = [spec, None]
348
287
    return parsed
349
288
 
350
 
def parse_args(command, argv, alias_argv=None):
 
289
def parse_args(command, argv):
351
290
    """Parse command line.
352
291
    
353
292
    Arguments and options are parsed at this level before being passed
355
294
    lookup table, something about the available options, what optargs
356
295
    they take, and which commands will accept them.
357
296
    """
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
362
 
    else:
363
 
        args = argv
364
 
 
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])
 
297
    # TODO: chop up this beast; make it a method of the Command
 
298
    args = []
 
299
    opts = {}
 
300
 
 
301
    cmd_options = command.options()
 
302
    argsover = False
 
303
    while argv:
 
304
        a = argv.pop(0)
 
305
        if argsover:
 
306
            args.append(a)
 
307
            continue
 
308
        elif a == '--':
 
309
            # We've received a standalone -- No more flags
 
310
            argsover = True
 
311
            continue
 
312
        if a[0] == '-':
 
313
            # option names must not be unicode
 
314
            a = str(a)
 
315
            optarg = None
 
316
            if a[1] == '-':
 
317
                mutter("  got option %r", a)
 
318
                if '=' in a:
 
319
                    optname, optarg = a[2:].split('=', 1)
 
320
                else:
 
321
                    optname = a[2:]
 
322
                if optname not in cmd_options:
 
323
                    raise BzrOptionError('unknown long option %r for command %s'
 
324
                        % (a, command.name()))
 
325
            else:
 
326
                shortopt = a[1:]
 
327
                if shortopt in Option.SHORT_OPTIONS:
 
328
                    # Multi-character options must have a space to delimit
 
329
                    # their value
 
330
                    # ^^^ what does this mean? mbp 20051014
 
331
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
332
                else:
 
333
                    # Single character short options, can be chained,
 
334
                    # and have their value appended to their name
 
335
                    shortopt = a[1:2]
 
336
                    if shortopt not in Option.SHORT_OPTIONS:
 
337
                        # We didn't find the multi-character name, and we
 
338
                        # didn't find the single char name
 
339
                        raise BzrError('unknown short option %r' % a)
 
340
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
341
 
 
342
                    if a[2:]:
 
343
                        # There are extra things on this option
 
344
                        # see if it is the value, or if it is another
 
345
                        # short option
 
346
                        optargfn = Option.OPTIONS[optname].type
 
347
                        if optargfn is None:
 
348
                            # This option does not take an argument, so the
 
349
                            # next entry is another short option, pack it back
 
350
                            # into the list
 
351
                            argv.insert(0, '-' + a[2:])
 
352
                        else:
 
353
                            # This option takes an argument, so pack it
 
354
                            # into the array
 
355
                            optarg = a[2:]
 
356
            
 
357
                if optname not in cmd_options:
 
358
                    raise BzrOptionError('unknown short option %r for command'
 
359
                        ' %s' % (shortopt, command.name()))
 
360
            if optname in opts:
 
361
                # XXX: Do we ever want to support this, e.g. for -r?
 
362
                raise BzrError('repeated option %r' % a)
 
363
                
 
364
            option_obj = cmd_options[optname]
 
365
            optargfn = option_obj.type
 
366
            if optargfn:
 
367
                if optarg == None:
 
368
                    if not argv:
 
369
                        raise BzrError('option %r needs an argument' % a)
 
370
                    else:
 
371
                        optarg = argv.pop(0)
 
372
                opts[optname] = optargfn(optarg)
 
373
            else:
 
374
                if optarg != None:
 
375
                    raise BzrError('option %r takes no argument' % optname)
 
376
                opts[optname] = True
 
377
        else:
 
378
            args.append(a)
368
379
    return args, opts
369
380
 
370
381
 
395
406
                raise BzrCommandError("command %r needs one or more %s"
396
407
                        % (cmd, argname.upper()))
397
408
            argdict[argname + '_list'] = args[:-1]
398
 
            args[:-1] = []
 
409
            args[:-1] = []                
399
410
        else:
400
411
            # just a plain arg
401
412
            argname = ap
436
447
        os.remove(pfname)
437
448
 
438
449
 
439
 
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
440
 
    from bzrlib.lsprof import profile
441
 
    import cPickle
442
 
    ret, stats = profile(the_callable, *args, **kwargs)
443
 
    stats.sort()
444
 
    if filename is None:
445
 
        stats.pprint()
446
 
    else:
447
 
        stats.freeze()
448
 
        cPickle.dump(stats, open(filename, 'w'), 2)
449
 
        print 'Profile data written to %r.' % filename
450
 
    return ret
451
 
 
452
 
 
453
 
def get_alias(cmd):
454
 
    """Return an expanded alias, or None if no alias exists"""
455
 
    import bzrlib.config
456
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
457
 
    if (alias):
458
 
        return alias.split(' ')
459
 
    return None
460
 
 
461
 
 
462
450
def run_bzr(argv):
463
451
    """Execute a command.
464
452
 
467
455
    
468
456
    argv
469
457
       The command-line arguments, without the program name from argv[0]
470
 
       These should already be decoded. All library/test code calling
471
 
       run_bzr should be passing valid strings (don't need decoding).
472
458
    
473
459
    Returns a command status or raises an exception.
474
460
 
478
464
    --no-plugins
479
465
        Do not load plugin modules at all
480
466
 
481
 
    --no-aliases
482
 
        Do not allow aliases
483
 
 
484
467
    --builtin
485
468
        Only use builtin commands.  (Plugins are still allowed to change
486
469
        other behaviour.)
487
470
 
488
471
    --profile
489
 
        Run under the Python hotshot profiler.
490
 
 
491
 
    --lsprof
492
 
        Run under the Python lsprof profiler.
 
472
        Run under the Python profiler.
493
473
    """
494
 
    argv = list(argv)
 
474
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
495
475
 
496
 
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
497
 
                opt_no_aliases = False
498
 
    opt_lsprof_file = None
 
476
    opt_profile = opt_no_plugins = opt_builtin = False
499
477
 
500
478
    # --no-plugins is handled specially at a very early stage. We need
501
479
    # to load plugins before doing other command parsing so that they
502
480
    # can override commands, but this needs to happen first.
503
481
 
504
 
    argv_copy = []
505
 
    i = 0
506
 
    while i < len(argv):
507
 
        a = argv[i]
 
482
    for a in argv:
508
483
        if a == '--profile':
509
484
            opt_profile = True
510
 
        elif a == '--lsprof':
511
 
            opt_lsprof = True
512
 
        elif a == '--lsprof-file':
513
 
            opt_lsprof = True
514
 
            opt_lsprof_file = argv[i + 1]
515
 
            i += 1
516
485
        elif a == '--no-plugins':
517
486
            opt_no_plugins = True
518
 
        elif a == '--no-aliases':
519
 
            opt_no_aliases = True
520
487
        elif a == '--builtin':
521
488
            opt_builtin = True
522
489
        elif a in ('--quiet', '-q'):
523
490
            be_quiet()
524
491
        else:
525
 
            argv_copy.append(a)
526
 
        i += 1
 
492
            continue
 
493
        argv.remove(a)
527
494
 
528
 
    argv = argv_copy
529
 
    if (not argv):
530
 
        from bzrlib.builtins import cmd_help
531
 
        cmd_help().run_argv_aliases([])
 
495
    if (not argv) or (argv[0] == '--help'):
 
496
        from bzrlib.help import help
 
497
        if len(argv) > 1:
 
498
            help(argv[1])
 
499
        else:
 
500
            help()
532
501
        return 0
533
502
 
534
503
    if argv[0] == '--version':
535
 
        from bzrlib.version import show_version
 
504
        from bzrlib.builtins import show_version
536
505
        show_version()
537
506
        return 0
538
507
        
539
508
    if not opt_no_plugins:
540
509
        from bzrlib.plugin import load_plugins
541
510
        load_plugins()
542
 
    else:
543
 
        from bzrlib.plugin import disable_plugins
544
 
        disable_plugins()
545
 
 
546
 
    alias_argv = None
547
 
 
548
 
    if not opt_no_aliases:
549
 
        alias_argv = get_alias(argv[0])
550
 
        if alias_argv:
551
 
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
552
 
            argv[0] = alias_argv.pop(0)
553
 
 
554
 
    cmd = 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.
 
511
 
 
512
    cmd = str(argv.pop(0))
558
513
 
559
514
    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
562
 
        run_argv = [argv]
563
 
    else:
564
 
        run = cmd_obj.run_argv_aliases
565
 
        run_argv = [argv, alias_argv]
566
515
 
567
516
    try:
568
 
        if opt_lsprof:
569
 
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
570
 
        elif opt_profile:
571
 
            ret = apply_profiled(run, *run_argv)
 
517
        if opt_profile:
 
518
            ret = apply_profiled(cmd_obj.run_argv, argv)
572
519
        else:
573
 
            ret = run(*run_argv)
 
520
            ret = cmd_obj.run_argv(argv)
574
521
        return ret or 0
575
522
    finally:
576
523
        # reset, in case we may do other commands later within the same process
584
531
            sys.stdout.flush()
585
532
            return result
586
533
        except IOError, e:
587
 
            if getattr(e, 'errno', None) is None:
 
534
            if not hasattr(e, 'errno'):
588
535
                raise
589
536
            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:
592
 
                    raise
 
537
                raise
593
538
            pass
594
539
        except KeyboardInterrupt:
595
540
            pass
598
543
 
599
544
def main(argv):
600
545
    import bzrlib.ui
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)
 
546
    ## bzrlib.trace.enable_default_logging()
 
547
    bzrlib.trace.log_startup(argv)
 
548
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
549
    ret = run_bzr_catch_errors(argv[1:])
605
550
    mutter("return code %d", ret)
606
551
    return ret
607
552
 
608
553
 
609
554
def run_bzr_catch_errors(argv):
610
555
    try:
611
 
        return run_bzr(argv)
612
 
        # do this here inside the exception wrappers to catch EPIPE
613
 
        sys.stdout.flush()
 
556
        try:
 
557
            return run_bzr(argv)
 
558
        finally:
 
559
            # do this here inside the exception wrappers to catch EPIPE
 
560
            sys.stdout.flush()
614
561
    except Exception, e:
615
562
        # used to handle AssertionError and KeyboardInterrupt
616
563
        # 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'
620
 
            import pdb
621
 
            pdb.post_mortem(sys.exc_traceback)
622
 
        return 3
 
564
        import errno
 
565
        if (isinstance(e, IOError) 
 
566
            and hasattr(e, 'errno')
 
567
            and e.errno == errno.EPIPE):
 
568
            bzrlib.trace.note('broken pipe')
 
569
            return 3
 
570
        else:
 
571
            bzrlib.trace.log_exception()
 
572
            if os.environ.get('BZR_PDB'):
 
573
                print '**** entering debugger'
 
574
                import pdb
 
575
                pdb.post_mortem(sys.exc_traceback)
 
576
            return 3
623
577
 
624
578
if __name__ == '__main__':
625
579
    sys.exit(main(sys.argv))