~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2007-03-07 23:15:10 UTC
  • mto: (1551.19.24 Aaron's mergeable stuff)
  • mto: This revision was merged to the branch mainline in revision 2325.
  • Revision ID: abentley@panoramicfeedback.com-20070307231510-jae63zsli83db3eb
Make ChangeReporter private

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by Canonical Ltd
 
1
# Copyright (C) 2006 Canonical Ltd
2
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
28
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
29
29
# the profile output behind so it can be interactively examined?
30
30
 
 
31
import os
31
32
import sys
32
 
import os
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import codecs
 
37
import errno
33
38
from warnings import warn
34
 
import errno
35
39
 
36
40
import bzrlib
37
 
from bzrlib.errors import (BzrError,
38
 
                           BzrCheckError,
39
 
                           BzrCommandError,
40
 
                           BzrOptionError,
41
 
                           NotBranchError)
 
41
from bzrlib import (
 
42
    debug,
 
43
    errors,
 
44
    option,
 
45
    osutils,
 
46
    trace,
 
47
    )
 
48
""")
 
49
 
 
50
from bzrlib.symbol_versioning import (
 
51
    deprecated_function,
 
52
    deprecated_method,
 
53
    zero_eight,
 
54
    zero_eleven,
 
55
    )
 
56
# Compatibility
42
57
from bzrlib.option import Option
43
 
from bzrlib.revisionspec import RevisionSpec
44
 
from bzrlib.symbol_versioning import *
45
 
import bzrlib.trace
46
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
 
58
 
47
59
 
48
60
plugin_cmds = {}
49
61
 
62
74
        k_unsquished = _unsquish_command_name(k)
63
75
    else:
64
76
        k_unsquished = k
65
 
    if not plugin_cmds.has_key(k_unsquished):
 
77
    if k_unsquished not in plugin_cmds:
66
78
        plugin_cmds[k_unsquished] = cmd
67
 
        mutter('registered plugin command %s', k_unsquished)
 
79
        ## trace.mutter('registered plugin command %s', k_unsquished)
68
80
        if decorate and k_unsquished in builtin_command_names():
69
81
            return _builtin_commands()[k_unsquished]
70
82
    elif decorate:
72
84
        plugin_cmds[k_unsquished] = cmd
73
85
        return result
74
86
    else:
75
 
        log_error('Two plugins defined the same command: %r' % k)
76
 
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
87
        trace.log_error('Two plugins defined the same command: %r' % k)
 
88
        trace.log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
77
89
 
78
90
 
79
91
def _squish_command_name(cmd):
127
139
    """
128
140
    from bzrlib.externalcommand import ExternalCommand
129
141
 
130
 
    cmd_name = str(cmd_name)            # not unicode
 
142
    # We want only 'ascii' command names, but the user may have typed
 
143
    # in a Unicode name. In that case, they should just get a
 
144
    # 'command not found' error later.
 
145
    # In the future, we may actually support Unicode command names.
131
146
 
132
147
    # first look up this command under the specified name
133
148
    cmds = _get_cmd_dict(plugins_override=plugins_override)
145
160
    if cmd_obj:
146
161
        return cmd_obj
147
162
 
148
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
163
    raise errors.BzrCommandError('unknown command "%s"' % cmd_name)
149
164
 
150
165
 
151
166
class Command(object):
189
204
    hidden
190
205
        If true, this command isn't advertised.  This is typically
191
206
        for commands intended for expert users.
 
207
 
 
208
    encoding_type
 
209
        Command objects will get a 'outf' attribute, which has been
 
210
        setup to properly handle encoding of unicode strings.
 
211
        encoding_type determines what will happen when characters cannot
 
212
        be encoded
 
213
            strict - abort if we cannot decode
 
214
            replace - put in a bogus character (typically '?')
 
215
            exact - do not encode sys.stdout
 
216
 
 
217
            NOTE: by default on Windows, sys.stdout is opened as a text
 
218
            stream, therefore LF line-endings are converted to CRLF.
 
219
            When a command uses encoding_type = 'exact', then
 
220
            sys.stdout is forced to be a binary stream, and line-endings
 
221
            will not mangled.
 
222
 
192
223
    """
193
224
    aliases = []
194
225
    takes_args = []
195
226
    takes_options = []
 
227
    encoding_type = 'strict'
196
228
 
197
229
    hidden = False
198
230
    
206
238
 
207
239
        Maps from long option name to option object."""
208
240
        r = dict()
209
 
        r['help'] = Option.OPTIONS['help']
 
241
        r['help'] = option.Option.OPTIONS['help']
210
242
        for o in self.takes_options:
211
 
            if not isinstance(o, Option):
212
 
                o = Option.OPTIONS[o]
 
243
            if isinstance(o, basestring):
 
244
                o = option.Option.OPTIONS[o]
213
245
            r[o.name] = o
214
246
        return r
215
247
 
 
248
    def _setup_outf(self):
 
249
        """Return a file linked to stdout, which has proper encoding."""
 
250
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
251
 
 
252
        # Originally I was using self.stdout, but that looks
 
253
        # *way* too much like sys.stdout
 
254
        if self.encoding_type == 'exact':
 
255
            # force sys.stdout to be binary stream on win32
 
256
            if sys.platform == 'win32':
 
257
                fileno = getattr(sys.stdout, 'fileno', None)
 
258
                if fileno:
 
259
                    import msvcrt
 
260
                    msvcrt.setmode(fileno(), os.O_BINARY)
 
261
            self.outf = sys.stdout
 
262
            return
 
263
 
 
264
        output_encoding = osutils.get_terminal_encoding()
 
265
 
 
266
        # use 'replace' so that we don't abort if trying to write out
 
267
        # in e.g. the default C locale.
 
268
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
269
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
270
        # it just returns the encoding of the wrapped file, which is completely
 
271
        # bogus. So set the attribute, so we can find the correct encoding later.
 
272
        self.outf.encoding = output_encoding
 
273
 
216
274
    @deprecated_method(zero_eight)
217
275
    def run_argv(self, argv):
218
276
        """Parse command line and run.
223
281
 
224
282
    def run_argv_aliases(self, argv, alias_argv=None):
225
283
        """Parse the command line and run with extra aliases in alias_argv."""
 
284
        if argv is None:
 
285
            warn("Passing None for [] is deprecated from bzrlib 0.10",
 
286
                 DeprecationWarning, stacklevel=2)
 
287
            argv = []
226
288
        args, opts = parse_args(self, argv, alias_argv)
227
289
        if 'help' in opts:  # e.g. bzr add --help
228
290
            from bzrlib.help import help_on_command
229
291
            help_on_command(self.name())
230
292
            return 0
231
 
        # XXX: This should be handled by the parser
232
 
        allowed_names = self.options().keys()
233
 
        for oname in opts:
234
 
            if oname not in allowed_names:
235
 
                raise BzrCommandError("option '--%s' is not allowed for"
236
 
                                      " command %r" % (oname, self.name()))
237
293
        # mix arguments and options into one dictionary
238
294
        cmdargs = _match_argform(self.name(), self.takes_args, args)
239
295
        cmdopts = {}
243
299
        all_cmd_args = cmdargs.copy()
244
300
        all_cmd_args.update(cmdopts)
245
301
 
 
302
        self._setup_outf()
 
303
 
246
304
        return self.run(**all_cmd_args)
247
305
    
248
306
    def run(self):
255
313
        shell error code if not.  It's OK for this method to allow
256
314
        an exception to raise up.
257
315
        """
258
 
        raise NotImplementedError('no implementation of command %r' 
 
316
        raise NotImplementedError('no implementation of command %r'
259
317
                                  % self.name())
260
318
 
261
319
    def help(self):
268
326
    def name(self):
269
327
        return _unsquish_command_name(self.__class__.__name__)
270
328
 
271
 
 
 
329
    def plugin_name(self):
 
330
        """Get the name of the plugin that provides this command.
 
331
 
 
332
        :return: The name of the plugin or None if the command is builtin.
 
333
        """
 
334
        mod_parts = self.__module__.split('.')
 
335
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
336
            return mod_parts[2]
 
337
        else:
 
338
            return None
 
339
 
 
340
 
 
341
# Technically, this function hasn't been use in a *really* long time
 
342
# but we are only deprecating it now.
 
343
@deprecated_function(zero_eleven)
272
344
def parse_spec(spec):
273
345
    """
274
346
    >>> parse_spec(None)
308
380
    lookup table, something about the available options, what optargs
309
381
    they take, and which commands will accept them.
310
382
    """
311
 
    # TODO: chop up this beast; make it a method of the Command
312
 
    args = []
313
 
    opts = {}
314
 
    alias_opts = {}
315
 
 
316
 
    cmd_options = command.options()
317
 
    argsover = False
318
 
    proc_aliasarg = True # Are we processing alias_argv now?
319
 
    for proc_argv in alias_argv, argv:
320
 
        while proc_argv:
321
 
            a = proc_argv.pop(0)
322
 
            if argsover:
323
 
                args.append(a)
324
 
                continue
325
 
            elif a == '--':
326
 
                # We've received a standalone -- No more flags
327
 
                argsover = True
328
 
                continue
329
 
            if a[0] == '-':
330
 
                # option names must not be unicode
331
 
                a = str(a)
332
 
                optarg = None
333
 
                if a[1] == '-':
334
 
                    mutter("  got option %r", a)
335
 
                    if '=' in a:
336
 
                        optname, optarg = a[2:].split('=', 1)
337
 
                    else:
338
 
                        optname = a[2:]
339
 
                    if optname not in cmd_options:
340
 
                        raise BzrOptionError('unknown long option %r for'
341
 
                                             ' command %s' % 
342
 
                                             (a, command.name()))
343
 
                else:
344
 
                    shortopt = a[1:]
345
 
                    if shortopt in Option.SHORT_OPTIONS:
346
 
                        # Multi-character options must have a space to delimit
347
 
                        # their value
348
 
                        # ^^^ what does this mean? mbp 20051014
349
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
350
 
                    else:
351
 
                        # Single character short options, can be chained,
352
 
                        # and have their value appended to their name
353
 
                        shortopt = a[1:2]
354
 
                        if shortopt not in Option.SHORT_OPTIONS:
355
 
                            # We didn't find the multi-character name, and we
356
 
                            # didn't find the single char name
357
 
                            raise BzrError('unknown short option %r' % a)
358
 
                        optname = Option.SHORT_OPTIONS[shortopt].name
359
 
 
360
 
                        if a[2:]:
361
 
                            # There are extra things on this option
362
 
                            # see if it is the value, or if it is another
363
 
                            # short option
364
 
                            optargfn = Option.OPTIONS[optname].type
365
 
                            if optargfn is None:
366
 
                                # This option does not take an argument, so the
367
 
                                # next entry is another short option, pack it
368
 
                                # back into the list
369
 
                                proc_argv.insert(0, '-' + a[2:])
370
 
                            else:
371
 
                                # This option takes an argument, so pack it
372
 
                                # into the array
373
 
                                optarg = a[2:]
374
 
                
375
 
                    if optname not in cmd_options:
376
 
                        raise BzrOptionError('unknown short option %r for'
377
 
                                             ' command %s' % 
378
 
                                             (shortopt, command.name()))
379
 
                if optname in opts:
380
 
                    # XXX: Do we ever want to support this, e.g. for -r?
381
 
                    if proc_aliasarg:
382
 
                        raise BzrError('repeated option %r' % a)
383
 
                    elif optname in alias_opts:
384
 
                        # Replace what's in the alias with what's in the real
385
 
                        # argument
386
 
                        del alias_opts[optname]
387
 
                        del opts[optname]
388
 
                        proc_argv.insert(0, a)
389
 
                        continue
390
 
                    else:
391
 
                        raise BzrError('repeated option %r' % a)
392
 
                    
393
 
                option_obj = cmd_options[optname]
394
 
                optargfn = option_obj.type
395
 
                if optargfn:
396
 
                    if optarg == None:
397
 
                        if not proc_argv:
398
 
                            raise BzrError('option %r needs an argument' % a)
399
 
                        else:
400
 
                            optarg = proc_argv.pop(0)
401
 
                    opts[optname] = optargfn(optarg)
402
 
                    if proc_aliasarg:
403
 
                        alias_opts[optname] = optargfn(optarg)
404
 
                else:
405
 
                    if optarg != None:
406
 
                        raise BzrError('option %r takes no argument' % optname)
407
 
                    opts[optname] = True
408
 
                    if proc_aliasarg:
409
 
                        alias_opts[optname] = True
410
 
            else:
411
 
                args.append(a)
412
 
        proc_aliasarg = False # Done with alias argv
 
383
    # TODO: make it a method of the Command?
 
384
    parser = option.get_optparser(command.options())
 
385
    if alias_argv is not None:
 
386
        args = alias_argv + argv
 
387
    else:
 
388
        args = argv
 
389
 
 
390
    options, args = parser.parse_args(args)
 
391
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if
 
392
                 v is not option.OptionParser.DEFAULT_VALUE])
413
393
    return args, opts
414
394
 
415
395
 
430
410
                argdict[argname + '_list'] = None
431
411
        elif ap[-1] == '+':
432
412
            if not args:
433
 
                raise BzrCommandError("command %r needs one or more %s"
434
 
                        % (cmd, argname.upper()))
 
413
                raise errors.BzrCommandError("command %r needs one or more %s"
 
414
                                             % (cmd, argname.upper()))
435
415
            else:
436
416
                argdict[argname + '_list'] = args[:]
437
417
                args = []
438
418
        elif ap[-1] == '$': # all but one
439
419
            if len(args) < 2:
440
 
                raise BzrCommandError("command %r needs one or more %s"
441
 
                        % (cmd, argname.upper()))
 
420
                raise errors.BzrCommandError("command %r needs one or more %s"
 
421
                                             % (cmd, argname.upper()))
442
422
            argdict[argname + '_list'] = args[:-1]
443
 
            args[:-1] = []                
 
423
            args[:-1] = []
444
424
        else:
445
425
            # just a plain arg
446
426
            argname = ap
447
427
            if not args:
448
 
                raise BzrCommandError("command %r requires argument %s"
449
 
                        % (cmd, argname.upper()))
 
428
                raise errors.BzrCommandError("command %r requires argument %s"
 
429
                               % (cmd, argname.upper()))
450
430
            else:
451
431
                argdict[argname] = args.pop(0)
452
432
            
453
433
    if args:
454
 
        raise BzrCommandError("extra argument to command %s: %s"
455
 
                              % (cmd, args[0]))
 
434
        raise errors.BzrCommandError("extra argument to command %s: %s"
 
435
                                     % (cmd, args[0]))
456
436
 
457
437
    return argdict
458
438
 
495
475
    return ret
496
476
 
497
477
 
498
 
def get_alias(cmd):
499
 
    """Return an expanded alias, or None if no alias exists"""
500
 
    import bzrlib.config
501
 
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
478
def get_alias(cmd, config=None):
 
479
    """Return an expanded alias, or None if no alias exists.
 
480
 
 
481
    cmd
 
482
        Command to be checked for an alias.
 
483
    config
 
484
        Used to specify an alternative config to use,
 
485
        which is especially useful for testing.
 
486
        If it is unspecified, the global config will be used.
 
487
    """
 
488
    if config is None:
 
489
        import bzrlib.config
 
490
        config = bzrlib.config.GlobalConfig()
 
491
    alias = config.get_alias(cmd)
502
492
    if (alias):
503
 
        return alias.split(' ')
 
493
        import shlex
 
494
        return [a.decode('utf-8') for a in shlex.split(alias.encode('utf-8'))]
504
495
    return None
505
496
 
506
497
 
512
503
    
513
504
    argv
514
505
       The command-line arguments, without the program name from argv[0]
 
506
       These should already be decoded. All library/test code calling
 
507
       run_bzr should be passing valid strings (don't need decoding).
515
508
    
516
509
    Returns a command status or raises an exception.
517
510
 
534
527
    --lsprof
535
528
        Run under the Python lsprof profiler.
536
529
    """
537
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
530
    argv = list(argv)
 
531
    trace.mutter("bzr arguments: %r", argv)
538
532
 
539
533
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
540
534
                opt_no_aliases = False
553
547
        elif a == '--lsprof':
554
548
            opt_lsprof = True
555
549
        elif a == '--lsprof-file':
 
550
            opt_lsprof = True
556
551
            opt_lsprof_file = argv[i + 1]
557
552
            i += 1
558
553
        elif a == '--no-plugins':
562
557
        elif a == '--builtin':
563
558
            opt_builtin = True
564
559
        elif a in ('--quiet', '-q'):
565
 
            be_quiet()
 
560
            trace.be_quiet()
 
561
        elif a.startswith('-D'):
 
562
            debug.debug_flags.add(a[2:])
566
563
        else:
567
564
            argv_copy.append(a)
568
565
        i += 1
574
571
        return 0
575
572
 
576
573
    if argv[0] == '--version':
577
 
        from bzrlib.builtins import show_version
 
574
        from bzrlib.version import show_version
578
575
        show_version()
579
576
        return 0
580
577
        
593
590
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
594
591
            argv[0] = alias_argv.pop(0)
595
592
 
596
 
    cmd = str(argv.pop(0))
 
593
    cmd = argv.pop(0)
 
594
    # We want only 'ascii' command names, but the user may have typed
 
595
    # in a Unicode name. In that case, they should just get a
 
596
    # 'command not found' error later.
597
597
 
598
598
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
599
599
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
613
613
        return ret or 0
614
614
    finally:
615
615
        # reset, in case we may do other commands later within the same process
616
 
        be_quiet(False)
 
616
        trace.be_quiet(False)
617
617
 
618
618
def display_command(func):
619
619
    """Decorator that suppresses pipe/interrupt errors."""
623
623
            sys.stdout.flush()
624
624
            return result
625
625
        except IOError, e:
626
 
            if not hasattr(e, 'errno'):
 
626
            if getattr(e, 'errno', None) is None:
627
627
                raise
628
628
            if e.errno != errno.EPIPE:
629
 
                raise
 
629
                # Win32 raises IOError with errno=0 on a broken pipe
 
630
                if sys.platform != 'win32' or (e.errno not in (0, errno.EINVAL)):
 
631
                    raise
630
632
            pass
631
633
        except KeyboardInterrupt:
632
634
            pass
636
638
def main(argv):
637
639
    import bzrlib.ui
638
640
    from bzrlib.ui.text import TextUIFactory
639
 
    ## bzrlib.trace.enable_default_logging()
640
 
    bzrlib.trace.log_startup(argv)
641
641
    bzrlib.ui.ui_factory = TextUIFactory()
642
 
    ret = run_bzr_catch_errors(argv[1:])
643
 
    mutter("return code %d", ret)
 
642
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
643
    ret = run_bzr_catch_errors(argv)
 
644
    trace.mutter("return code %d", ret)
644
645
    return ret
645
646
 
646
647
 
647
648
def run_bzr_catch_errors(argv):
648
649
    try:
649
 
        try:
650
 
            return run_bzr(argv)
651
 
        finally:
652
 
            # do this here inside the exception wrappers to catch EPIPE
653
 
            sys.stdout.flush()
654
 
    except Exception, e:
 
650
        return run_bzr(argv)
 
651
        # do this here inside the exception wrappers to catch EPIPE
 
652
        sys.stdout.flush()
 
653
    except (KeyboardInterrupt, Exception), e:
655
654
        # used to handle AssertionError and KeyboardInterrupt
656
655
        # specially here, but hopefully they're handled ok by the logger now
657
 
        import errno
658
 
        if (isinstance(e, IOError) 
659
 
            and hasattr(e, 'errno')
660
 
            and e.errno == errno.EPIPE):
661
 
            bzrlib.trace.note('broken pipe')
662
 
            return 3
663
 
        else:
664
 
            bzrlib.trace.log_exception()
665
 
            if os.environ.get('BZR_PDB'):
666
 
                print '**** entering debugger'
667
 
                import pdb
668
 
                pdb.post_mortem(sys.exc_traceback)
669
 
            return 3
 
656
        trace.report_exception(sys.exc_info(), sys.stderr)
 
657
        if os.environ.get('BZR_PDB'):
 
658
            print '**** entering debugger'
 
659
            import pdb
 
660
            pdb.post_mortem(sys.exc_traceback)
 
661
        return 3
670
662
 
671
663
if __name__ == '__main__':
672
664
    sys.exit(main(sys.argv))