~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-10-20 04:09:18 UTC
  • Revision ID: robertc@robertcollins.net-20051020040918-4498d0a87aa37c87
update NEWS for post_commit

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
35
 
36
36
import bzrlib
37
 
from bzrlib.errors import (BzrError,
38
 
                           BzrCheckError,
39
 
                           BzrCommandError,
40
 
                           BzrOptionError,
41
 
                           NotBranchError)
 
37
import bzrlib.trace
 
38
from bzrlib.trace import mutter, note, log_error, warning
 
39
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
 
40
from bzrlib.revisionspec import RevisionSpec
 
41
from bzrlib import BZRDIR
42
42
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
47
43
 
48
44
plugin_cmds = {}
49
45
 
50
46
 
51
 
def register_command(cmd, decorate=False):
52
 
    """Utility function to help register a command
53
 
 
54
 
    :param cmd: Command subclass to register
55
 
    :param decorate: If true, allow overriding an existing command
56
 
        of the same name; the old command is returned by this function.
57
 
        Otherwise it is an error to try to override an existing command.
58
 
    """
 
47
def register_command(cmd):
 
48
    "Utility function to help register a command"
59
49
    global plugin_cmds
60
50
    k = cmd.__name__
61
51
    if k.startswith("cmd_"):
64
54
        k_unsquished = k
65
55
    if not plugin_cmds.has_key(k_unsquished):
66
56
        plugin_cmds[k_unsquished] = cmd
67
 
        mutter('registered plugin command %s', k_unsquished)
68
 
        if decorate and k_unsquished in builtin_command_names():
69
 
            return _builtin_commands()[k_unsquished]
70
 
    elif decorate:
71
 
        result = plugin_cmds[k_unsquished]
72
 
        plugin_cmds[k_unsquished] = cmd
73
 
        return result
 
57
        mutter('registered plugin command %s', k_unsquished)      
74
58
    else:
75
59
        log_error('Two plugins defined the same command: %r' % k)
76
60
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
91
75
    builtins = bzrlib.builtins.__dict__
92
76
    for name in builtins:
93
77
        if name.startswith("cmd_"):
94
 
            real_name = _unsquish_command_name(name)
 
78
            real_name = _unsquish_command_name(name)        
95
79
            r[real_name] = builtins[name]
96
80
    return r
 
81
 
97
82
            
98
83
 
99
84
def builtin_command_names():
173
158
        List of argument forms, marked with whether they are optional,
174
159
        repeated, etc.
175
160
 
176
 
                Examples:
177
 
 
178
 
                ['to_location', 'from_branch?', 'file*']
179
 
 
180
 
                'to_location' is required
181
 
                'from_branch' is optional
182
 
                'file' can be specified 0 or more times
183
 
 
184
161
    takes_options
185
162
        List of options that may be given for this command.  These can
186
163
        be either strings, referring to globally-defined options,
213
190
            r[o.name] = o
214
191
        return r
215
192
 
216
 
    @deprecated_method(zero_eight)
217
193
    def run_argv(self, argv):
218
 
        """Parse command line and run.
219
 
        
220
 
        See run_argv_aliases for the 0.8 and beyond api.
221
 
        """
222
 
        return self.run_argv_aliases(argv)
223
 
 
224
 
    def run_argv_aliases(self, argv, alias_argv=None):
225
 
        """Parse the command line and run with extra aliases in alias_argv."""
226
 
        args, opts = parse_args(self, argv, alias_argv)
 
194
        """Parse command line and run."""
 
195
        args, opts = parse_args(self, argv)
227
196
        if 'help' in opts:  # e.g. bzr add --help
228
197
            from bzrlib.help import help_on_command
229
198
            help_on_command(self.name())
232
201
        allowed_names = self.options().keys()
233
202
        for oname in opts:
234
203
            if oname not in allowed_names:
235
 
                raise BzrCommandError("option '--%s' is not allowed for"
236
 
                                      " command %r" % (oname, self.name()))
 
204
                raise BzrCommandError("option '--%s' is not allowed for command %r"
 
205
                                      % (oname, self.name()))
237
206
        # mix arguments and options into one dictionary
238
207
        cmdargs = _match_argform(self.name(), self.takes_args, args)
239
208
        cmdopts = {}
244
213
        all_cmd_args.update(cmdopts)
245
214
 
246
215
        return self.run(**all_cmd_args)
 
216
 
247
217
    
248
218
    def run(self):
249
219
        """Actually run the command.
255
225
        shell error code if not.  It's OK for this method to allow
256
226
        an exception to raise up.
257
227
        """
258
 
        raise NotImplementedError('no implementation of command %r' 
259
 
                                  % self.name())
 
228
        raise NotImplementedError()
 
229
 
260
230
 
261
231
    def help(self):
262
232
        """Return help message for this class."""
263
 
        from inspect import getdoc
264
233
        if self.__doc__ is Command.__doc__:
265
234
            return None
266
235
        return getdoc(self)
300
269
        parsed = [spec, None]
301
270
    return parsed
302
271
 
303
 
def parse_args(command, argv, alias_argv=None):
 
272
def parse_args(command, argv):
304
273
    """Parse command line.
305
274
    
306
275
    Arguments and options are parsed at this level before being passed
311
280
    # TODO: chop up this beast; make it a method of the Command
312
281
    args = []
313
282
    opts = {}
314
 
    alias_opts = {}
315
283
 
316
284
    cmd_options = command.options()
317
285
    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
 
286
    while argv:
 
287
        a = argv.pop(0)
 
288
        if argsover:
 
289
            args.append(a)
 
290
            continue
 
291
        elif a == '--':
 
292
            # We've received a standalone -- No more flags
 
293
            argsover = True
 
294
            continue
 
295
        if a[0] == '-':
 
296
            # option names must not be unicode
 
297
            a = str(a)
 
298
            optarg = None
 
299
            if a[1] == '-':
 
300
                mutter("  got option %r" % a)
 
301
                if '=' in a:
 
302
                    optname, optarg = a[2:].split('=', 1)
 
303
                else:
 
304
                    optname = a[2:]
 
305
                if optname not in cmd_options:
 
306
                    raise BzrCommandError('unknown long option %r for command %s' 
 
307
                            % (a, command.name()))
 
308
            else:
 
309
                shortopt = a[1:]
 
310
                if shortopt in Option.SHORT_OPTIONS:
 
311
                    # Multi-character options must have a space to delimit
 
312
                    # their value
 
313
                    # ^^^ what does this mean? mbp 20051014
 
314
                    optname = Option.SHORT_OPTIONS[shortopt].name
 
315
                else:
 
316
                    # Single character short options, can be chained,
 
317
                    # and have their value appended to their name
 
318
                    shortopt = a[1:2]
 
319
                    if shortopt not in Option.SHORT_OPTIONS:
 
320
                        # We didn't find the multi-character name, and we
 
321
                        # didn't find the single char name
 
322
                        raise BzrError('unknown short option %r' % a)
 
323
                    optname = Option.SHORT_OPTIONS[shortopt].name
359
324
 
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:]
 
325
                    if a[2:]:
 
326
                        # There are extra things on this option
 
327
                        # see if it is the value, or if it is another
 
328
                        # short option
 
329
                        optargfn = Option.OPTIONS[optname].type
 
330
                        if optargfn is None:
 
331
                            # This option does not take an argument, so the
 
332
                            # next entry is another short option, pack it back
 
333
                            # into the list
 
334
                            argv.insert(0, '-' + a[2:])
 
335
                        else:
 
336
                            # This option takes an argument, so pack it
 
337
                            # into the array
 
338
                            optarg = a[2:]
 
339
            
 
340
            if optname in opts:
 
341
                # XXX: Do we ever want to support this, e.g. for -r?
 
342
                raise BzrError('repeated option %r' % a)
374
343
                
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
 
344
            option_obj = cmd_options[optname]
 
345
            optargfn = option_obj.type
 
346
            if optargfn:
 
347
                if optarg == None:
 
348
                    if not argv:
 
349
                        raise BzrError('option %r needs an argument' % a)
390
350
                    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
 
351
                        optarg = argv.pop(0)
 
352
                opts[optname] = optargfn(optarg)
410
353
            else:
411
 
                args.append(a)
412
 
        proc_aliasarg = False # Done with alias argv
 
354
                if optarg != None:
 
355
                    raise BzrError('option %r takes no argument' % optname)
 
356
                opts[optname] = True
 
357
        else:
 
358
            args.append(a)
413
359
    return args, opts
414
360
 
415
361
 
481
427
        os.remove(pfname)
482
428
 
483
429
 
484
 
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
485
 
    from bzrlib.lsprof import profile
486
 
    import cPickle
487
 
    ret, stats = profile(the_callable, *args, **kwargs)
488
 
    stats.sort()
489
 
    if filename is None:
490
 
        stats.pprint()
491
 
    else:
492
 
        stats.freeze()
493
 
        cPickle.dump(stats, open(filename, 'w'), 2)
494
 
        print 'Profile data written to %r.' % filename
495
 
    return ret
496
 
 
497
 
 
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)
502
 
    if (alias):
503
 
        return alias.split(' ')
504
 
    return None
505
 
 
506
 
 
507
430
def run_bzr(argv):
508
431
    """Execute a command.
509
432
 
521
444
    --no-plugins
522
445
        Do not load plugin modules at all
523
446
 
524
 
    --no-aliases
525
 
        Do not allow aliases
526
 
 
527
447
    --builtin
528
448
        Only use builtin commands.  (Plugins are still allowed to change
529
449
        other behaviour.)
530
450
 
531
451
    --profile
532
 
        Run under the Python hotshot profiler.
533
 
 
534
 
    --lsprof
535
 
        Run under the Python lsprof profiler.
 
452
        Run under the Python profiler.
536
453
    """
537
454
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
538
455
 
539
 
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
540
 
                opt_no_aliases = False
541
 
    opt_lsprof_file = None
 
456
    opt_profile = opt_no_plugins = opt_builtin = False
542
457
 
543
458
    # --no-plugins is handled specially at a very early stage. We need
544
459
    # to load plugins before doing other command parsing so that they
545
460
    # can override commands, but this needs to happen first.
546
461
 
547
 
    argv_copy = []
548
 
    i = 0
549
 
    while i < len(argv):
550
 
        a = argv[i]
 
462
    for a in argv:
551
463
        if a == '--profile':
552
464
            opt_profile = True
553
 
        elif a == '--lsprof':
554
 
            opt_lsprof = True
555
 
        elif a == '--lsprof-file':
556
 
            opt_lsprof_file = argv[i + 1]
557
 
            i += 1
558
465
        elif a == '--no-plugins':
559
466
            opt_no_plugins = True
560
 
        elif a == '--no-aliases':
561
 
            opt_no_aliases = True
562
467
        elif a == '--builtin':
563
468
            opt_builtin = True
564
 
        elif a in ('--quiet', '-q'):
565
 
            be_quiet()
566
469
        else:
567
 
            argv_copy.append(a)
568
 
        i += 1
 
470
            break
 
471
        argv.remove(a)
569
472
 
570
 
    argv = argv_copy
571
 
    if (not argv):
572
 
        from bzrlib.builtins import cmd_help
573
 
        cmd_help().run_argv_aliases([])
 
473
    if (not argv) or (argv[0] == '--help'):
 
474
        from bzrlib.help import help
 
475
        if len(argv) > 1:
 
476
            help(argv[1])
 
477
        else:
 
478
            help()
574
479
        return 0
575
480
 
576
481
    if argv[0] == '--version':
581
486
    if not opt_no_plugins:
582
487
        from bzrlib.plugin import load_plugins
583
488
        load_plugins()
584
 
    else:
585
 
        from bzrlib.plugin import disable_plugins
586
 
        disable_plugins()
587
 
 
588
 
    alias_argv = None
589
 
 
590
 
    if not opt_no_aliases:
591
 
        alias_argv = get_alias(argv[0])
592
 
        if alias_argv:
593
 
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
594
 
            argv[0] = alias_argv.pop(0)
595
489
 
596
490
    cmd = str(argv.pop(0))
597
491
 
598
492
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
599
 
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
600
 
        run = cmd_obj.run_argv
601
 
        run_argv = [argv]
 
493
 
 
494
    if opt_profile:
 
495
        ret = apply_profiled(cmd_obj.run_argv, argv)
602
496
    else:
603
 
        run = cmd_obj.run_argv_aliases
604
 
        run_argv = [argv, alias_argv]
605
 
 
606
 
    try:
607
 
        if opt_lsprof:
608
 
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
609
 
        elif opt_profile:
610
 
            ret = apply_profiled(run, *run_argv)
611
 
        else:
612
 
            ret = run(*run_argv)
613
 
        return ret or 0
614
 
    finally:
615
 
        # reset, in case we may do other commands later within the same process
616
 
        be_quiet(False)
 
497
        ret = cmd_obj.run_argv(argv)
 
498
    return ret or 0
617
499
 
618
500
def display_command(func):
619
 
    """Decorator that suppresses pipe/interrupt errors."""
620
501
    def ignore_pipe(*args, **kwargs):
621
502
        try:
622
 
            result = func(*args, **kwargs)
623
 
            sys.stdout.flush()
624
 
            return result
 
503
            func(*args, **kwargs)
625
504
        except IOError, e:
626
 
            if not hasattr(e, 'errno'):
627
 
                raise
628
505
            if e.errno != errno.EPIPE:
629
506
                raise
630
 
            pass
631
 
        except KeyboardInterrupt:
632
 
            pass
633
507
    return ignore_pipe
634
508
 
635
 
 
636
509
def main(argv):
637
510
    import bzrlib.ui
638
 
    from bzrlib.ui.text import TextUIFactory
639
 
    ## bzrlib.trace.enable_default_logging()
640
511
    bzrlib.trace.log_startup(argv)
641
 
    bzrlib.ui.ui_factory = TextUIFactory()
642
 
    ret = run_bzr_catch_errors(argv[1:])
643
 
    mutter("return code %d", ret)
644
 
    return ret
 
512
    bzrlib.ui.ui_factory = bzrlib.ui.TextUIFactory()
 
513
 
 
514
    return run_bzr_catch_errors(argv[1:])
645
515
 
646
516
 
647
517
def run_bzr_catch_errors(argv):
651
521
        finally:
652
522
            # do this here inside the exception wrappers to catch EPIPE
653
523
            sys.stdout.flush()
 
524
    except BzrCommandError, e:
 
525
        # command line syntax error, etc
 
526
        log_error(str(e))
 
527
        return 1
 
528
    except BzrError, e:
 
529
        bzrlib.trace.log_exception()
 
530
        return 1
 
531
    except AssertionError, e:
 
532
        bzrlib.trace.log_exception('assertion failed: ' + str(e))
 
533
        return 3
 
534
    except KeyboardInterrupt, e:
 
535
        bzrlib.trace.log_exception('interrupted')
 
536
        return 2
654
537
    except Exception, e:
655
 
        # used to handle AssertionError and KeyboardInterrupt
656
 
        # specially here, but hopefully they're handled ok by the logger now
657
538
        import errno
658
539
        if (isinstance(e, IOError) 
659
540
            and hasattr(e, 'errno')
660
541
            and e.errno == errno.EPIPE):
661
542
            bzrlib.trace.note('broken pipe')
662
 
            return 3
 
543
            return 2
663
544
        else:
 
545
            ## import pdb
 
546
            ## pdb.pm()
664
547
            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
 
548
            return 2
670
549
 
671
550
if __name__ == '__main__':
672
551
    sys.exit(main(sys.argv))