~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Robert Collins
  • Date: 2005-10-18 05:26:22 UTC
  • mto: This revision was merged to the branch mainline in revision 1463.
  • Revision ID: robertc@robertcollins.net-20051018052622-653d638c9e26fde4
fix broken tests

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