~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Michael Ellerman
  • Date: 2006-02-28 14:45:51 UTC
  • mto: (1558.1.18 Aaron's integration)
  • mto: This revision was merged to the branch mainline in revision 1586.
  • Revision ID: michael@ellerman.id.au-20060228144551-3d9941ecde4a0b0a
Update contrib/pwk for -p1 diffs from bzr

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?
31
30
import sys
32
31
import os
33
32
from warnings import warn
 
33
from inspect import getdoc
34
34
import errno
35
 
import codecs
36
35
 
37
36
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.errors import (BzrError,
 
37
from bzrlib.errors import (BzrError, 
 
38
                           BzrCheckError,
40
39
                           BzrCommandError,
41
 
                           BzrCheckError,
 
40
                           BzrOptionError,
42
41
                           NotBranchError)
43
42
from bzrlib.option import Option
44
 
import bzrlib.osutils
45
43
from bzrlib.revisionspec import RevisionSpec
46
 
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
47
 
from bzrlib import trace
 
44
from bzrlib.symbol_versioning import *
 
45
import bzrlib.trace
48
46
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_"):
66
58
        k_unsquished = k
67
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():
147
140
    if cmd_obj:
148
141
        return cmd_obj
149
142
 
150
 
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
143
    raise BzrCommandError("unknown command %r" % cmd_name)
151
144
 
152
145
 
153
146
class Command(object):
191
184
    hidden
192
185
        If true, this command isn't advertised.  This is typically
193
186
        for commands intended for expert users.
194
 
 
195
 
    encoding_type
196
 
        Command objects will get a 'outf' attribute, which has been
197
 
        setup to properly handle encoding of unicode strings.
198
 
        encoding_type determines what will happen when characters cannot
199
 
        be encoded
200
 
            strict - abort if we cannot decode
201
 
            replace - put in a bogus character (typically '?')
202
 
            exact - do not encode sys.stdout
203
 
 
204
187
    """
205
188
    aliases = []
206
189
    takes_args = []
207
190
    takes_options = []
208
 
    encoding_type = 'strict'
209
191
 
210
192
    hidden = False
211
193
    
226
208
            r[o.name] = o
227
209
        return r
228
210
 
229
 
    def _setup_outf(self):
230
 
        """Return a file linked to stdout, which has proper encoding."""
231
 
        assert self.encoding_type in ['strict', 'exact', 'replace']
232
 
 
233
 
        # Originally I was using self.stdout, but that looks
234
 
        # *way* too much like sys.stdout
235
 
        if self.encoding_type == 'exact':
236
 
            self.outf = sys.stdout
237
 
            return
238
 
 
239
 
        output_encoding = bzrlib.osutils.get_terminal_encoding()
240
 
 
241
 
        # use 'replace' so that we don't abort if trying to write out
242
 
        # in e.g. the default C locale.
243
 
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
244
 
        # For whatever reason codecs.getwriter() does not advertise its encoding
245
 
        # it just returns the encoding of the wrapped file, which is completely
246
 
        # bogus. So set the attribute, so we can find the correct encoding later.
247
 
        self.outf.encoding = output_encoding
248
 
 
249
211
    @deprecated_method(zero_eight)
250
212
    def run_argv(self, argv):
251
213
        """Parse command line and run.
265
227
        allowed_names = self.options().keys()
266
228
        for oname in opts:
267
229
            if oname not in allowed_names:
268
 
                raise BzrOptionError("option '--%s' is not allowed for"
269
 
                                " command %r" % (oname, self.name()))
 
230
                raise BzrCommandError("option '--%s' is not allowed for"
 
231
                                      " command %r" % (oname, self.name()))
270
232
        # mix arguments and options into one dictionary
271
233
        cmdargs = _match_argform(self.name(), self.takes_args, args)
272
234
        cmdopts = {}
276
238
        all_cmd_args = cmdargs.copy()
277
239
        all_cmd_args.update(cmdopts)
278
240
 
279
 
        self._setup_outf()
280
 
 
281
241
        return self.run(**all_cmd_args)
282
242
    
283
243
    def run(self):
290
250
        shell error code if not.  It's OK for this method to allow
291
251
        an exception to raise up.
292
252
        """
293
 
        raise NotImplementedError('no implementation of command %r' 
294
 
                                  % self.name())
 
253
        raise NotImplementedError()
 
254
 
295
255
 
296
256
    def help(self):
297
257
        """Return help message for this class."""
298
 
        from inspect import getdoc
299
258
        if self.__doc__ is Command.__doc__:
300
259
            return None
301
260
        return getdoc(self)
303
262
    def name(self):
304
263
        return _unsquish_command_name(self.__class__.__name__)
305
264
 
306
 
    def plugin_name(self):
307
 
        """Get the name of the plugin that provides this command.
308
 
 
309
 
        :return: The name of the plugin or None if the command is builtin.
310
 
        """
311
 
        mod_parts = self.__module__.split('.')
312
 
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
313
 
            return mod_parts[2]
314
 
        else:
315
 
            return None
316
 
 
317
265
 
318
266
def parse_spec(spec):
319
267
    """
368
316
            if argsover:
369
317
                args.append(a)
370
318
                continue
371
 
            elif a == '-':
372
 
                args.append(a)
373
 
                continue
374
319
            elif a == '--':
375
320
                # We've received a standalone -- No more flags
376
321
                argsover = True
386
331
                    else:
387
332
                        optname = a[2:]
388
333
                    if optname not in cmd_options:
389
 
                        raise BzrCommandError('unknown option "%s"' % a)
 
334
                        raise BzrOptionError('unknown long option %r for'
 
335
                                             ' command %s' % 
 
336
                                             (a, command.name()))
390
337
                else:
391
338
                    shortopt = a[1:]
392
339
                    if shortopt in Option.SHORT_OPTIONS:
401
348
                        if shortopt not in Option.SHORT_OPTIONS:
402
349
                            # We didn't find the multi-character name, and we
403
350
                            # didn't find the single char name
404
 
                            raise BzrCommandError('unknown option "%s"' % a)
 
351
                            raise BzrError('unknown short option %r' % a)
405
352
                        optname = Option.SHORT_OPTIONS[shortopt].name
406
353
 
407
354
                        if a[2:]:
418
365
                                # This option takes an argument, so pack it
419
366
                                # into the array
420
367
                                optarg = a[2:]
 
368
                
421
369
                    if optname not in cmd_options:
422
 
                        raise BzrCommandError('unknown option "%s"' % shortopt)
 
370
                        raise BzrOptionError('unknown short option %r for'
 
371
                                             ' command %s' % 
 
372
                                             (shortopt, command.name()))
423
373
                if optname in opts:
424
374
                    # XXX: Do we ever want to support this, e.g. for -r?
425
375
                    if proc_aliasarg:
426
 
                        raise BzrCommandError('repeated option %r' % a)
 
376
                        raise BzrError('repeated option %r' % a)
427
377
                    elif optname in alias_opts:
428
378
                        # Replace what's in the alias with what's in the real
429
379
                        # argument
432
382
                        proc_argv.insert(0, a)
433
383
                        continue
434
384
                    else:
435
 
                        raise BzrCommandError('repeated option %r' % a)
 
385
                        raise BzrError('repeated option %r' % a)
436
386
                    
437
387
                option_obj = cmd_options[optname]
438
388
                optargfn = option_obj.type
439
389
                if optargfn:
440
390
                    if optarg == None:
441
391
                        if not proc_argv:
442
 
                            raise BzrCommandError('option %r needs an argument' % a)
 
392
                            raise BzrError('option %r needs an argument' % a)
443
393
                        else:
444
394
                            optarg = proc_argv.pop(0)
445
395
                    opts[optname] = optargfn(optarg)
447
397
                        alias_opts[optname] = optargfn(optarg)
448
398
                else:
449
399
                    if optarg != None:
450
 
                        raise BzrCommandError('option %r takes no argument' % optname)
 
400
                        raise BzrError('option %r takes no argument' % optname)
451
401
                    opts[optname] = True
452
402
                    if proc_aliasarg:
453
403
                        alias_opts[optname] = True
484
434
                raise BzrCommandError("command %r needs one or more %s"
485
435
                        % (cmd, argname.upper()))
486
436
            argdict[argname + '_list'] = args[:-1]
487
 
            args[:-1] = []
 
437
            args[:-1] = []                
488
438
        else:
489
439
            # just a plain arg
490
440
            argname = ap
556
506
    
557
507
    argv
558
508
       The command-line arguments, without the program name from argv[0]
559
 
       These should already be decoded. All library/test code calling
560
 
       run_bzr should be passing valid strings (don't need decoding).
561
509
    
562
510
    Returns a command status or raises an exception.
563
511
 
580
528
    --lsprof
581
529
        Run under the Python lsprof profiler.
582
530
    """
583
 
    argv = list(argv)
 
531
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
584
532
 
585
533
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
586
534
                opt_no_aliases = False
599
547
        elif a == '--lsprof':
600
548
            opt_lsprof = True
601
549
        elif a == '--lsprof-file':
602
 
            opt_lsprof = True
603
550
            opt_lsprof_file = argv[i + 1]
604
551
            i += 1
605
552
        elif a == '--no-plugins':
615
562
        i += 1
616
563
 
617
564
    argv = argv_copy
618
 
    if (not argv):
619
 
        from bzrlib.builtins import cmd_help
620
 
        cmd_help().run_argv_aliases([])
 
565
    if (not argv) or (argv[0] == '--help'):
 
566
        from bzrlib.help import help
 
567
        if len(argv) > 1:
 
568
            help(argv[1])
 
569
        else:
 
570
            help()
621
571
        return 0
622
572
 
623
573
    if argv[0] == '--version':
673
623
            if not hasattr(e, 'errno'):
674
624
                raise
675
625
            if e.errno != errno.EPIPE:
676
 
                # Win32 raises IOError with errno=0 on a broken pipe
677
 
                if sys.platform != 'win32' or e.errno != 0:
678
 
                    raise
 
626
                raise
679
627
            pass
680
628
        except KeyboardInterrupt:
681
629
            pass
685
633
def main(argv):
686
634
    import bzrlib.ui
687
635
    from bzrlib.ui.text import TextUIFactory
 
636
    ## bzrlib.trace.enable_default_logging()
 
637
    bzrlib.trace.log_startup(argv)
688
638
    bzrlib.ui.ui_factory = TextUIFactory()
689
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
690
 
    ret = run_bzr_catch_errors(argv)
 
639
    ret = run_bzr_catch_errors(argv[1:])
691
640
    mutter("return code %d", ret)
692
641
    return ret
693
642
 
694
643
 
695
644
def run_bzr_catch_errors(argv):
696
645
    try:
697
 
        return run_bzr(argv)
698
 
        # do this here inside the exception wrappers to catch EPIPE
699
 
        sys.stdout.flush()
 
646
        try:
 
647
            return run_bzr(argv)
 
648
        finally:
 
649
            # do this here inside the exception wrappers to catch EPIPE
 
650
            sys.stdout.flush()
700
651
    except Exception, e:
701
652
        # used to handle AssertionError and KeyboardInterrupt
702
653
        # specially here, but hopefully they're handled ok by the logger now
703
 
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
704
 
        if os.environ.get('BZR_PDB'):
705
 
            print '**** entering debugger'
706
 
            import pdb
707
 
            pdb.post_mortem(sys.exc_traceback)
708
 
        return 3
 
654
        import errno
 
655
        if (isinstance(e, IOError) 
 
656
            and hasattr(e, 'errno')
 
657
            and e.errno == errno.EPIPE):
 
658
            bzrlib.trace.note('broken pipe')
 
659
            return 3
 
660
        else:
 
661
            bzrlib.trace.log_exception()
 
662
            if os.environ.get('BZR_PDB'):
 
663
                print '**** entering debugger'
 
664
                import pdb
 
665
                pdb.post_mortem(sys.exc_traceback)
 
666
            return 3
709
667
 
710
668
if __name__ == '__main__':
711
669
    sys.exit(main(sys.argv))